1. 程式人生 > >android-async-http使用

android-async-http使用

android-async-http是基於Apache HttpClient專門用於android的非同步http請求,所有的請求都在非UI執行緒執行。API比較全面,使用比較簡單,而且在CallBack中使用了Handler訊息機制,我們可以在回撥方法onSuccess,onFailure等中直接對UI進行操作。

我們通過官方教程首先來了解下其相關特性:

1,用HttpClient代替Android提供的DefaultHttpClient;

2,相容Android API 23以及更高;

3,傳送非同步http請求,在匿名的callback物件中處理response資訊;

4,在非UI執行緒執行http請求;

5,使用執行緒池處理併發請求;

6,RequestParams作為GET/POST引數構造器;

7,多部件檔案上傳,不需要引入第三方庫;

8,JSON資料流上傳,不需要引入庫;

9,能處理迴圈行和相對重定向;

10,對應用來說庫很小,總共只有90KB;

11,使多種多樣的移動連線具備良好自動智慧請求重試機制;

12,支援超快速請求的自動gzip響應解碼;

13,BinaryHttpResponseHandler支援二進位制通訊協議;

14,通過JsonHttpResponseHandler實現內建解析response成JSON格式;

15,通過FileAsyncHttpResponseHandler實現直接將response寫入儲存到檔案中;

16,持久化的cookie儲存,將cookie儲存到應用程式的SharePreferences中;

17,通過BaseJsonHttpResponseHandler整合Jackson Json,Gson和其他的JSON序列化庫;

18,通過SaxAsyncHttpResponseHandler支援SAX解析;

19,支援各種語言和內容編碼,不是隻有UTF-8;

整合方法:

在GIT原始碼中有介紹,這裡不詳細說,我們這裡用的Android studio,只要在app/build.gradle檔案加上:

dependencies {
    ......
    compile 'com.loopj.android:android-async-http:1.4.9'
}
使用步驟:

1,建立AsyncHttpClient物件;

2,如果需要引數,可以建立RequestParams物件新增引數,如果不需要引數,這一步可免;

3,按需要呼叫AsyncHttpClient的某個GET/POST的方法,傳遞需要的callback介面實現。

基礎體驗:

首先給出一個官方教程的建議,封裝靜態的HttpClient:

public class TwitterRestClient {
  private static final String BASE_URL = "https://......";

  private static AsyncHttpClient client = new AsyncHttpClient();

  public static void get(String url, RequestParams params, AsyncHttpResponseHandler responseHandler) {
      client.get(getAbsoluteUrl(url), params, responseHandler);
  }

  public static void post(String url, RequestParams params, AsyncHttpResponseHandler responseHandler) {
      client.post(getAbsoluteUrl(url), params, responseHandler);
  }

  private static String getAbsoluteUrl(String relativeUrl) {
      return BASE_URL + relativeUrl;
  }
  ......
}
這樣封裝好了使用起來特別方便:
class TwitterRestClientUsage {
    public void getPublicTimeline() throws JSONException {
        TwitterRestClient.get("statuses/public_timeline.json", null, new JsonHttpResponseHandler() {
            @Override
            public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
                // If the response is JSONObject instead of expected JSONArray
            }
            
            @Override
            public void onSuccess(int statusCode, Header[] headers, JSONArray timeline) {
                // Pull out the first event on the public timeline
                JSONObject firstEvent = timeline.get(0);
                String tweetText = firstEvent.getString("text");

                // Do something with the response
                System.out.println(tweetText);
            }
        });
    }
}
以上內容來自官方教程,詳細請看官方文件

每次請求都需要傳遞一個實現ResponseHandlerInterface介面的例項,用來接收處理回撥的事件,一般都繼承AsyncHttpResponseHandler。

1,首先看下AsyncHttpResponseHandler:

                RequestParams params = new RequestParams();
                TestHttpClient.get(AsyncHttpActivity.this, url, params, new AsyncHttpResponseHandler() {
                    @Override
                    public void onSuccess(int statusCode, Header[] headers, byte[] responseBody) {

                    }

                    @Override
                    public void onFailure(int statusCode, Header[] headers, byte[] responseBody, Throwable error) {

                    }
                });
可以看出,返回的結果是byte型別的,可以轉換成我們需要的格式。預設重寫了onSuccess和onFailure兩個方法,還可以重寫onStart、onFinish、onCancle、onRetry等方法來實現資料的獲取以及與介面的互動效果。

,2,BinaryHttpResponseHandler繼承自AsyncHttpResponseHandler,可以傳送二進位制請求,如請求圖片等:

                RequestParams params = new RequestParams();
                TestHttpClient.get(AsyncHttpActivity.this, url, params, new BinaryHttpResponseHandler() {
                    @Override
                    public void onSuccess(int statusCode, Header[] headers, byte[] binaryData) {

                    }

                    @Override
                    public void onFailure(int statusCode, Header[] headers, byte[] binaryData, Throwable error) {
                        
                    }
                });
這裡返回給我們的也是byte型別,我們可以轉換成Bitmap等。

3,TextHttpResponseHandler,繼承自AsyncHttpResponseHandler,只重寫了onSuccess和onFailure兩個方法。

                RequestParams params = new RequestParams();
                TestHttpClient.get(AsyncHttpActivity.this, url, params, new TextHttpResponseHandler() {
                    @Override
                    public void onFailure(int statusCode, Header[] headers, String responseString, Throwable throwable) {

                    }

                    @Override
                    public void onSuccess(int statusCode, Header[] headers, String responseString) {
                        
                    }
                });
返回的結果由原來的byte型別轉換成了String型別。

4,JsonHttpResponseHandler,繼承自TextHttpResponseHandler,同樣只重寫了onSuccess和onFailure兩個方法。

               RequestParams params = new RequestParams();
                TestHttpClient.get(AsyncHttpActivity.this, url, params, new JsonHttpResponseHandler(){
                    @Override
                    public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
                        super.onSuccess(statusCode, headers, response);
                    }
                    @Override
                    public void onSuccess(int statusCode, Header[] headers, JSONArray response) {
                        super.onSuccess(statusCode, headers, response);
                    }
                    @Override
                    public void onSuccess(int statusCode, Header[] headers, String responseString) {
                        super.onSuccess(statusCode, headers, responseString);
                    }

                    @Override
                    public void onFailure(int statusCode, Header[] headers, String responseString, Throwable throwable) {
                        super.onFailure(statusCode, headers, responseString, throwable);
                    }
                    @Override
                    public void onFailure(int statusCode, Header[] headers, Throwable throwable, JSONObject errorResponse) {
                        super.onFailure(statusCode, headers, throwable, errorResponse);
                    }
                    @Override
                    public void onFailure(int statusCode, Header[] headers, Throwable throwable, JSONArray errorResponse) {
                        super.onFailure(statusCode, headers, throwable, errorResponse);
                    }
                });
這裡有三個onSuccess和onFailure方法供重寫,返回的結果分別轉換成JSONObject、JSONArray和String型別。按照不同的返回值型別呼叫不同的方法,這個比較煩。

5,BaseJsonHttpResponseHandler,繼承自TextHttpResponseHandler,同樣只重寫了onSuccess、onFailure兩個方法,另外添加了一個方法parseResponse。

                RequestParams params = new RequestParams();
                TestHttpClient.get(AsyncHttpActivity.this, url, params, new BaseJsonHttpResponseHandler<Object>() {
                    @Override
                    public void onSuccess(int statusCode, Header[] headers, String rawJsonResponse, Object response) {

                    }
                    @Override
                    public void onFailure(int statusCode, Header[] headers, Throwable throwable, String rawJsonData, Object errorResponse) {

                    }
                    @Override
                    protected Object parseResponse(String rawJsonData, boolean isFailure) throws Throwable {
                        return null;
                    }
                });

首先要說明,在AsyncHttpResponseHandler中onSuccess和onFailure通過handler訊息機制的處理,可以直接在方法體中操作UI更新,但是在parseResponse方法中是不可以直接操作UI更新的。

這裡我們藉助原始碼學習下引數和方法的呼叫:

    /**
     * Should return deserialized instance of generic type, may return object for more vague
     * handling
     *
     * @param rawJsonData response string, may be null
     * @param isFailure   indicating if this method is called from onFailure or not
     * @return object of generic type or possibly null if you choose so
     * @throws Throwable allows you to throw anything from within deserializing JSON response
     */
    protected abstract JSON_TYPE parseResponse(String rawJsonData, boolean isFailure) throws Throwable;
能返回泛型的反序列化例項,也可以返回更多模糊處理的物件。第一個引數rawJsonData是response字串,第二個引數isFailure判斷是否被onFailure方法呼叫。
    @Override
    public final void onSuccess(final int statusCode, final Header[] headers, final String responseString) {
        if (statusCode != HttpStatus.SC_NO_CONTENT) {
            Runnable parser = new Runnable() {
                @Override
                public void run() {
                    try {
                        final JSON_TYPE jsonResponse = parseResponse(responseString, false);
                        postRunnable(new Runnable() {
                            @Override
                            public void run() {
                                onSuccess(statusCode, headers, responseString, jsonResponse);
                            }
                        });
                    } catch (final Throwable t) {
                        AsyncHttpClient.log.d(LOG_TAG, "parseResponse thrown an problem", t);
                        postRunnable(new Runnable() {
                            @Override
                            public void run() {
                                onFailure(statusCode, headers, t, responseString, null);
                            }
                        });
                    }
                }
            };
            if (!getUseSynchronousMode() && !getUsePoolThread()) {
                new Thread(parser).start();
            } else {
                // In synchronous mode everything should be run on one thread
                parser.run();
            }
        } else {
            onSuccess(statusCode, headers, null, null);
        }
    }
可以看出onSuccess的第三個引數responseString就是好返回的response字串,和parseResponse方法的第一個引數值一樣,onSuccess的第四個引數是在parseResponse方法處理後的返回值。
    @Override
    public final void onFailure(final int statusCode, final Header[] headers, final String responseString, final Throwable throwable) {
        if (responseString != null) {
            Runnable parser = new Runnable() {
                @Override
                public void run() {
                    try {
                        final JSON_TYPE jsonResponse = parseResponse(responseString, true);
                        postRunnable(new Runnable() {
                            @Override
                            public void run() {
                                onFailure(statusCode, headers, throwable, responseString, jsonResponse);
                            }
                        });
                    } catch (Throwable t) {
                        AsyncHttpClient.log.d(LOG_TAG, "parseResponse thrown an problem", t);
                        postRunnable(new Runnable() {
                            @Override
                            public void run() {
                                onFailure(statusCode, headers, throwable, responseString, null);
                            }
                        });
                    }
                }
            };
            if (!getUseSynchronousMode() && !getUsePoolThread()) {
                new Thread(parser).start();
            } else {
                // In synchronous mode everything should be run on one thread
                parser.run();
            }
        } else {
            onFailure(statusCode, headers, throwable, null, null);
        }
    }
onFailure的第四個引數是response字串,第五個引數是parseResponse方法處理後的返回值,第三個引數是丟擲的異常。

BaseJsonHttpResponseHandler需要自己對返回的值進行處理,內容解析。

6,FileAsyncHttpResponseHandler,可以實現將response寫入儲存到檔案中:

                RequestParams params = new RequestParams();
                TestHttpClient.get(AsyncHttpActivity.this, url, params, new FileAsyncHttpResponseHandler(AsyncHttpActivity.this) {
                    @Override
                    public void onFailure(int statusCode, Header[] headers, Throwable throwable, File file) {

                    }
                    @Override
                    public void onSuccess(int statusCode, Header[] headers, File file) {

                    }
                });
從引數可以看出,返回給我們的直接是File,內容已經寫入到File中了。建立FileAsyncHttpResponseHandler例項的時候,我們這裡傳了this引數,參考原始碼:
     /**
     * Obtains new FileAsyncHttpResponseHandler against context with target being temporary file
     *
     * @param context Context, must not be null
     */
    public FileAsyncHttpResponseHandler(Context context) {
        super();
        this.file = getTemporaryFile(context);
        this.append = false;
        this.renameIfExists = false;
    }
這裡會掉getTemporaryFile方法獲得File。
     /**
     * Used when there is no file to be used when calling constructor
     *
     * @param context Context, must not be null
     * @return temporary file or null if creating file failed
     */
    protected File getTemporaryFile(Context context) {
        Utils.asserts(context != null, "Tried creating temporary file without having Context");
        try {
            return File.createTempFile("temp_", "_handled", context.getCacheDir());
        } catch (IOException e) {
            AsyncHttpClient.log.e(LOG_TAG, "Cannot create temporary file", e);
        }
        return null;
    }
該方法中,會建立一個預設的File。

當然我們也可以自己建立指定的File,例項化的時候傳過去:

     /**
     * Obtains new FileAsyncHttpResponseHandler and stores response in passed file
     *
     * @param file File to store response within, must not be null
     */
    public FileAsyncHttpResponseHandler(File file) {
        this(file, false);
    }
該構造方法直接傳了File。
     /**
     * Obtains new FileAsyncHttpResponseHandler and stores response in passed file
     *
     * @param file   File to store response within, must not be null
     * @param append whether data should be appended to existing file
     */
    public FileAsyncHttpResponseHandler(File file, boolean append) {
        this(file, append, false);
    }
該構造犯法多傳一個引數boolean append,用於判斷是否將資料追加到已經存在的File中,如果是true不會覆蓋原來內容,加在後面,是false就覆蓋。
     /**
     * Obtains new FileAsyncHttpResponseHandler and stores response in passed file
     *
     * @param file                     File to store response within, must not be null
     * @param append                   whether data should be appended to existing file
     * @param renameTargetFileIfExists whether target file should be renamed if it already exists
     */
    public FileAsyncHttpResponseHandler(File file, boolean append, boolean renameTargetFileIfExists) {
        this(file,append,renameTargetFileIfExists,false);
    }
多一個引數boolean renameTargetFileIfExists,用於判斷當檔案已經存在的時候是否重新命名檔案。
     /**
     * Obtains new FileAsyncHttpResponseHandler and stores response in passed file
     *
     * @param file                     File to store response within, must not be null
     * @param append                   whether data should be appended to existing file
     * @param renameTargetFileIfExists whether target file should be renamed if it already exists
     * @param usePoolThread Whether to use the pool's thread to fire callbacks
     */
    public FileAsyncHttpResponseHandler(File file, boolean append, boolean renameTargetFileIfExists,boolean usePoolThread) {
        super(usePoolThread);
        Utils.asserts(file != null, "File passed into FileAsyncHttpResponseHandler constructor must not be null");
        if (!file.isDirectory() && !file.getParentFile().isDirectory()) {
            Utils.asserts(file.getParentFile().mkdirs(), "Cannot create parent directories for requested File location");
        }
        if (file.isDirectory()) {
            if (!file.mkdirs()) {
                AsyncHttpClient.log.d(LOG_TAG, "Cannot create directories for requested Directory location, might not be a problem");
            }
        }
        this.file = file;
        this.append = append;
        this.renameIfExists = renameTargetFileIfExists;
    }
這裡多一個引數boolean usePoolThread,是否使用執行緒池裡面的執行緒實現回撥,預設是false,如果是true的話,就不會呼叫封裝好的Looper和Handler實現資料的處理和回撥。


7,SaxAsyncHttpResponseHandler,繼承自AsyncHttpResponseHandler,可以進行SAX解析。

                RequestParams params = new RequestParams();
                DefaultHandler dh = new DefaultHandler();
                TestHttpClient.get(AsyncHttpActivity.this, url, params, new SaxAsyncHttpResponseHandler<DefaultHandler>(dh) {
                    @Override
                    public void onSuccess(int statusCode, Header[] headers, DefaultHandler defaultHandler) {

                    }
                    @Override
                    public void onFailure(int statusCode, Header[] headers, DefaultHandler defaultHandler) {

                    }
                });

例項化SaxAsyncHttpResponseHandler的時候傳遞引數和返回的型別需要繼承自DefaultHandler,然後通過DefaultHandler例項進行sax解析。


Cookie持久化儲存:

這裡使用PersistentCookieStore實現持久化儲存,實現Apche HttpClient CookieStore介面,cookies儲存到SharePreferences中。

首先建立一個AsyncHttpClient例項:

AsyncHttpClient client = new AsyncHttpClient();
然後set client的cookie store為一個PersistentCookieStore例項,建構函式傳入引數Activity或者Application context。
        PersistentCookieStore cookieStore = new PersistentCookieStore(this);
        client.setCookieStore(cookieStore);
任何從伺服器獲取的cookie都會被存到這個持久化的cookie store中,需要新增自己的cookie到store中,只需要構造一個新的cookie,掉用addCookie方法就可以了。
        BasicClientCookie cookie = new BasicClientCookie("testName","testValue");
        cookie.setVersion(1);
        cookie.setDomain("mydomain.com");
        cookie.setPath("/");
        cookieStore.addCookie(cookie);
RequestParams給GET/POST請求新增引數:

RequestParams可以通過不同的方式來建立例項,建立一個空的RequestParams,新增引數:

        RequestParams params = new RequestParams();
        params.put("name","tome");
        params.put("age","22");

建立帶單個引數例項:

        RequestParams params = new RequestParams("singleParam","value");
通過一個已存在的鍵/值Map來建立:
        Map<String,Object> map = new HashMap<String,Object>();
        map.put("name","Tom");
        RequestParams params = new RequestParams(map);

RequestParams上傳檔案:

支援多種檔案上傳,RequestParams上傳InputStream:

        InputStream in = ....;
        RequestParams params = new RequestParams();
        params.put("secrect_password",in,"password.txt");
這裡put三個引數分別是:key;InputStream;InputStream的name。

RequestParams上傳File物件:

        File file = new File("../../text.png");
        RequestParams params = new RequestParams();
        try {
            params.put("myFile",file);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }
RequestParams上傳位元組陣列:
        byte [] newByte = ...;
        RequestParams params = new RequestParams();
        params.put("key",new ByteArrayInputStream(newByte),"test.mp3");

以上是對該網路請求框架閱讀官方教程後的一些瞭解,有什麼不足之處還望指點。