彻底掌握网络通信(八)AsyncHttpClient源码解读

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/yi_master/article/details/80619480

网络通信系列文章序

彻底掌握网络通信(一)Http协议基础知识
彻底掌握网络通信(二)Apache的HttpClient基础知识
彻底掌握网络通信(三)Android源码中HttpClient的在不同版本的使用
彻底掌握网络通信(四)Android源码中HttpClient的发送框架解析
彻底掌握网络通信(五)DefaultRequestDirector解析
彻底掌握网络通信(六)HttpRequestRetryHandler解析
彻底掌握网络通信(七)ConnectionReuseStrategy,ConnectionKeepAliveStrategy解析
彻底掌握网络通信(八)AsyncHttpClient源码解读
彻底掌握网络通信(九)AsyncHttpClient为什么无法用Fiddler来抓包
彻底掌握网络通信(十)AsyncHttpClient如何发送JSON解析JSON,以及一些其他用法


在之前的文章中,我们系统的分析了httpclient的内部发送过程,以及httpclient是如何重连,保活等机制;这章我们就来看下以httpclient为原型而封装的一个异步请求发送库AsyncHttpClient,下载地址

官网地址


1:一次完整发送过程
这里写图片描述

可见AsyncHttpClient的发送也是依赖DefaultHttpClient类的execute方法,总结一下具体的步骤
1.1)创建AsyncHttpClient实例,在这个过程中主要完成一些httpparams参数的设置,线程池的创建,重发机制的创建等等
1.2)调用post或者get等方法,传入实现了ResponseHandlerInterface接口的类,该接口主要用于回调请求成功和失败
1.3)在调用post和get之后,会构建AsyncHttpRequest实例,该实例实现了runnable接口,并将该实例提交到线程池
1.4)提交到线程池之后,AsyncHttpRequest的makeRequest方法会调用DefaultHttpClient的excute方法完成http请求,之后调用ResponseHandlerInterface的sendResponseMessage方法
1.5)在ResponseHandlerInterface具体实现类的sendResponseMessage方法中会完成资源的释放,并通过handler将处理结果(如成功,失败的)消息发送出去

2:我们看下AsyncHttpClient中的DefaultHttpClient和Apach中的DefaultHttpClient在实现上有什么区别
2.1)AsyncHttpClient中的DefaultHttpClient的类图
这里写图片描述

2.2)Apach中的DefaultHttpClient的类图
这里写图片描述

从上面的两个类图可见,他们两者之间的区别就在于AbstractHttpClient的实现上,AsyncHttpClient继承了CloseableHttpClient类,同时CloseableHttpClient实现了Closeable接口和HttpClient接口,这样做的好处就是AsyncHttpClient更容易对资源进行释放和处理

3:AsyncHttpClient的常用发起请求的代码

        AsyncHttpClient client = new AsyncHttpClient();
        client.get("http://www.baidu.com", new AsyncHttpResponseHandler() {
            @Override
            public void onSuccess(int i, cz.msebera.android.httpclient.Header[] headers, byte[] bytes) {
                Log.d("hwj", "**AsyncHttpClientActivity onSuccess**");
            }

            @Override
            public void onFailure(int i, cz.msebera.android.httpclient.Header[] headers, byte[] bytes, Throwable throwable) {
                Log.d("hwj", "**AsyncHttpClientActivity onFailure**");
            }
        });

很简答的代码,下面我们就这个post请求做下源码分析

4:源码分析
4.1)实例化AsyncHttpClient

    public AsyncHttpClient() {
        this(false, 80, 443);
    }

调用

    public AsyncHttpClient(boolean fixNoHttpResponseException, int httpPort, int httpsPort) {
        this(getDefaultSchemeRegistry(fixNoHttpResponseException, httpPort, httpsPort));
    }

最终调用如下方法完成实例的创建

    public AsyncHttpClient(SchemeRegistry schemeRegistry) {
        this.maxConnections = 10;
        this.connectTimeout = 10000;
        this.responseTimeout = 10000;
        this.isUrlEncodingEnabled = true;
        BasicHttpParams httpParams = new BasicHttpParams();
        ConnManagerParams.setTimeout(httpParams, (long)this.connectTimeout);
        ConnManagerParams.setMaxConnectionsPerRoute(httpParams, new ConnPerRouteBean(this.maxConnections));
        ConnManagerParams.setMaxTotalConnections(httpParams, 10);
        HttpConnectionParams.setSoTimeout(httpParams, this.responseTimeout);
        HttpConnectionParams.setConnectionTimeout(httpParams, this.connectTimeout);
        HttpConnectionParams.setTcpNoDelay(httpParams, true);
        HttpConnectionParams.setSocketBufferSize(httpParams, 8192);
        HttpProtocolParams.setVersion(httpParams, HttpVersion.HTTP_1_1);
        ClientConnectionManager cm = this.createConnectionManager(schemeRegistry, httpParams);
        Utils.asserts(cm != null, "Custom implementation of #createConnectionManager(SchemeRegistry, BasicHttpParams) returned null");
        this.threadPool = this.getDefaultThreadPool();
        this.requestMap = Collections.synchronizedMap(new WeakHashMap());
        this.clientHeaderMap = new HashMap();
        this.httpContext = new SyncBasicHttpContext(new BasicHttpContext());
        this.httpClient = new DefaultHttpClient(cm, httpParams);
        this.httpClient.addRequestInterceptor(new HttpRequestInterceptor() {
            public void process(HttpRequest request, HttpContext context) {
                if (!request.containsHeader("Accept-Encoding")) {
                    request.addHeader("Accept-Encoding", "gzip");
                }

                String header;
                for(Iterator var3 = AsyncHttpClient.this.clientHeaderMap.keySet().iterator(); var3.hasNext(); request.addHeader(header, (String)AsyncHttpClient.this.clientHeaderMap.get(header))) {
                    header = (String)var3.next();
                    if (request.containsHeader(header)) {
                        Header overwritten = request.getFirstHeader(header);
                        AsyncHttpClient.log.d("AsyncHttpClient", String.format("Headers were overwritten! (%s | %s) overwrites (%s | %s)", header, AsyncHttpClient.this.clientHeaderMap.get(header), overwritten.getName(), overwritten.getValue()));
                        request.removeHeader(overwritten);
                    }
                }

            }
        });
        this.httpClient.addResponseInterceptor(new HttpResponseInterceptor() {
            public void process(HttpResponse response, HttpContext context) {
                HttpEntity entity = response.getEntity();
                if (entity != null) {
                    Header encoding = entity.getContentEncoding();
                    if (encoding != null) {
                        HeaderElement[] var5 = encoding.getElements();
                        int var6 = var5.length;

                        for(int var7 = 0; var7 < var6; ++var7) {
                            HeaderElement element = var5[var7];
                            if (element.getName().equalsIgnoreCase("gzip")) {
                                response.setEntity(new AsyncHttpClient.InflatingEntity(entity));
                                break;
                            }
                        }
                    }

                }
            }
        });
        this.httpClient.addRequestInterceptor(new HttpRequestInterceptor() {
            public void process(HttpRequest request, HttpContext context) throws HttpException, IOException {
                AuthState authState = (AuthState)context.getAttribute("http.auth.target-scope");
                CredentialsProvider credsProvider = (CredentialsProvider)context.getAttribute("http.auth.credentials-provider");
                HttpHost targetHost = (HttpHost)context.getAttribute("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);
        this.httpClient.setHttpRequestRetryHandler(new RetryHandler(5, 1500));
    }

第6~14行,设置httpparams参数
第15行,设置客户端连接管理,在Apache的DefaultHttpClient中,客户端连接管理默认为SingleClientConnManager,管理客户端连接管理者的作用,可参考这里管理客户端连接管理者
第17行,设置线程池,用于将http请求提交到线程池中
第21行,设置DefaultHttpClient的实例
第22行~76行,设置Http请求的拦截器
第77行,设置http请求的重试机制

4.1.1)看下线程池

    protected ExecutorService getDefaultThreadPool() {
        return Executors.newCachedThreadPool();
    }
    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

通过源码我们可以看见创建了一个max_value,队列为SynchronousQueue的线程池

4.2)提交http请求到线程池中
在构建好AsyncHttpClient实例之后,就可以调用post方法完成请求的发送

    public RequestHandle post(String url, ResponseHandlerInterface responseHandler) {
        return this.post((Context)null, url, (RequestParams)null, responseHandler);
    }

其最终调用

    public RequestHandle post(Context context, String url, HttpEntity entity, String contentType, ResponseHandlerInterface responseHandler) {
        return this.sendRequest(this.httpClient, this.httpContext, this.addEntityToRequestBase(new HttpPost(this.getURI(url)), entity), contentType, responseHandler, context);
    }

这个方法里面,有一个HttpEntity entity的参数,当你的http请求中,如果有需要添加到消息体里的内容,就可以通过这个参数进行添加

我们看下sendRequest方法

    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");
        } else if (responseHandler == null) {
            throw new IllegalArgumentException("ResponseHandler must not be null");
        } else 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.");
        } else {
            if (contentType != null) {
                if (uriRequest instanceof HttpEntityEnclosingRequestBase && ((HttpEntityEnclosingRequestBase)uriRequest).getEntity() != null && uriRequest.containsHeader("Content-Type")) {
                    log.w("AsyncHttpClient", "Passed contentType will be ignored because HttpEntity sets content type");
                } else {
                    uriRequest.setHeader("Content-Type", contentType);
                }
            }

            responseHandler.setRequestHeaders(uriRequest.getAllHeaders());
            responseHandler.setRequestURI(uriRequest.getURI());
            AsyncHttpRequest request = this.newAsyncHttpRequest(client, httpContext, uriRequest, contentType, responseHandler, context);
            this.threadPool.submit(request);
            RequestHandle requestHandle = new RequestHandle(request);
            if (context != null) {
                Map var10 = this.requestMap;
                List requestList;
                synchronized(this.requestMap) {
                    requestList = (List)this.requestMap.get(context);
                    if (requestList == null) {
                        requestList = Collections.synchronizedList(new LinkedList());
                        this.requestMap.put(context, requestList);
                    }
                }

                requestList.add(requestHandle);
                Iterator iterator = requestList.iterator();

                while(iterator.hasNext()) {
                    if (((RequestHandle)iterator.next()).shouldBeGarbageCollected()) {
                        iterator.remove();
                    }
                }
            }

            return requestHandle;
        }
    }

第19行,创建AsyncHttpRequest request实例,AsyncHttpRequest 实现了runnable接口
第20行,将上面创建的request提交到线程池中去进行http请求的发送
第21行,创建RequestHandle requestHandle
第22行~33行,将requestHandle添加到requestList 的list中,这样开发者就可以调用cancelRequests来取消一个正在进行http请求了
第34行~41行,如果是已经执行的http请求,则需要在requestList 清空掉

4.3:发送http请求
当我们把AsyncHttpRequest request提交到线程池中,一个http请求就会被发起,看下AsyncHttpRequest 的run方法

    public void run() {
        if (!this.isCancelled()) {
            if (!this.isRequestPreProcessed) {
                this.isRequestPreProcessed = true;
                this.onPreProcessRequest(this);
            }

            if (!this.isCancelled()) {
                this.responseHandler.sendStartMessage();
                if (!this.isCancelled()) {
                    try {
                        this.makeRequestWithRetries();
                    } catch (IOException var2) {
                        if (!this.isCancelled()) {
                            this.responseHandler.sendFailureMessage(0, (Header[])null, (byte[])null, var2);
                        } else {
                            AsyncHttpClient.log.e("AsyncHttpRequest", "makeRequestWithRetries returned error", var2);
                        }
                    }

                    if (!this.isCancelled()) {
                        this.responseHandler.sendFinishMessage();
                        if (!this.isCancelled()) {
                            this.onPostProcessRequest(this);
                            this.isFinished = true;
                        }
                    }
                }
            }
        }
    }

第8行,当这个请求没有被cancle的情况下,即没有调用cancelRequests的情况下,方将请求发出去
第9行,通过handler发送一个what为2的message给AsyncHttpResponseHandler的handleMessage处理,AsyncHttpResponseHandler在接收到what为2的msg回调onStart方法,onStart是空方法体,可自行实现其逻辑
第12行,调用makeRequestWithRetries将请求真正发送出去
第15行,当http请求在经过重试并出现IOException 异常的时候,通过handler发送一个what为1的message给AsyncHttpResponseHandler的handleMessage处理,AsyncHttpResponseHandler在接收到what为1的msg回调onFailure方法
第21行~26行,当一个请求被正常执行,通过handler发送一个what为3的message给AsyncHttpResponseHandler的handleMessage处理,AsyncHttpResponseHandler在接收到what为3的msg回调onFinish方法,onFinish是空方法体,可自行实现其逻辑

我们重点看下makeRequestWithRetries是如何发送http请求的

    private void makeRequestWithRetries() throws IOException {
        boolean retry = true;
        IOException cause = null;
        HttpRequestRetryHandler retryHandler = this.client.getHttpRequestRetryHandler();

        while(true) {
            try {
                if (retry) {
                    try {
                        this.makeRequest();
                        return;
                    } catch (UnknownHostException var5) {
                        cause = new IOException("UnknownHostException exception: " + var5.getMessage());
                        retry = this.executionCount > 0 && retryHandler.retryRequest(var5, ++this.executionCount, this.context);
                    } catch (NullPointerException var6) {
                        cause = new IOException("NPE in HttpClient: " + var6.getMessage());
                        retry = retryHandler.retryRequest(cause, ++this.executionCount, this.context);
                    } catch (IOException var7) {
                        if (this.isCancelled()) {
                            return;
                        }

                        cause = var7;
                        retry = retryHandler.retryRequest(var7, ++this.executionCount, this.context);
                    }

                    if (retry) {
                        this.responseHandler.sendRetryMessage(this.executionCount);
                    }
                    continue;
                }
            } catch (Exception var8) {
                AsyncHttpClient.log.e("AsyncHttpRequest", "Unhandled exception origin cause", var8);
                cause = new IOException("Unhandled exception: " + var8.getMessage());
            }

            throw cause;
        }
    }

第4行,得到我们在创建AsyncHttpClient实例的时候设置的HttpRequestRetryHandler
第6行,开启while循环,当出现异常的情况下,我们可以通过HttpRequestRetryHandler来尝试重新发送http请求
第10行,调用 makeRequest完成http消息发送

    private void makeRequest() throws IOException {
        if (!this.isCancelled()) {
            if (this.request.getURI().getScheme() == null) {
                throw new MalformedURLException("No valid URI scheme was provided");
            } else {
                if (this.responseHandler instanceof RangeFileAsyncHttpResponseHandler) {
                    ((RangeFileAsyncHttpResponseHandler)this.responseHandler).updateRequestHeaders(this.request);
                }

                HttpResponse response = this.client.execute(this.request, this.context);
                if (!this.isCancelled()) {
                    this.responseHandler.onPreProcessResponse(this.responseHandler, response);
                    if (!this.isCancelled()) {
                        this.responseHandler.sendResponseMessage(response);
                        if (!this.isCancelled()) {
                            this.responseHandler.onPostProcessResponse(this.responseHandler, response);
                        }
                    }
                }
            }
        }
    }

第10行,通过调用DefaultHttpClient的execute方法完成http请求的发送和接收,这个过程和Apache的DefaultHttpClient的执行过程是一致的,有不明白的地方可以参考
彻底掌握网络通信(四)Android源码中HttpClient的发送框架解析
彻底掌握网络通信(五)DefaultRequestDirector解析
第14行,调用AsyncHttpResponseHandler的sendResponseMessage,将response传递给AsyncHttpResponseHandler

我们在看下AsyncHttpResponseHandler的sendResponseMessage方法

    public void sendResponseMessage(HttpResponse response) throws IOException {
        if (!Thread.currentThread().isInterrupted()) {
            StatusLine status = response.getStatusLine();
            byte[] responseBody = this.getResponseData(response.getEntity());
            if (!Thread.currentThread().isInterrupted()) {
                if (status.getStatusCode() >= 300) {
                    this.sendFailureMessage(status.getStatusCode(), response.getAllHeaders(), responseBody, new HttpResponseException(status.getStatusCode(), status.getReasonPhrase()));
                } else {
                    this.sendSuccessMessage(status.getStatusCode(), response.getAllHeaders(), responseBody);
                }
            }
        }

    }

第4行,将response转为byte[]
第9行,通过handler发送一个what为0的message给AsyncHttpResponseHandler的handleMessage处理,AsyncHttpResponseHandler在接收到what为0的msg回调onSuccess方法

我们注意看下第4行的getResponseData的方法,这里有很关键的资源释放动作

    byte[] getResponseData(HttpEntity entity) throws IOException {
        byte[] responseBody = null;
        if (entity != null) {
            InputStream instream = entity.getContent();
            if (instream != null) {
                long contentLength = entity.getContentLength();
                if (contentLength > 2147483647L) {
                    throw new IllegalArgumentException("HTTP entity too large to be buffered in memory");
                }

                int buffersize = contentLength <= 0L ? 4096 : (int)contentLength;

                try {
                    ByteArrayBuffer buffer = new ByteArrayBuffer(buffersize);

                    try {
                        byte[] tmp = new byte[4096];
                        long count = 0L;

                        int l;
                        while((l = instream.read(tmp)) != -1 && !Thread.currentThread().isInterrupted()) {
                            count += (long)l;
                            buffer.append(tmp, 0, l);
                            this.sendProgressMessage(count, contentLength <= 0L ? 1L : contentLength);
                        }
                    } finally {
                        AsyncHttpClient.silentCloseInputStream(instream);
                        AsyncHttpClient.endEntityViaReflection(entity);
                    }

                    responseBody = buffer.toByteArray();
                } catch (OutOfMemoryError var16) {
                    System.gc();
                    throw new IOException("File too large to fit into available memory");
                }
            }
        }

        return responseBody;
    }

从第26行开始,就是默认处理一些资源的回收动作

AsyncHttpClient的分析到此结束 - _ -

猜你喜欢

转载自blog.csdn.net/yi_master/article/details/80619480
今日推荐