天天看點

Android開源架構之AsyncHttpClient

AsyncHttpClient應用了Android的Handler發送消息機制。你也可以把AsyncHttpClient應用在Service中或者背景線程中,庫代碼會自動識别出它所運作的context。它的feature包括:

1. 發送異步http請求,在匿名callback對象中處理response;

2. http請求發生在UI線程之外;

3. 内部采用線程池來處理并發請求;

4. GET/POST 參數構造,通過RequestParams類。

5. 内置多部分檔案上傳,不需要第三方庫支援;

6. 流式Json上傳,不需要額外的庫;

7. 能處理環行和相對重定向;

8. 和你的app大小相比來說,庫的size很小,所有的一切隻有90kb;

9. 自動智能的請求重試機制在各種各樣的移動連接配接環境中;

10. 自動的gzip響應解碼;

11. 内置多種形式的響應解析,有原生的位元組流,string,json對象,甚至可以将response寫到檔案中;

12. 永久的cookie儲存,内部實作用的是Android的SharedPreferences;

13. 通過BaseJsonHttpResponseHandler和各種json庫內建;

14. 支援SAX解析器;

15. 支援各種語言和content編碼,不僅僅是UTF-8。

  android-async-http來寫代碼是怎麼實作,簡單來說你隻需要3步:

1. 建立一個AsyncHttpClient;

2. (可選的)通過RequestParams對象設定請求參數;

3. 調用AsyncHttpClient的某個get方法,傳遞你需要的(成功和失敗時)callback接口實作,一般都是匿名内部類,實作了AsyncHttpResponseHandler,類庫自己也提供了好些現成的response handler,你一般不需要自己建立一個。

通過AsyncHttpClient類的執行個體就可以執行網絡請求,包括get、put、post、head、delete。并指定一個ResponseHandlerInterface的執行個體接收請求結果。

public class RequestManager {

    private static AsyncHttpClient client = new AsyncHttpClient();

    static {
        client.setTimeout();// 設定連結逾時,如果不設定,預設為10s
    }

    public static AsyncHttpClient getClient() {
        return client;
    }

    /**
     * http Get 請求
     *
     * @param requestCode 請求碼
     * @param url         請求url 位址
     * @param params      請求參數集合
     * @param callback    回調函數
     */
    public static void get(final int requestCode, String url, RequestParams params, final RequestCallback callback) {
        if (params == null) params = new RequestParams();
        LogUtils.i("http-get", url + "?" + params.toString());
        client.get(url, params, new TextHttpResponseHandler() {
            @Override
            public void onFailure(int statusCode, Header[] headers, String responseString, Throwable throwable) {
                callback.onFailureCallback(requestCode, responseString);
            }

            @Override
            public void onSuccess(int statusCode, Header[] headers, String responseString) {
                callback.onSuccessCallback(requestCode, responseString);
            }
        });
    }

    /**
     * http Post 請求
     *
     * @param requestCode 請求碼
     * @param url         請求url 位址
     * @param params      請求參數集合
     * @param callback    回調函數
     */
    public static void post(final int requestCode, String url, RequestParams params, final RequestCallback callback) {
        if (params == null) params = new RequestParams();
        LogUtils.i("http-post", url + "?" + params.toString());
        client.post(url, params, new TextHttpResponseHandler() {
            @Override
            public void onFailure(int statusCode, Header[] headers, String responseString,
                                  Throwable throwable) {
                callback.onFailureCallback(requestCode, responseString);
            }

            @Override
            public void onSuccess(int statusCode, Header[] headers, String responseString) {
                callback.onSuccessCallback(requestCode, responseString);
            }
        });
    }

    /**
     * http Post 請求 ,指定CONTENT_TYPE 為application/json
     *
     * @param context     context
     * @param requestCode 請求碼
     * @param url         請求url 位址
     * @param params      請求參數集合
     * @param callback    回調函數
     */
    public static void post(Context context, final int requestCode, String url, String params,
                            final RequestCallback callback) {
        LogUtils.i("http-post", url + "?" + params);
        ByteArrayEntity entity = null;
        try {
            entity = new ByteArrayEntity(params.getBytes("UTF-8"));
            entity.setContentType(new BasicHeader(HTTP.CONTENT_TYPE, "application/json"));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        client.post(context, url, entity, "application/json", new JsonHttpResponseHandler() {
            @Override
            public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
                callback.onSuccessCallback(requestCode, response != null ? response.toString() : "");
            }

            @Override
            public void onFailure(int statusCode, Header[] headers, Throwable throwable, JSONObject errorResponse) {
                callback.onFailureCallback(requestCode, errorResponse != null ? errorResponse.toString() : "");
            }
        });
    }

    /**
     * http Delete 請求
     *
     * @param requestCode 請求碼
     * @param url         請求url 位址
     * @param params      請求參數集合
     * @param callback    回調函數
     */
    public static void delete(final int requestCode, String url, RequestParams params, final RequestCallback callback) {
        if (params == null) params = new RequestParams();
        LogUtils.i("http-delete", url + "?" + params.toString());
        client.delete(url, params, new TextHttpResponseHandler() {
            @Override
            public void onFailure(int statusCode, Header[] headers, String responseString, Throwable throwable) {
                callback.onFailureCallback(requestCode, responseString);
            }

            @Override
            public void onSuccess(int statusCode, Header[] headers, String responseString) {
                callback.onSuccessCallback(requestCode, responseString);
            }
        });
    }

    /**
     * http Put 請求
     *
     * @param requestCode 請求碼
     * @param url         請求url 位址
     * @param params      請求參數集合
     * @param callback    回調函數
     */
    public static void put(final int requestCode, String url, RequestParams params, final RequestCallback callback) {
        if (params == null) params = new RequestParams();
        LogUtils.i("http-put", url + "?" + params.toString());
        client.put(url, params, new TextHttpResponseHandler() {
            @Override
            public void onFailure(int statusCode, Header[] headers, String responseString, Throwable throwable) {
                callback.onFailureCallback(requestCode, responseString);
            }

            @Override
            public void onSuccess(int statusCode, Header[] headers, String responseString) {
                callback.onSuccessCallback(requestCode, responseString);
            }
        });
    }

    /**
     * http Put 請求 指定CONTENT_TYPE 為application/json
     *
     * @param context     context
     * @param requestCode 請求碼
     * @param url         請求url 位址
     * @param params      請求參數集合
     * @param callback    回調函數
     */
    public static void put(Context context, final int requestCode, String url, String params, final RequestCallback callback) {
        LogUtils.e("http-request", url + "?" + params);
        ByteArrayEntity entity = null;
        try {
            entity = new ByteArrayEntity(params.getBytes("UTF-8"));
            entity.setContentType(new BasicHeader(HTTP.CONTENT_TYPE, "application/json"));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        client.put(context, url, entity, "application/json", new JsonHttpResponseHandler() {
            @Override
            public void onSuccess(int statusCode, Header[] headers, JSONObject response) {
                callback.onSuccessCallback(requestCode, response != null ? response.toString() : "");
            }

            @Override
            public void onFailure(int statusCode, Header[] headers, Throwable throwable, JSONObject errorResponse) {
                callback.onFailureCallback(requestCode, errorResponse != null ? errorResponse.toString() : "");
            }
        });
    }
}
           

AsyncHttpClient主要類介紹

AsyncHttpRequest

繼承自Runnabler,被submit至線程池執行網絡請求并發送start,success等消息

AsyncHttpResponseHandler

接收請求結果,一般重寫onSuccess及onFailure接收請求成功或失敗的消息,還有onStart,onFinish等消息

TextHttpResponseHandler

繼承自AsyncHttpResponseHandler,隻是重寫了AsyncHttpResponseHandler的onSuccess和onFailure方法,将請求結果由byte數組轉換為String

JsonHttpResponseHandler

繼承自TextHttpResponseHandler,同樣是重寫onSuccess和onFailure方法,将請求結果由String轉換為JSONObject或JSONArray

BaseJsonHttpResponseHandler

繼承自TextHttpResponseHandler,是一個泛型類,提供了parseResponse方法,子類需要提供實作,将請求結果解析成需要的類型,子類可以靈活地使用解析方法,可以直接原始解析,使用gson等。

RequestParams

請求參數,可以添加普通的字元串參數,并可添加File,InputStream上傳檔案

AsyncHttpClient

核心類,使用HttpClient執行網絡請求,提供了get,put,post,delete,head等請求方法,使用起來很簡單,隻需以url及RequestParams調用相應的方法即可,還可以選擇性地傳入Context,用于取消Content相關的請求,同時必須提供ResponseHandlerInterface(AsyncHttpResponseHandler繼承自ResponseHandlerInterface)的實作類,一般為AsyncHttpResponseHandler的子類,AsyncHttpClient内部有一個線程池,當使用AsyncHttpClient執行網絡請求時,最終都會調用sendRequest方法,在這個方法内部将請求參數封裝成AsyncHttpRequest(繼承自Runnable)交由内部的線程池執行。

SyncHttpClient

繼承自AsyncHttpClient,同步執行網絡請求,AsyncHttpClient把請求封裝成AsyncHttpRequest後送出至線程池,SyncHttpClient把請求封裝成AsyncHttpRequest後直接調用它的run方法。

AsyncHttpClient請求流程

Android開源架構之AsyncHttpClient

1.調用AsyncHttpClient的get或post等方法發起網絡請求

2.所有的請求都走了sendRequest,在sendRequest中把請求封裝為了AsyncHttpRequest,并添加到線程池執行

3.當請求被執行時(即AsyncHttpRequest的run方法),執行AsyncHttpRequest的makeRequestWithRetries方法執行實際的請求,當請求失敗時可以重試。并在請求開始,結束,成功或失敗時向請求時傳的ResponseHandlerInterface執行個體發送消息

4.基本上使用的都是AsyncHttpResponseHandler的子類,調用其onStart,onSuccess等方法傳回請求結果

AsyncHttpClient說明及總結

Android-Async-Http的使用非常簡單,通過AsyncHttpClient發起請求就可以了,如果需要添加參數,直接傳一個RequestParams過去,而且參數可以是String、File和InputStream,可以很友善地上傳檔案。

每個請求都需要傳一個ResponseHandlerInterface的執行個體用以接收請求結果或請求失敗,請求結束等通知,一般是AsyncHttpResponseHandler的子類。

通過BinaryHttpResponseHandler可以發起二進制請求,如請求圖檔。

通過TextHttpResponseHandler可以發起傳回結果為字元串的請求,一般這個使用較多。

也可以使用它的子類JsonHttpResponseHandler,傳回結果是一個JSONObject或JSONArray。不過感覺這個類作用不大,一是有另一個類BaseJsonHttpResponseHandler,可以直接解析傳回的JSON資料,二是JsonHttpResponseHandler的方法太複雜了,有太多的onSuccess和onFailure方法,都不知道重寫哪個了。

Android開源架構之AsyncHttpClient

這個類庫還有一點不足,就是onSuccess等方法一般會在主線程執行。

public AsyncHttpResponseHandler() {
    boolean missingLooper = null == Looper.myLooper();
    // Try to create handler
    if (!missingLooper)
        handler = new ResponderHandler(this);
    else {
        // There is no Looper on this thread so synchronous mode should be used.
        handler = null;
        setUseSynchronousMode(true);
        Log.i(LOG_TAG, "Current thread has not called Looper.prepare(). Forcing synchronous mode.");
    }
    // Init Looper by calling postRunnable without an argument.
    postRunnable(null);
}
           

可以看到,内部使用了Handler,當建立AsyncHttpResponseHandler的執行個體的時候會擷取目前線程的Looper,如果為空就啟用同步模式,即所有的回調都會在執行請求的線程中執行,當在一個普通的背景線程時這樣執行是正常的,而我們一般都會在主線程發請請求,結果就是所有的回調都會在主線程中執行,這就限制了我們在onSuccess中執行耗時操作,比如請求成功後将資料持久化到資料庫。

不過可以看到建立Handler的時候使用了Looper對象,是以我們就可以改進一下其構造函數,添加一個Looper參數(同步修改子類),這樣所有的回調就都會在Looper所線上程執行,這樣我們隻需要開啟一個HandlerThread就行了。但這樣和Looper為空時一樣有一個弊端,

1)如果要更新UI操作的話,還需要向一個主線程的Handler發送消息讓UI更新。

2)還有第二個弊端,所有回調都在同一個HandlerThread中執行,如果一個處理耗時太久會阻塞後面的請求結果處理,如果隻是簡單地寫個資料庫影響應該不大,如果真耗時太久,為這個耗時處理再開個線程吧。