android-async-httpフレームのソースコードを読みます。
16654 ワード
PS文:最終的にはCSDNを選んで、ここ数年の知識点を整理して発表します。この文章はCSDNに平行に移行します。CSDNもMarkDown文法を支持したので、牛は強いです。
オープンソース項目リンク
android-async-http倉庫:git clonehttps://github.com/loopj/android-async-http
android-async-httpホームページ:http://loopj.com/android-async-http/
通学分析を開始する
前編の基礎に基づいて教程を使うと、まず得られたのはAynchttp Clientの実例です。ここから分析を始めます。
次に私達が調達したのはAcynchttp Clientの中の各種get、post、deleteなどの方法です。コードを見ると、それらが最終的に呼び出されたのは全部sendRequestの方法です。
今から見ると、要求を送る過程は主に要求を作成してから、submitはスレッド池に行って、残りのことはスレッド池に任せて自分で処理します。私たちは座って待つだけで呼び出されます。
newAsynchttpRequestというロジックの実現を見に来ました。
これで要求プロセスを送り終わります。
受信プロセスはより簡単ですので、分析はしません。
いくつかの文をまとめる
振り返ってみると、私たちの要求にはContectパラメータを加えたほうがいいです。これはActivity pauseまたはstopの時に無駄な要求をキャンセルすることができます。
更に全体の機能を整理します。
AynchttpClientコアクラスは、HttpClientを使ってネットワーク要求を実行し、get、put、post、delete、headなどの要求方法を提供しています。使いやすいです。url及びRequest Paramesで該当方法を呼び出すだけでいいです。また、Conteet関連の要求をキャンセルするために選択的にConttに導入できます。(AynchttpResonseHandlerからReportynch Handler Interface)の実装クラスは、一般的にAynchttpResonseHandlerのサブクラスであり、AynchttpClient内部にスレッドがあり、AynchttpClientを使ってネットワーク要求を実行すると、最終的にはsendquestメソッドを呼び出して、このパッケージ化要求を行います。内部のスレッドプールで実行します。
SynchttpClidentはAynchttpClientから引き継ぎ、ネットワーク要求を同期して実行し、AynchttpClientは要求をAynchttpRequestにカプセル化してからスレッド池に提出し、SynchttpClientは要求をAcHttpRequestにカプセル化した後、直接にそのrun方法を呼び出す。
Aynch HttpRequestはRunnablearから引き継ぎ、submitからスレッド池にネットワーク要求を行い、start、successなどのメッセージを送る。
Asynch HttpResonseHandlerは要求結果を受信して、普通はOnSuccessとone Failureは成功または失敗を要求するメッセージを受信して、onStart、onFinishなどのメッセージがあります。
Text HttpResonseHandler、JsonHttpResonseHandler、BaseJsonHttpResonseHandlerなどはAsynchttpResonseHandlerから継承されていますが、AynchttpResonseHandlerのSuonccessとonlineメソッドは書き換えられます。
Request Paraamsはパラメータを要求し、通常の文字列パラメータを追加してファイルをアップロードすることができます。
最近は状態がないので、分析を深めて後で補充します。
オープンソース項目リンク
android-async-http倉庫:git clonehttps://github.com/loopj/android-async-http
android-async-httpホームページ:http://loopj.com/android-async-http/
通学分析を開始する
前編の基礎に基づいて教程を使うと、まず得られたのはAynchttp Clientの実例です。ここから分析を始めます。
/** * Creates a new AsyncHttpClient with default constructor arguments values */
public AsyncHttpClient() {
this(false, 80, 443);
}
AynchttpClientクラスのこのデフォルトのコンストラクタは最終的に次のAsyncHttpClient(boolean fixNoHttpResponseException, int httpPort, int httpsPort)
コンストラクタに転送され、デフォルト値にHTTPプロトコルが設定されているデフォルトポートは80で、HTTPSプロトコルのデフォルトポートは443である。他のコンストラクタによってAynchttpClientオブジェクトを実装できることも分かった。/** * 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));
}
まず、ConManager ParamesとHttpConnection ParamesとHttpProtocol Paramesによって基本パラメータを設定します。例えば、バージョン、timeout時間、max connect countなど。次に、createConnectionManager(schemeRegistry, httpParams);
方法でClienntConnection Managerを作成しましたが、実際には、現在のクラスThreadSafeClientConManagerは複雑な実装でクライアント接続池を管理しています。これは、複数の実行スレッドからサービス接続要求を行うこともできます。基本的なルートごとに接続は池で管理されています。次いで、ネットワーク要求のスレッドプールは、threadPool = getDefaultThreadPool();
を介して初期化される。次に、request Mapを初期化し、Android Contectに対応する要求mapを使用する。client Header Mapを初期化して、クライアントを置く要求header mapと使用する。次に一対の初期化であり、完了はhttpClient.setHttpRequestRetryHandler(new RetryHandler(DEFAULT_MAX_RETRIES, DEFAULT_RETRY_SLEEP_TIME_MILLIS));
によって再試験されたHandlerを設定し、適切な場合に自動的に再試行される。次に私達が調達したのはAcynchttp Clientの中の各種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);
を介してRequest Handleに要求を包装して、その後のキャンセル、管理などの動作を行う主な論理である。今から見ると、要求を送る過程は主に要求を作成してから、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);
}
実質的にはAynchttpRequestの例を得て、引き続き見てみるとAynchttpRequest implement Runnableが発見されます。これはsubmitがスレッド池に行くRunnableです。これで要求プロセスを送り終わります。
受信プロセスはより簡単ですので、分析はしません。
いくつかの文をまとめる
振り返ってみると、私たちの要求にはContectパラメータを加えたほうがいいです。これはActivity pauseまたはstopの時に無駄な要求をキャンセルすることができます。
更に全体の機能を整理します。
AynchttpClientコアクラスは、HttpClientを使ってネットワーク要求を実行し、get、put、post、delete、headなどの要求方法を提供しています。使いやすいです。url及びRequest Paramesで該当方法を呼び出すだけでいいです。また、Conteet関連の要求をキャンセルするために選択的にConttに導入できます。(AynchttpResonseHandlerからReportynch Handler Interface)の実装クラスは、一般的にAynchttpResonseHandlerのサブクラスであり、AynchttpClient内部にスレッドがあり、AynchttpClientを使ってネットワーク要求を実行すると、最終的にはsendquestメソッドを呼び出して、このパッケージ化要求を行います。内部のスレッドプールで実行します。
SynchttpClidentはAynchttpClientから引き継ぎ、ネットワーク要求を同期して実行し、AynchttpClientは要求をAynchttpRequestにカプセル化してからスレッド池に提出し、SynchttpClientは要求をAcHttpRequestにカプセル化した後、直接にそのrun方法を呼び出す。
Aynch HttpRequestはRunnablearから引き継ぎ、submitからスレッド池にネットワーク要求を行い、start、successなどのメッセージを送る。
Asynch HttpResonseHandlerは要求結果を受信して、普通はOnSuccessとone Failureは成功または失敗を要求するメッセージを受信して、onStart、onFinishなどのメッセージがあります。
Text HttpResonseHandler、JsonHttpResonseHandler、BaseJsonHttpResonseHandlerなどはAsynchttpResonseHandlerから継承されていますが、AynchttpResonseHandlerのSuonccessとonlineメソッドは書き換えられます。
Request Paraamsはパラメータを要求し、通常の文字列パラメータを追加してファイルをアップロードすることができます。
最近は状態がないので、分析を深めて後で補充します。