android-async-http框架庫源碼走讀
開源項目鏈接
android-async-http倉庫:git clone https://github.com/loopj/android-async-http
android-async-http主頁:http://loopj.com/android-async-http/
開始走讀分析
依據前一篇的基礎使用教程可以發現,首先得到的是AsyncHttpClient實例,所以從這里入手分析一下:
/** * Creates a new AsyncHttpClient with default constructor arguments values */ public AsyncHttpClient() { this(false, 80, 443); }
AsyncHttpClient類的這個默認構造函數最終調運了如下AsyncHttpClient(boolean fixNoHttpResponseException, int httpPort, int httpsPort)
構造函數,對于默認值設置了HTTP協議的默認端口為80,HTTPS協議的默認端口為443。同時發現可以通過其他構造函數來實例化AsyncHttpClient對象。
/** * Creates new AsyncHttpClient using given params * * @param fixNoHttpResponseException Whether to fix issue or not, by omitting SSL verification * @param httpPort HTTP port to be used, must be greater than 0 * @param httpsPort HTTPS port to be used, must be greater than 0 */ public AsyncHttpClient(boolean fixNoHttpResponseException, int httpPort, int httpsPort) { this(getDefaultSchemeRegistry(fixNoHttpResponseException, httpPort, httpsPort)); }
在該函數中調用了AsyncHttpClient(SchemeRegistry schemeRegistry)
構造函數,而真正的實例化獲取邏輯過程就在AsyncHttpClient(SchemeRegistry schemeRegistry)
方法中,如下所示:
/** * Creates a new AsyncHttpClient. * * @param schemeRegistry SchemeRegistry to be used */ public AsyncHttpClient(SchemeRegistry schemeRegistry) { BasicHttpParams httpParams = new BasicHttpParams(); ConnManagerParams.setTimeout(httpParams, connectTimeout); ConnManagerParams.setMaxConnectionsPerRoute(httpParams, new ConnPerRouteBean(maxConnections)); ConnManagerParams.setMaxTotalConnections(httpParams, DEFAULT_MAX_CONNECTIONS); HttpConnectionParams.setSoTimeout(httpParams, responseTimeout); HttpConnectionParams.setConnectionTimeout(httpParams, connectTimeout); HttpConnectionParams.setTcpNoDelay(httpParams, true); HttpConnectionParams.setSocketBufferSize(httpParams, DEFAULT_SOCKET_BUFFER_SIZE); HttpProtocolParams.setVersion(httpParams, HttpVersion.HTTP_1_1); ClientConnectionManager cm = createConnectionManager(schemeRegistry, httpParams); Utils.asserts(cm != null, "Custom implementation of #createConnectionManager(SchemeRegistry, BasicHttpParams) returned null"); threadPool = getDefaultThreadPool(); requestMap = Collections.synchronizedMap(new WeakHashMap<Context, List<RequestHandle>>()); clientHeaderMap = new HashMap<String, String>(); httpContext = new SyncBasicHttpContext(new BasicHttpContext()); httpClient = new DefaultHttpClient(cm, httpParams); httpClient.addRequestInterceptor(new HttpRequestInterceptor() { @Override public void process(HttpRequest request, HttpContext context) { if (!request.containsHeader(HEADER_ACCEPT_ENCODING)) { request.addHeader(HEADER_ACCEPT_ENCODING, ENCODING_GZIP); } for (String header : clientHeaderMap.keySet()) { if (request.containsHeader(header)) { Header overwritten = request.getFirstHeader(header); Log.d(LOG_TAG, String.format("Headers were overwritten! (%s | %s) overwrites (%s | %s)", header, clientHeaderMap.get(header), overwritten.getName(), overwritten.getValue()) ); //remove the overwritten header request.removeHeader(overwritten); } request.addHeader(header, clientHeaderMap.get(header)); } } }); httpClient.addResponseInterceptor(new HttpResponseInterceptor() { @Override public void process(HttpResponse response, HttpContext context) { final HttpEntity entity = response.getEntity(); if (entity == null) { return; } final Header encoding = entity.getContentEncoding(); if (encoding != null) { for (HeaderElement element : encoding.getElements()) { if (element.getName().equalsIgnoreCase(ENCODING_GZIP)) { response.setEntity(new InflatingEntity(entity)); break; } } } } }); httpClient.addRequestInterceptor(new HttpRequestInterceptor() { @Override public void process(final HttpRequest request, final HttpContext context) throws HttpException, IOException { AuthState authState = (AuthState) context.getAttribute(ClientContext.TARGET_AUTH_STATE); CredentialsProvider credsProvider = (CredentialsProvider) context.getAttribute( ClientContext.CREDS_PROVIDER); HttpHost targetHost = (HttpHost) context.getAttribute(ExecutionContext.HTTP_TARGET_HOST); if (authState.getAuthScheme() == null) { AuthScope authScope = new AuthScope(targetHost.getHostName(), targetHost.getPort()); Credentials creds = credsProvider.getCredentials(authScope); if (creds != null) { authState.setAuthScheme(new BasicScheme()); authState.setCredentials(creds); } } } }, 0); httpClient.setHttpRequestRetryHandler(new RetryHandler(DEFAULT_MAX_RETRIES, DEFAULT_RETRY_SLEEP_TIME_MILLIS)); }
首先通過ConnManagerParams和HttpConnectionParams及HttpProtocolParams設置一些基本參數,譬如版本,timeout時間,max connect count等。接著通過createConnectionManager(schemeRegistry, httpParams);
方法創建了一個ClientConnectionManager,其實現類ThreadSafeClientConnManager是一個復雜的實現來管理客戶端連接池,它也可以從多個執行線程中服務連接請求。對每個基本的路由,連接都是池管理的。接著通過threadPool = getDefaultThreadPool();
初始化網絡請求的線程池。接著初始化requestMap,用來與Android Context對應的請求map。初始化clientHeaderMap,用來與放置客戶端的請求header map。接著也是一對初始化,完事通過httpClient.setHttpRequestRetryHandler(new RetryHandler(DEFAULT_MAX_RETRIES, DEFAULT_RETRY_SLEEP_TIME_MILLIS));
設置重試Handler,會在合適的情況下自動重試。
接下來我們調運的就是AsyncHttpClient里面的各種get、post、delete等方法,通過看代碼可以發現它們最終調用的都是sendRequest方法,如下:
/** * Puts a new request in queue as a new thread in pool to be executed * * @param client HttpClient to be used for request, can differ in single requests * @param contentType MIME body type, for POST and PUT requests, may be null * @param context Context of Android application, to hold the reference of request * @param httpContext HttpContext in which the request will be executed * @param responseHandler ResponseHandler or its subclass to put the response into * @param uriRequest instance of HttpUriRequest, which means it must be of HttpDelete, * HttpPost, HttpGet, HttpPut, etc. * @return RequestHandle of future request process */ protected RequestHandle sendRequest(DefaultHttpClient client, HttpContext httpContext, HttpUriRequest uriRequest, String contentType, ResponseHandlerInterface responseHandler, Context context) { if (uriRequest == null) { throw new IllegalArgumentException("HttpUriRequest must not be null"); } if (responseHandler == null) { throw new IllegalArgumentException("ResponseHandler must not be null"); } if (responseHandler.getUseSynchronousMode() && !responseHandler.getUsePoolThread()) { throw new IllegalArgumentException("Synchronous ResponseHandler used in AsyncHttpClient. You should create your response handler in a looper thread or use SyncHttpClient instead."); } if (contentType != null) { if (uriRequest instanceof HttpEntityEnclosingRequestBase && ((HttpEntityEnclosingRequestBase) uriRequest).getEntity() != null) { Log.w(LOG_TAG, "Passed contentType will be ignored because HttpEntity sets content type"); } else { uriRequest.setHeader(HEADER_CONTENT_TYPE, contentType); } } responseHandler.setRequestHeaders(uriRequest.getAllHeaders()); responseHandler.setRequestURI(uriRequest.getURI()); AsyncHttpRequest request = newAsyncHttpRequest(client, httpContext, uriRequest, contentType, responseHandler, context); threadPool.submit(request); RequestHandle requestHandle = new RequestHandle(request); if (context != null) { // Add request to request map List<RequestHandle> requestList = requestMap.get(context); synchronized (requestMap) { if (requestList == null) { requestList = Collections.synchronizedList(new LinkedList<RequestHandle>()); requestMap.put(context, requestList); } } requestList.add(requestHandle); Iterator<RequestHandle> iterator = requestList.iterator(); while (iterator.hasNext()) { if (iterator.next().shouldBeGarbageCollected()) { iterator.remove(); } } } return requestHandle; }
這個方法的主要作用是將一個新的請求添加到隊列線程池中執行。
AsyncHttpRequest request = newAsyncHttpRequest(client, httpContext, uriRequest,contentType, responseHandler, context);
這行開始是主要的邏輯,其創建了請求,接著通過threadPool.submit(request);
把請求提交到線程池,接著通過RequestHandle requestHandle = new RequestHandle(request);
把請求包裝到RequestHandle用于之后的取消、管理等操作。
現在來看,發送請求的過程其實重點是創建請求,然后submit到線程池,剩下的事情就交給線程池自己處理了,我們只需要坐等被調用。
現在來看下newAsyncHttpRequest這個邏輯實現:
/** * Instantiate a new asynchronous HTTP request for the passed parameters. * * @param client HttpClient to be used for request, can differ in single requests * @param contentType MIME body type, for POST and PUT requests, may be null * @param context Context of Android application, to hold the reference of request * @param httpContext HttpContext in which the request will be executed * @param responseHandler ResponseHandler or its subclass to put the response into * @param uriRequest instance of HttpUriRequest, which means it must be of HttpDelete, * HttpPost, HttpGet, HttpPut, etc. * @return AsyncHttpRequest ready to be dispatched */ protected AsyncHttpRequest newAsyncHttpRequest(DefaultHttpClient client, HttpContexthttpContext, HttpUriRequest uriRequest, String contentType,ResponseHandlerInterface responseHandler, Context context) { return new AsyncHttpRequest(client, httpContext, uriRequest, responseHandler); }
實質就是得到了一個AsyncHttpRequest的實例,繼續看下會發現AsyncHttpRequest implements Runnable,這就是submit到線程池的Runnable了。
至此發送請求過程就結束了。
接收過程更容易,所以不做分析。
總結幾句
回過頭會發現在我們的請求中最好都加上Context參數,因為這樣可以在Activity pause或stop時取消掉沒用的請求。
再來整理下整個類功能:
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方法。
AsyncHttpRequest 繼承自Runnabler,被submit至線程池執行網絡請求并發送start,success等消息。
AsyncHttpResponseHandler 接收請求結果,一般重寫onSuccess及onFailure接收請求成功或失敗的消息,還有onStart,onFinish等消息。
TextHttpResponseHandler、JsonHttpResponseHandler、BaseJsonHttpResponseHandler這些類都繼承自AsyncHttpResponseHandler,只是重寫了AsyncHttpResponseHandler的onSuccess和onFailure方法,將請求結果進行了轉換而已。
RequestParams 請求參數,可以添加普通的字符串參數,并可添加File,InputStream上傳文件。
最近不在狀態,深入分析待日后補充。。。