基于AsyncHttpClient源码改造同步的LHttpClient

来源:互联网 发布:热分析软件哪个好 编辑:程序博客网 时间:2024/05/22 13:41

AsyncHttpClient:https://github.com/loopj/android-async-http

有时使用AsyncHttpClient时各种ResponseHandler使用很频繁。如果想要先后发送两个请求,在回调函数里面写起来很别扭,需要拆分成好几个方法,使用SyncHttpClient在回调里面写感觉怪怪的。根据它的源码摘出一个简单的、同步的LHttpClient,以备后用。做的工作只是代码搬运工,没有什么技术。参考源码版本为1.3.1和1.6.0,因为在1.6.0里面源码关联太多,有些东西不好直接使用。


谢谢@zhuxixi527提醒,post方法的时候没有传url,已经在代码上修改。但是下载资源不能编辑,只能重新上传一个了T_T


#2016-01-07修改

现在已经将代码同步到github:https://github.com/liguanliang2014/LHttpClient

修过了部分内容:增加了一个统一打印的log类,将auth的部分代码删除,修改之前的引用bug


代码结构如下:

com.net.lhttpclient

|---LHttpClient.java

|---LHttpRequest.java

|---LHttpResponse.java

|---LogUtil.java

|---RequestParams.java

|---RetryHandler.java

|---SimpleMultipartEntity.java


其中RequestParams.java、RetryHandler.java、SimpleMultipartEntity.java是从1.3.1版本直接复制过来的。


简单调用:

new Thread() {public void run() {LHttpClient httpClient = new LHttpClient();LHttpResponse response = httpClient.get("http://www.baidu.com");String str = "";try {str = new String(response.getResponse(), "utf-8");} catch (UnsupportedEncodingException e) {e.printStackTrace();}System.out.println(str);}}.start();
以后再写几个包装LHttpResponse的类,类似AsyncHttpClient中的各种Handler,使用会更方便。

源码:

LHttpClient.java(主要修改部分就是自己写的get、post和sendRequest方法,代码412-439行,只是实现了get和post,其余四个没有实现)

package com.net.lhttpclient;import java.io.IOException;import java.io.InputStream;import java.io.PushbackInputStream;import java.lang.reflect.Field;import java.util.HashMap;import java.util.Map;import java.util.zip.GZIPInputStream;import org.apache.http.Header;import org.apache.http.HeaderElement;import org.apache.http.HttpEntity;import org.apache.http.HttpHost;import org.apache.http.HttpRequest;import org.apache.http.HttpRequestInterceptor;import org.apache.http.HttpResponse;import org.apache.http.HttpResponseInterceptor;import org.apache.http.HttpVersion;import org.apache.http.auth.AuthScope;import org.apache.http.auth.UsernamePasswordCredentials;import org.apache.http.client.CookieStore;import org.apache.http.client.HttpClient;import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;import org.apache.http.client.methods.HttpGet;import org.apache.http.client.methods.HttpPost;import org.apache.http.client.methods.HttpUriRequest;import org.apache.http.client.protocol.ClientContext;import org.apache.http.conn.params.ConnManagerParams;import org.apache.http.conn.params.ConnPerRouteBean;import org.apache.http.conn.params.ConnRoutePNames;import org.apache.http.conn.scheme.PlainSocketFactory;import org.apache.http.conn.scheme.Scheme;import org.apache.http.conn.scheme.SchemeRegistry;import org.apache.http.conn.ssl.SSLSocketFactory;import org.apache.http.entity.HttpEntityWrapper;import org.apache.http.impl.client.DefaultHttpClient;import org.apache.http.impl.conn.tsccm.ThreadSafeClientConnManager;import org.apache.http.params.BasicHttpParams;import org.apache.http.params.HttpConnectionParams;import org.apache.http.params.HttpParams;import org.apache.http.params.HttpProtocolParams;import org.apache.http.protocol.BasicHttpContext;import org.apache.http.protocol.HttpContext;import org.apache.http.protocol.SyncBasicHttpContext;import com.loopj.android.http.PersistentCookieStore;/** * code from AsyncHttpClient(https://github.com/loopj/android-async-http) *  * @author ligl01 *  */public class LHttpClient {public static boolean DEBUG = true;public static final String HEADER_CONTENT_TYPE = "Content-Type";public static final String HEADER_CONTENT_RANGE = "Content-Range";public static final String HEADER_CONTENT_ENCODING = "Content-Encoding";public static final String HEADER_CONTENT_DISPOSITION = "Content-Disposition";public static final String HEADER_ACCEPT_ENCODING = "Accept-Encoding";public static final String ENCODING_GZIP = "gzip";public static final int DEFAULT_MAX_CONNECTIONS = 10;public static final int DEFAULT_TIMEOUT = 10 * 1000;public static final int DEFAULT_SOCKET_TIMEOUT = 10 * 1000;public static final int DEFAULT_MAX_RETRIES = 5;public static final int DEFAULT_RETRY_SLEEP_TIME_MILLIS = 1500;public static final int DEFAULT_SOCKET_BUFFER_SIZE = 8192;private int maxConnections = DEFAULT_MAX_CONNECTIONS;private int connectTimeout = DEFAULT_TIMEOUT;private int responseTimeout = DEFAULT_SOCKET_TIMEOUT;private final DefaultHttpClient httpClient;private final HttpContext httpContext;private final Map<String, String> clientHeaderMap;public LHttpClient() {this(80, 443);}public LHttpClient(int httpPort, int httpsPort) {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);SchemeRegistry schemeRegistry = new SchemeRegistry();schemeRegistry.register(new Scheme("http", PlainSocketFactory.getSocketFactory(), httpPort));schemeRegistry.register(new Scheme("https", SSLSocketFactory.getSocketFactory(), httpsPort));ThreadSafeClientConnManager cm = new ThreadSafeClientConnManager(httpParams, schemeRegistry);httpContext = new SyncBasicHttpContext(new BasicHttpContext());httpClient = new DefaultHttpClient(cm, httpParams);httpClient.addRequestInterceptor(new HttpRequestInterceptor() {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()) {request.addHeader(header, clientHeaderMap.get(header));}}});httpClient.addResponseInterceptor(new HttpResponseInterceptor() {public void process(HttpResponse response, HttpContext context) {final HttpEntity entity = response.getEntity();final Header encoding = entity.getContentEncoding();if (encoding != null) {for (HeaderElement element : encoding.getElements()) {if (element.getName().equalsIgnoreCase(ENCODING_GZIP)) {response.setEntity(new InflatingEntity(response.getEntity()));break;}}}}});httpClient.setHttpRequestRetryHandler(new RetryHandler(DEFAULT_MAX_RETRIES, DEFAULT_RETRY_SLEEP_TIME_MILLIS));clientHeaderMap = new HashMap<String, String>();}/** * Get the underlying HttpClient instance. This is useful for setting * additional fine-grained settings for requests by accessing the client's * ConnectionManager, HttpParams and SchemeRegistry. */public HttpClient getHttpClient() {return this.httpClient;}/** * Get the underlying HttpContext instance. This is useful for getting and * setting fine-grained settings for requests by accessing the context's * attributes such as the CookieStore. *  * @return underlying HttpContext instance */public HttpContext getHttpContext() {return this.httpContext;}/** * Sets an optional CookieStore to use when making requests *  * @param cookieStore *            The CookieStore implementation to use, usually an instance of *            {@link PersistentCookieStore} */public void setCookieStore(CookieStore cookieStore) {httpContext.setAttribute(ClientContext.COOKIE_STORE, cookieStore);}/** * Returns current connection timeout limit (milliseconds). By default, this * is set to 10 seconds. *  * @return Connection timeout limit in milliseconds */public int getConnectTimeout() {return connectTimeout;}/** * Set connection timeout limit (milliseconds). By default, this is set to * 10 seconds. *  * @param value *            Connection timeout in milliseconds, minimal value is 1000 (1 *            second). */public void setConnectTimeout(int value) {connectTimeout = value < 1000 ? DEFAULT_SOCKET_TIMEOUT : value;final HttpParams httpParams = httpClient.getParams();ConnManagerParams.setTimeout(httpParams, connectTimeout);HttpConnectionParams.setConnectionTimeout(httpParams, connectTimeout);}/** * Returns current response timeout limit (milliseconds). By default, this * is set to 10 seconds. *  * @return Response timeout limit in milliseconds */public int getResponseTimeout() {return responseTimeout;}/** * Set response timeout limit (milliseconds). By default, this is set to 10 * seconds. *  * @param value *            Response timeout in milliseconds, minimal value is 1000 (1 *            second). */public void setResponseTimeout(int value) {responseTimeout = value < 1000 ? DEFAULT_SOCKET_TIMEOUT : value;final HttpParams httpParams = httpClient.getParams();HttpConnectionParams.setSoTimeout(httpParams, responseTimeout);}/** * Sets the Proxy by it's hostname and port *  * @param hostname *            the hostname (IP or DNS name) * @param port *            the port number. -1 indicates the scheme default port. */public void setProxy(String hostname, int port) {final HttpHost proxy = new HttpHost(hostname, port);final HttpParams httpParams = this.httpClient.getParams();httpParams.setParameter(ConnRoutePNames.DEFAULT_PROXY, proxy);}/** * Sets the Proxy by it's hostname,port,username and password *  * @param hostname *            the hostname (IP or DNS name) * @param port *            the port number. -1 indicates the scheme default port. * @param username *            the username * @param password *            the password */public void setProxy(String hostname, int port, String username,String password) {httpClient.getCredentialsProvider().setCredentials(new AuthScope(hostname, port),new UsernamePasswordCredentials(username, password));final HttpHost proxy = new HttpHost(hostname, port);final HttpParams httpParams = this.httpClient.getParams();httpParams.setParameter(ConnRoutePNames.DEFAULT_PROXY, proxy);}/** * Sets the SSLSocketFactory to user when making requests. By default, a * new, default SSLSocketFactory is used. *  * @param sslSocketFactory *            the socket factory to use for https requests. */public void setSSLSocketFactory(SSLSocketFactory sslSocketFactory) {this.httpClient.getConnectionManager().getSchemeRegistry().register(new Scheme("https", sslSocketFactory, 443));}/** * Sets the maximum number of retries and timeout for a particular Request. *  * @param retries *            maximum number of retries per request * @param timeout *            sleep between retries in milliseconds */public void setMaxRetriesAndTimeout(int retries, int timeout) {this.httpClient.setHttpRequestRetryHandler(new RetryHandler(retries,timeout));}/** * Will, before sending, remove all headers currently present in * AsyncHttpClient instance, which applies on all requests this client makes */public void removeAllHeaders() {clientHeaderMap.clear();}/** * Sets headers that will be added to all requests this client makes (before * sending). *  * @param header *            the name of the header * @param value *            the contents of the header */public void addHeader(String header, String value) {clientHeaderMap.put(header, value);}/** * Remove header from all requests this client makes (before sending). *  * @param header *            the name of the header */public void removeHeader(String header) {clientHeaderMap.remove(header);}///**// * Sets basic authentication for the request. Uses AuthScope.ANY. This is// * the same as setBasicAuth('username','password',AuthScope.ANY)// * // * @param username// *            Basic Auth username// * @param password// *            Basic Auth password// *///public void setBasicAuth(String username, String password) {//setBasicAuth(username, password, false);//}/////**// * Sets basic authentication for the request. Uses AuthScope.ANY. This is// * the same as setBasicAuth('username','password',AuthScope.ANY)// * // * @param username// *            Basic Auth username// * @param password// *            Basic Auth password// * @param preemtive// *            sets authorization in preemtive manner// *///public void setBasicAuth(String username, String password, boolean preemtive) {//setBasicAuth(username, password, null, preemtive);//}/////**// * Sets basic authentication for the request. You should pass in your// * AuthScope for security. It should be like this// * setBasicAuth("username","password", new// * AuthScope("host",port,AuthScope.ANY_REALM))// * // * @param username// *            Basic Auth username// * @param password// *            Basic Auth password// * @param scope// *            - an AuthScope object// *///public void setBasicAuth(String username, String password, AuthScope scope) {//setBasicAuth(username, password, scope, false);//}/////**// * Sets basic authentication for the request. You should pass in your// * AuthScope for security. It should be like this// * setBasicAuth("username","password", new// * AuthScope("host",port,AuthScope.ANY_REALM))// * // * @param username// *            Basic Auth username// * @param password// *            Basic Auth password// * @param scope// *            an AuthScope object// * @param preemtive// *            sets authorization in preemtive manner// *///public void setBasicAuth(String username, String password, AuthScope scope,//boolean preemtive) {//UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(//username, password);//setCredentials(scope, credentials);//setAuthenticationPreemptive(preemtive);//}////public void setCredentials(AuthScope authScope, Credentials credentials) {//if (credentials == null) {//LogUtil.d("Provided credentials are null, not setting");//return;//}//this.httpClient.getCredentialsProvider().setCredentials(//authScope == null ? AuthScope.ANY : authScope, credentials);//}/////**// * Sets HttpRequestInterceptor which handles authorization in preemtive way,// * as workaround you can use call// * `AsyncHttpClient.addHeader("Authorization",// * "Basic base64OfUsernameAndPassword==")`// * // * @param isPreemtive// *            whether the authorization is processed in preemtive way// *///public void setAuthenticationPreemptive(boolean isPreemtive) {//if (isPreemtive) {//httpClient.addRequestInterceptor(//new PreemtiveAuthorizationHttpRequestInterceptor(), 0);//} else {//httpClient//.removeRequestInterceptorByClass(PreemtiveAuthorizationHttpRequestInterceptor.class);//}//}public LHttpResponse get(String url) {return get(url, null);}public LHttpResponse get(String url, RequestParams params) {return sendRequest(httpClient, httpContext, new HttpGet(getUrlWithQueryString(url, params)), null);}public LHttpResponse post(String url) {return post(url, null);}public LHttpResponse post(String url, RequestParams params) {return post(url, paramsToEntity(params), null);}public LHttpResponse post(String url, HttpEntity entity, String contentType) {return sendRequest(httpClient, httpContext,addEntityToRequestBase(new HttpPost(url), entity), contentType);}private LHttpResponse sendRequest(DefaultHttpClient client,HttpContext httpContext, HttpUriRequest uriRequest,String contentType) {LHttpRequest request = new LHttpRequest(httpClient, httpContext, uriRequest);return request.request();}private HttpEntityEnclosingRequestBase addEntityToRequestBase(HttpEntityEnclosingRequestBase requestBase, HttpEntity entity) {if (entity != null) {requestBase.setEntity(entity);}return requestBase;}private HttpEntity paramsToEntity(RequestParams params) {HttpEntity entity = null;if (params != null) {entity = params.getEntity();}return entity;}private String getUrlWithQueryString(String url, RequestParams params) {if (params != null) {String paramString = params.getParamString();url += "?" + paramString;}return url;}/**     * This horrible hack is required on Android, due to implementation of BasicManagedEntity, which     * doesn't chain call consumeContent on underlying wrapped HttpEntity     *     * @param entity HttpEntity, may be null     */    public static void endEntityViaReflection(HttpEntity entity) {        if (entity instanceof HttpEntityWrapper) {            try {                Field f = null;                Field[] fields = HttpEntityWrapper.class.getDeclaredFields();                for (Field ff : fields) {                    if (ff.getName().equals("wrappedEntity")) {                        f = ff;                        break;                    }                }                if (f != null) {                    f.setAccessible(true);                    HttpEntity wrapped = (HttpEntity) f.get(entity);                    if (wrapped != null) {                        wrapped.consumeContent();                    }                }            } catch (Throwable t) {                LogUtil.e("wrappedEntity consume", t);            }        }    }/** * Checks the InputStream if it contains GZIP compressed data *  * @param inputStream *            InputStream to be checked * @return true or false if the stream contains GZIP compressed data * @throws java.io.IOException */public static boolean isInputStreamGZIPCompressed(final PushbackInputStream inputStream) throws IOException {if (inputStream == null)return false;byte[] signature = new byte[2];int readStatus = inputStream.read(signature);inputStream.unread(signature);int streamHeader = ((int) signature[0] & 0xff)| ((signature[1] << 8) & 0xff00);return readStatus == 2 && GZIPInputStream.GZIP_MAGIC == streamHeader;}/** * A utility function to close an input stream without raising an exception. *  * @param is *            input stream to close safely */public static void silentCloseInputStream(InputStream is) {try {if (is != null) {is.close();}} catch (IOException e) {LogUtil.w("Cannot close input stream", e);}}/** * Enclosing entity to hold stream of gzip decoded data for accessing * HttpEntity contents */private static class InflatingEntity extends HttpEntityWrapper {public InflatingEntity(HttpEntity wrapped) {super(wrapped);}InputStream wrappedStream;PushbackInputStream pushbackStream;GZIPInputStream gzippedStream;@Overridepublic InputStream getContent() throws IOException {wrappedStream = wrappedEntity.getContent();pushbackStream = new PushbackInputStream(wrappedStream, 2);if (isInputStreamGZIPCompressed(pushbackStream)) {gzippedStream = new GZIPInputStream(pushbackStream);return gzippedStream;} else {return pushbackStream;}}@Overridepublic long getContentLength() {return wrappedEntity == null ? 0 : wrappedEntity.getContentLength();}@Overridepublic void consumeContent() throws IOException {LHttpClient.silentCloseInputStream(wrappedStream);LHttpClient.silentCloseInputStream(pushbackStream);LHttpClient.silentCloseInputStream(gzippedStream);super.consumeContent();}}}





LHttpRequest.java

package com.net.lhttpclient;import java.io.IOException;import java.io.InputStream;import java.net.UnknownHostException;import org.apache.http.HttpEntity;import org.apache.http.HttpResponse;import org.apache.http.StatusLine;import org.apache.http.client.HttpRequestRetryHandler;import org.apache.http.client.HttpResponseException;import org.apache.http.client.methods.HttpUriRequest;import org.apache.http.impl.client.AbstractHttpClient;import org.apache.http.protocol.HttpContext;import org.apache.http.util.ByteArrayBuffer;public class LHttpRequest {protected static final int BUFFER_SIZE = 4096;private final AbstractHttpClient client;    private final HttpContext context;    private final HttpUriRequest request;    private int executionCount;        public LHttpRequest(AbstractHttpClient client, HttpContext context, HttpUriRequest request) {    this.client = client;        this.context = context;        this.request = request;    }        public LHttpResponse request() {    LHttpResponse lHttpResponse = new LHttpResponse();    HttpResponse httpResponse = null;    try {    httpResponse = makeRequestWithRetries();} catch (IOException e) {LogUtil.w("makeRequestWithRetries exception", e);lHttpResponse.setSuccess(false);lHttpResponse.setThrowable(e);}    if(httpResponse != null) {    byte[] responseBody;        StatusLine status = httpResponse.getStatusLine();        try {responseBody = getResponseData(httpResponse.getEntity());if (status.getStatusCode() >= 300) {lHttpResponse.setSuccess(false);lHttpResponse.setStatusCode(status.getStatusCode());lHttpResponse.setResponse(responseBody);lHttpResponse.setThrowable(new HttpResponseException(status.getStatusCode(), status.getReasonPhrase()));} else {lHttpResponse.setSuccess(true);lHttpResponse.setStatusCode(status.getStatusCode());lHttpResponse.setResponse(responseBody);}} catch (IOException e) {LogUtil.w("getResponseData exception", e);lHttpResponse.setSuccess(false);lHttpResponse.setThrowable(e);}    }        return lHttpResponse;    }        /**     * Returns byte array of response HttpEntity contents     *     * @param entity can be null     * @return response entity body or null     * @throws java.io.IOException if reading entity or creating byte array failed     */    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 > Integer.MAX_VALUE) {                    throw new IllegalArgumentException("HTTP entity too large to be buffered in memory");                }                int buffersize = (contentLength <= 0) ? BUFFER_SIZE : (int) contentLength;                try {                    ByteArrayBuffer buffer = new ByteArrayBuffer(buffersize);                    try {                        byte[] tmp = new byte[BUFFER_SIZE];                        int l, count = 0;                        // do not send messages if request has been cancelled                        while ((l = instream.read(tmp)) != -1 && !Thread.currentThread().isInterrupted()) {                            count += l;                            buffer.append(tmp, 0, l);                            // sendProgressMessage(count, (int) (contentLength <= 0 ? 1 : contentLength));                        }                    } finally {                        LHttpClient.silentCloseInputStream(instream);                        LHttpClient.endEntityViaReflection(entity);                    }                    responseBody = buffer.toByteArray();                } catch (OutOfMemoryError e) {                    System.gc();                    throw new IOException("File too large to fit into available memory");                }            }        }        return responseBody;    }        private HttpResponse makeRequest() throws IOException {    return client.execute(request, context);    }        private HttpResponse makeRequestWithRetries() throws IOException {        boolean retry = true;        IOException cause = null;        HttpRequestRetryHandler retryHandler = client.getHttpRequestRetryHandler();        try {            while (retry) {                try {                    return makeRequest();                } catch (UnknownHostException e) {                    // switching between WI-FI and mobile data networks can cause a retry which then results in an UnknownHostException                    // while the WI-FI is initialising. The retry logic will be invoked here, if this is NOT the first retry                    // (to assist in genuine cases of unknown host) which seems better than outright failure                    cause = new IOException("UnknownHostException exception: " + e.getMessage());                    retry = (executionCount > 0) && retryHandler.retryRequest(cause, ++executionCount, context);                } catch (NullPointerException e) {                    // there's a bug in HttpClient 4.0.x that on some occasions causes                    // DefaultRequestExecutor to throw an NPE, see                    // http://code.google.com/p/android/issues/detail?id=5255                    cause = new IOException("NPE in HttpClient: " + e.getMessage());                    retry = retryHandler.retryRequest(cause, ++executionCount, context);                } catch (IOException e) {                    cause = e;                    retry = retryHandler.retryRequest(cause, ++executionCount, context);                }            }        } catch (Exception e) {            // catch anything else to ensure failure message is propagated            // Log.e("AsyncHttpRequest", "Unhandled exception origin cause", e);            cause = new IOException("Unhandled exception: " + e.getMessage());        }        // cleaned up to throw IOException        throw (cause);    }}




LHttpResponse.java

package com.net.lhttpclient;public class LHttpResponse {private boolean success;private byte[] response;private int statusCode;private Throwable throwable;/** * 将byte数组转换为字符串,如果发生异常返回null * @param charsetName 比如utf-8、gbk等等 * @return 字符串,如果发生异常返回null */public String getTextResponse(String charsetName) {if(response == null) {return null;}try {return new String(response, charsetName);} catch(Exception e) {LogUtil.e("getTextResponse", e);}return null;}public boolean isSuccess() {return success;}public void setSuccess(boolean success) {this.success = success;}public byte[] getResponse() {return response;}public void setResponse(byte[] response) {this.response = response;}public int getStatusCode() {return statusCode;}public void setStatusCode(int statusCode) {this.statusCode = statusCode;}public Throwable getThrowable() {return throwable;}public void setThrowable(Throwable throwable) {this.throwable = throwable;}}




源码下载 注:这里的代码是以前的版本,最新代码已同步到github:https://github.com/liguanliang2014/LHttpClient)
0 0
原创粉丝点击