一、Json指定轉化成對象返回
上篇文章主要講基礎的框架搭建起來了,這次需要做一些些的擴展,這裏Json轉化用到了google的Gson。
上篇文章,我們直接返回了String的字符串,那麼如果是請求返回回來的是Json格式的,我們能否在數據返回的時候將數據轉化成需要的對象呢。答案當然是可以的。
我們可以在UI線程中創建Callback的時候將預處理的對象放入進去,還是直接代碼描述比較清楚:
1. 首先我們需要傳遞 實體類 的class 進去,該方法我們可以在抽象類 AbstractCallback 中定義:
- public AbstractCallback<T> setReturnClass(Class<T> clz) {
- this.mReturnClass = clz;
- return this;
- }
- public AbstractCallback<T> setReturnType(Type type) {
- this.mReturnType = type;
- return this;
- }
- private void requestJson() {
- Request request = new Request(UrlHelper.test_json_url, RequestMethod.GET);//UrlHelper.test_json_url是一個json地址
- request.setCallback(new JsonCallback<Entity>() { // Entity 爲 json 要轉化的實體類 ,可以是 ArrayList<Entity>的形式等
- @Override
- public void onFilure(Exception result) {
- }
- @Override
- public void onSuccess(Entity result) {
- mTestResultLabel.setText(result.weatherinfo + "----");
- }
- }.setReturnType(new TypeToken<Entity>(){}.getType()));//.setReturnClass(Entity.class));
- request.execute();
- }
- public abstract class JsonCallback<T> extends AbstractCallback<T> {
- public static Gson gson = new Gson();
- @Override
- protected T bindData(String content) {
- Log.i("bindData", content);
- if (TextUtil.isValidate(path)) {
- content = IOUtilities.readFromFile(path);
- }
- if (mReturnClass != null) {
- return gson.fromJson(content, mReturnClass);
- } else if (mReturnType != null) {
- return gson.fromJson(content, mReturnType);
- }
- return null;
- }
- }
二、下載進度更新
處理思路:
在 AsyncTask 中doInBackground 裏有一個方法publishProgress ,通過 AbstractCallback 的裏的寫入文件的進度,將進度實時更新到 onProgressUpdate 從而將更新進度更新到 主線程的功能,然後對進度進行相應的處理。如更新進度條等。
1. 首先添加一個監聽接口:
- public interface IProgressListener {
- void onProgressUpdate(int curPos,int contentLength);
- }
- while ((read = in.read(b)) != -1) {
- // TODO update progress
- fos.write(b, 0, read);
- }
a. 修改 ICallback 接口:
- Object handle(HttpResponse response, IProgressListener mProgressListener);
- @Override
- public Object handle(HttpResponse response, IProgressListener mProgressListener){.........}
- byte[] b = new byte[IO_BUFFER_SIZE];
- int read;
- long curPos = 0;
- long length = entity.getContentLength();
- while ((read = in.read(b)) != -1) {
- checkIfCanceled();
- if (mProgressListener != null) {
- curPos += read;
- //將當前進度和總進度返回到具體的實現層,
- //我們在 RequestTask 的 doInBackground 中去實現 IProgressLinstener 接口中的的該方法,將值傳到onProgressUpdate中
- mProgressListener.onProgressUpdate((int) (curPos / 1024), (int) (length / 1024));
- }
- fos.write(b, 0, read);
- }
3. 在RequestTask.java 中的doInBackground 中我們來實現上述內容:
- @Override
- protected Object doInBackground(Object... params) {
- try {
- HttpResponse response = HttpClientUtil.excute(request);
- //response 解析代碼放到對應的類中,對應handle中的bindData方法
- Log.i("doInBackground", response.toString());
- if (request.mProgressListener != null) {
- return request.callback.handle(response, new IProgressListener() {
- @Override
- public void onProgressUpdate(int curPos, int contentLength) {
- //這裏的參數類型是 AsyncTask<Object, Integer, Object>中的Integer決定的,在onProgressUpdate中可以得到這個值去更新UI主線程
- publishProgress(curPos,contentLength);
- }
- });
- }else {
- return request.callback.handle(response, null);
- }
- } catch (Exception e) {
- return e;
- }
- }
- public IProgressListener mProgressListener;
- public void setProgressListener(IProgressListener iProgressListener) {
- this.mProgressListener = iProgressListener;
- }
- @Override
- protected void onProgressUpdate(Integer... values) {
- super.onProgressUpdate(values);
- if (request.mProgressListener != null) {
- request.mProgressListener.onProgressUpdate(values[0], values[1]);
- }
- }
- private void requestString() {
- //設置保存路徑
- String path = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "mrfu_http.txt";
- Request request = new Request(UrlHelper.test_string_url, RequestMethod.GET);
- request.setCallback(new StringCallback() {
- @Override
- public void onSuccess(String result) {
- mTestResultLabel.setText((String)result);
- }
- @Override
- public void onFilure(Exception result) {
- result.printStackTrace();
- }
- }.setPath(path));
- request.setProgressListener(new IProgressListener() {
- //在這裏實現 IProgressListener 的onProgressUpdate 將子線程中得到的進度值獲取到。
- //這裏,我們只是顯示到LogCat中,實際我們可以根據需要實現進度條更新等操作
- @Override
- public void onProgressUpdate(int curPos, int contentLength) {
- System.err.println("curPost:"+curPos +",contentLength:" + contentLength);
- }
- });
- request.execute();
- }
三、 如何隨時取消 Request 請求
1. 我們需要取消 Request 請求,那麼,在代碼中,我們在哪些地方可以取消請求呢?我們先來分析框架的基本內容:
a. 在主線程我們執行 request.execute(); 在 Request.java 中開啓了一個 RequestTask,它繼承自 AsyncTask
b. doInBackground 是異步執行的,在這個子線程中 我們執行 HttpResponse response = HttpClientUtil.excute(request); 代碼段 正式調用HTTP的get或者set方法,得到類型爲 HttpResponse 的返回值 ,然後執行 AbstractCallback 的 handle 方法
c. 在AbstractCallback 的 public T handle(HttpResponse response, IProgressListener mProgressListener) 方法中我們處理返回回來的 HttpResponse 的內容,如果返回的code是200,則成功,那麼我們根據是否設置了下載路徑選擇是否下載,或者是直接返回數值。
d. 根據主線程設置的 StringCallback 或者JsonCallback 或者其他解析類型,通過調用 bindData(....); 去具體的解析內容,並返回到 UI 線程。
2. 設計思路:
在主線程,我們接到了取消請求的需求,通過 調用 Requset 的 cancel() 方法,去調用 callback 中的 cancel(); 方法,將其AbstractCallback 中的取消標誌設置爲true,如果爲true 我們就在checkIfCanceled()方法中拋出異常,結束該次請求。我們可以將 checkIfCanceled() 方法放在handle(..., ...)剛開始的時候,放在while ((read = in.read(b)) != -1){ fos.write(b, 0, read); } 寫入文件的時候 以及返回數據放入不同callback中進行處理的時候。下面我們會給出具體的實現方法,還有http請求的 get 和 post 的時候。
3. 實現代碼:
a. ICallback 接口中定義如下方法:
- void checkIfCanceled() throws AppException;
- void cancel();
b. 主線程調用cancel請求:
- public void testCancel(){
- String path = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "stay_training_http.txt";
- final Request request = new Request("http://h.hiphotos.baidu.com/image/w%3D2048/sign=432fca00369b033b2c88fbda21f636d3/a2cc7cd98d1001e9ae04c30bba0e7bec54e797fe.jpg",RequestMethod.GET);
- request.setCallback(new PathCallback() {
- @Override
- public void onSuccess(String result) {
- }
- @Override
- public void onFilure(Exception result) {
- result.printStackTrace();
- }
- }.setPath(path));
- request.setProgressListener(new IProgressListener() {
- @Override
- public void onProgressUpdate(int curPos, int contentLength) {
- System.err.println("curPost:"+curPos +",contentLength:" + contentLength);
- if (curPos > 8) {
- request.cancel();//當下載進度爲8的時候我們取消了該請求
- }
- }
- });
- request.execute();
- }
- }
c. 在 Request 中實現 cancel() 方法,方法內調用 callback 的 cancel() 方法:
- public ICallback callback;
- public void cancel(){
- if (callback != null) {
- this.callback.cancel();
- }
- }
d. 在 AbstractCallback 中實現ICallback 裏定義的 cancel() 的方法,如果主線程調用了cancel方法,我們就將標誌設置爲 true
- protected boolean isCancelled;
- @Override
- public void cancel() {
- isCancelled = true;
- }
e. 實現 ICallback 中的 checkIfCanceled() 方法,如果 isCancelled 爲 true 則拋出異常,即可中斷請求操作,代碼中出現了 AppException 自定義異常類 這個我們後面再講
- @Override
- public void checkIfCanceled() throws AppException {
- if (isCancelled) {
- throw new AppException(EnumException.CancelException, "request has been cancelled");
- }
- }
- @Override
- public T handle(HttpResponse response, IProgressListener mProgressListener) throws AppException{
- // file, json, xml, image, string
- checkIfCanceled();//在這裏我們調用檢查是否取消請求的方法
- int statusCode = -1;
- InputStream in = null;
- try {
- HttpEntity entity = response.getEntity();
- statusCode = response.getStatusLine().getStatusCode();
- switch (statusCode) {
- case HttpStatus.SC_OK:
- if (TextUtil.isValidate(path)) {
- //將服務器返回的數據寫入到文件當中
- FileOutputStream fos = new FileOutputStream(path);
- if (entity.getContentEncoding() != null) {
- String encoding = entity.getContentEncoding().getValue();
- if (encoding != null && "gzip".equalsIgnoreCase(encoding)) {
- in = new GZIPInputStream(entity.getContent());
- } if (encoding != null && "deflate".equalsIgnoreCase(encoding)) {
- in = new InflaterInputStream(entity.getContent());
- }
- } else {
- in = entity.getContent();
- }
- byte[] b = new byte[IO_BUFFER_SIZE];
- int read;
- long curPos = 0;
- long length = entity.getContentLength();
- while ((read = in.read(b)) != -1) {
- checkIfCanceled(); //<span style="font-family: Arial, Helvetica, sans-serif;">在這裏我們調用檢查是否取消請求的方法</span>
- if (mProgressListener != null) {
- curPos += read;
- //將當前進度和總進度返回到具體的實現層,
- //我們在 RequestTask 的 doInBackground 中去實現 IProgressLinstener 接口中的的該方法,將值傳到onProgressUpdate中
- mProgressListener.onProgressUpdate((int) (curPos / 1024), (int) (length / 1024));
- }
- fos.write(b, 0, read);
- }
- fos.flush();
- fos.close();
- in.close();
- //寫入文件之後,再從文件當中將數據讀取出來,直接返回對象
- return bindData(path);
- } else {
- // 需要返回的是對象,而不是數據流,所以需要去解析服務器返回的數據
- // 對應StringCallback 中的return content;
- //2. 調用binData
- return bindData(EntityUtils.toString(entity));
- }
- default:
- break;
- }
- return null;
- } catch (ParseException e) {
- throw new AppException(EnumException.ParseException, e.getMessage());
- } catch (IOException e) {
- throw new AppException(EnumException.IOException, e.getMessage());
- }
- }
- /**
- * 數據放入到不同的Callback中處理,StringCallback 等方法中實現了該方法
- * @throws AppException
- */
- protected T bindData(String content) throws AppException{
- checkIfCanceled();//在這裏我們檢查是否取消請求的方法
- return null;
- }
g. 我們在 RequestTask 中重寫 onCancelled 判斷 是否有做了 task.cancel(true); 的操作,當然,我們並沒有實現該操作,那是因爲,我們需要不管Request 的請求結果如果,我都需要返回到主線程,如果我這個時候取消掉了 AsyncTask ,那麼AsyncTask 就永遠不繼續執行了,也就無法回調回來了。
- @Override
- protected void onCancelled() {
- super.onCancelled();
- if (request.callback != null) {
- request.callback.cancel();
- }
- }
- private static HttpResponse get(Request request) throws AppException {
- try {
- //如果在代碼已經執行到這裏的時候,AbstractCallback中的isCancelled被置爲了 true
- //這時我們就要再一次進行檢查是否取消。 post方法同理,不再贅述
- if (request.callback != null) {
- request.callback.checkIfCanceled();
- }
- HttpClient client = new DefaultHttpClient();
- HttpGet get = new HttpGet(request.url);
- addHeader(get, request.headers);
- //返回的結果放到上一層進行處理
- HttpResponse response = client.execute(get);
- return response;
- } catch (ClientProtocolException e) {
- throw new AppException(EnumException.ClientProtocolException, e.getMessage());
- } catch (IOException e) {
- throw new AppException(EnumException.IOException, e.getMessage());
- }
- }