[Android]浅析Http框架 - Android-Async-http
2015-06-02 23:59
429 查看
现在主流的网络框架越来越多, Volley, okHttp, Android-Async-http. Android-Async-http是出现比较久的网络框架,用的人相当多. 后来Google结合HttpClient和HttpUrlConnection的优点又推出同样优秀的框架Volley.这些都是优秀的框架,值得我们去阅读源码学习. Android-Async-http我用过很长事件,但都没去看源码(鄙视下自己).直到最近有个朋友来问我这个框架内为何定义了HttpGet, 这个和系统的HttpGet有何区别? 我不能贸然回答他, 于是决定去看看源码了.该框架的类源码很多, 浏览了大部分方法名和一些关键方法的实现. 主要是学习下大神的思路.下面通过几个关键类去了解:
AsyncHttpClient类
这应该是在使用过程中接触最多的类,当然还有各种回调的handler.所以应该猜到,这应该是集成度很高的类,里面有很多方法.看构造方法,AsyncHttpClient支持SSL,但是默认的构造方法是省略对SSL认证的支持.如果要添加SSL证书,只需要在构建AsyncHttpClient的方法中
扒一扒设置Cookie的方法:new AsyncHttpClient(fixNoHttpResponseException, httpPort, httpsPort)[code]将参数fixNoHttpResponseException设为true即可支持返回信任的安全套接字SSLSocketFactory.
/** * 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); }
接着看构建AsyncHttpClient的所有构建方法,最终都会调用该方法(代码没贴全,有兴趣可以去看源码):
在这个方法中, 所有参数一目了然, 请求超时时间, 连接超时时间,最大连接数,header的添加等等参数都在这里得到设置处理,但很快就会发现一个重要的对象,threadPool. 这是获取的默认的线程池,为什么重要?快速浏览该类,发现一些蛛丝马迹:/** * 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); //... }
AsyncHttpRequest request = newAsyncHttpRequest(client, httpContext, uriRequest, contentType, responseHandler, context); threadPool.submit(request);
看到上面的代码, 不禁要联想翩翩.没错,这的确是提交HTTP请求的实现.其实看到这里,我们大概猜到,框架中使用线程池去管理发送网络请求的所有线程.responseHandler是实现了封装接口,并且有回调方法的对象,比如我们在源码中所看到的BinaryHttpResponseHandler,DataAsyncHttpResponseHandler,JsonHttpResponseHandler等各种负责精细处理的handler都继承于AsyncHttpResponseHandler这个抽象类.抽象类我们都知道主要是规范子类,比如定义一些接口方法什么的.我们常用的onSuccess(...),onFailure(Throwable e),onFinish(),onRetry()等方法都在这里被定义,这些方法也理应不被实现,因为只是充当被回调的角色,等待HTTP请求后才被回调,而且回调想要处理的结果也不一样,有的是json,有的是binary,有的是String... 它们也应当在子类中被各自实现,得到不同的作用. 再来看,AsyncHttpResponseHandler的确实现ResponseHandlerInterface接口,也就是上面代码中所传进来的responseHandler.这里我们就明白,为何get,post等方法,我们要传各种handler(AsyncHttpResponseHandler的子类)进来.
这个类的亮点是蛮多的,等待大家发现.这里紧跟上面说到线程池,必须提及一个优秀的设计,在封装的sendRequest()方法中,作者使用了Map<Context, List<RequestHandle>> requestMap去缓存请求数,并不是来多少请求就抛多少进去线程池,这样肯定效率极低.而是将这些请求队列放入Map中,这样如果要取消某些请求,管理起来就非常方便.
相关代码比较分散,下面是缓存请求的部分代码:
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) { List<RequestHandle> requestList; // Add request to request map synchronized (requestMap) { requestList = requestMap.get(context); 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(); } } }取消请求的代码直接看该类的cancelRequests(...),cancelAllRequests(...)相关方法就好.
AsyncHttpRequest类
上面的代码中还有一个AsyncHttpRequest类,它是被提交到线程中,所以应该是一个实现Runnable接口的类.其中newAsyncHttpRequest(...)这个方法的参数中,可见框架实现网络请求的方式的确是使用了系统的DefaultHttpClient.
既然是实现Runnable接口,重点应该放在run方法中:protected AsyncHttpRequest newAsyncHttpRequest(DefaultHttpClient client, HttpContext httpContext, HttpUriRequest uriRequest, String contentType, ResponseHandlerInterface responseHandler, Context context) { return new AsyncHttpRequest(client, httpContext, uriRequest, responseHandler); }[code]
@Override public void run() { if (isCancelled()) { return; } // Carry out pre-processing for this request only once. if (!isRequestPreProcessed) { isRequestPreProcessed = true; onPreProcessRequest(this); } if (isCancelled()) { return; } responseHandler.sendStartMessage(); if (isCancelled()) { return; } try { makeRequestWithRetries(); } catch (IOException e) { if (!isCancelled()) { responseHandler.sendFailureMessage(0, null, null, e); } else { Log.e("AsyncHttpRequest", "makeRequestWithRetries returned error", e); } } if (isCancelled()) { return; } responseHandler.sendFinishMessage(); if (isCancelled()) { return; } // Carry out post-processing for this request. onPostProcessRequest(this); isFinished = true; }思路大概是,首先检查这个request是否被取消,如果被取消则不发送请求直接返回,否则就用responseHandler去发请求了.由于ResponseHandlerInterface接口的方法在各个handler中已经实现,所以这里直接调用即可.代码看起来相当简洁!同时也看到run方法中好几个地方都去isCanCelled()检查请求是否取消.能比较有效管理请求.并不是说,只在开始run的时候检查一次是否取消后面就不再检查.因为用户可是随时都有可能取消操作的.
同时通过接收异常,方法中还有针对异常处理,和重新发请求的机制.代码简洁有力,逻辑清晰,再次赞叹大神的能力.
由于大家经常用该框架的接口方法,其实到了这里,我们对Android-Async-http框架已有初步的印象.知道比如访问超时,连接超时等这些参数在哪里被设置,网络请求如何被发送和被取消,取消和重发的机制又是怎样的等等. 下面我们看看Cookie如何被保存.PersistentCookieStore类框架中已经针对Cookie做了处理, 封装成PersistentCookieStore对象, 会保存在SharePreference中.进入的构建方法,会发现是这么实现的:
/** * Construct a persistent cookie store. * * @param context Context to attach cookie store to */ public PersistentCookieStore(Context context) { cookiePrefs = context.getSharedPreferences(COOKIE_PREFS, 0); cookies = new ConcurrentHashMap<String, Cookie>(); // Load any previously stored cookies into the store String storedCookieNames = cookiePrefs.getString(COOKIE_NAME_STORE, null); if (storedCookieNames != null) { String[] cookieNames = TextUtils.split(storedCookieNames, ","); for (String name : cookieNames) { String encodedCookie = cookiePrefs.getString(COOKIE_NAME_PREFIX + name, null); if (encodedCookie != null) { Cookie decodedCookie = decodeCookie(encodedCookie); if (decodedCookie != null) { cookies.put(name, decodedCookie); } } } // Clear out expired cookies clearExpired(new Date()); } }
cookiePrefs就是SharePreference对象.代码中的cookies是存放cookie的Map对象.PersistentCookieStore类已经便捷的提供了clear() , deleteCookie(Cookie cookie), List<Cookie> getCookies(), addCookie(Cookie cookie)等方法.直接new PersistentCookieStore(Context context) 就能获取存储的Cookie, 并自动清除过期的cookies, 然后set入AsyncHttpClient中. 当然在应用的使用过程中, 无论是AsyncHttpClient还是系统自带HttpClient., 它们就像电脑上面的浏览器, 只要保持一直用同一个对象去发送诸如GET, POST的这些请求, 对象是会自动保持cookie的, 所以适用过程中并不需要担心cookie的问题. 本地化存储的好处, 是在下次打开应用时还能使用之前的操作记录.
借鉴这种思路, 我们在使用系统的DefaultHttpClient时,也能对其子类使用相同的处理方法去本地化保存Cookie了.BinaryHttpResponseHandler类然后来看其中一个Handler类.依然是抽象类, onSuccess(...), onFailure(...)等这些方法依然是抽象方法. 知道多态继承和抽象的童鞋, 应该都知道为啥这么干了, 因为这些方法基本是在回调的时候才去实现这些接口方法的. 为了降藕,提高功能模块的灵活性和可扩展, 对外只提供接口, 不提供实现, 这是优秀的架构设计.通过简单的BinaryHttpResponseHandler类来了解其他Handler类的设计思路,Handler类必须结合AsyncHttpRequest和AsyncHttpResponseHandler这2个类去理解.在AsyncHttpRequest的run方法中,sendStartMessage()发出请求, 本质还是通过HttpClient的excute()方法去实现. 得到的返回结果又使用sendResponseMessage(response)去解析.这些方法要去AsyncHttpResponseHandler和它的子类BinaryHttpResponseHandler去看.如果应用中是通过BinaryHttpResponseHandler发请求,那么sendResponseMessage(response)方法会首先回调BinaryHttpResponseHandler中的sendResponseMessage(response)去解析, 当然有些子类比如JsonHttpResPonseHandler是没有sendResponseMessage(...)方法的, 那么就直接交给AsyncHttpResponseHandler的该方法去处理. 就像BinaryHttpResponseHandler类, 在sendResponseMessage(...)中处理完结果还是会super.sendResponseMessage(...)调用父类的方法去继续处理, 因为onSuccess(...), onFailure(...)等的这些方法的回调机制都在父类AsyncHttpResponseHandler中实现. 这里我们再次看到这种设计的思路, 子类尽可能的去实现去解析,做一些具体的处理, 而将一些相同的可封装的方法都封装在父类中.回到AsyncHttpResponseHandler的sendResponseMessage(...)方法:
Override public void sendResponseMessage(HttpResponse response) throws IOException { // do not process if request has been cancelled if (!Thread.currentThread().isInterrupted()) { StatusLine status = response.getStatusLine(); byte[] responseBody; responseBody = getResponseData(response.getEntity()); // additional cancellation check as getResponseData() can take non-zero time to process if (!Thread.currentThread().isInterrupted()) { if (status.getStatusCode() >= 300) { sendFailureMessage(status.getStatusCode(), response.getAllHeaders(), responseBody, new HttpResponseException(status.getStatusCode(), status.getReasonPhrase())); } else { sendSuccessMessage(status.getStatusCode(), response.getAllHeaders(), responseBody); } } } }通过查看sendFailureMessage(...)和sendSuccessMessage(...)的方法代码, 看到里面利用Handler对象去进行通信.一直追踪, 终于发现这段代码:
protected void handleMessage(Message message) { Object[] response; try { switch (message.what) { case SUCCESS_MESSAGE: response = (Object[]) message.obj; if (response != null && response.length >= 3) { onSuccess((Integer) response[0], (Header[]) response[1], (byte[]) response[2]); } else { Log.e(LOG_TAG, "SUCCESS_MESSAGE didn't got enough params"); } break; case FAILURE_MESSAGE: response = (Object[]) message.obj; if (response != null && response.length >= 4) { onFailure((Integer) response[0], (Header[]) response[1], (byte[]) response[2], (Throwable) response[3]); } else { Log.e(LOG_TAG, "FAILURE_MESSAGE didn't got enough params"); } break; case START_MESSAGE: onStart(); break; case FINISH_MESSAGE: onFinish(); break; case PROGRESS_MESSAGE: response = (Object[]) message.obj; if (response != null && response.length >= 2) { try { onProgress((Long) response[0], (Long) response[1]); } catch (Throwable t) { Log.e(LOG_TAG, "custom onProgress contains an error", t); } } else { Log.e(LOG_TAG, "PROGRESS_MESSAGE didn't got enough params"); } break; case RETRY_MESSAGE: response = (Object[]) message.obj; if (response != null && response.length == 1) { onRetry((Integer) response[0]); } else { Log.e(LOG_TAG, "RETRY_MESSAGE didn't get enough params"); } break; case CANCEL_MESSAGE: onCancel(); break; } } catch(Throwable error) { onUserException(error); } }到了这里, 我们对一个AsyncHttpResponseHandler的子类所必须实现的onSuccess(...), onFailure(...), onFinish(...)等这些方法是如何被回调的, 以及框架中从请求发起到取消, 或到返回的结果如何解析, 如何判断和处理异常, 有比较清晰的认识. 而其他的AsyncHttpResponseHandler子类都跟BinaryHttpResponseHandler类似.原理是一样的. 对待一个框架, 一开始不建议逐个类盲目去看, 特别当框架中类也多,代码也多, 简直无从入手. 如果还逐行代码去看必然花费大量时间.优秀的框架, 优秀的方法代码, 必然在设计上是思路清晰的, 并且有关键的注释帮助理解. 这样不但方便以后自己持续的维护项目代码, 也帮助后来进入的开发者能快速的理解代码进入项目的设计中. 另一方面, 我们以后去设计功能模块, 敲代码, 关键的地方也请写上必要的注释, 在设计尽量上将功能模块之间处理的合理, 比如提高可复用, 降藕等等.对HTTP框架Android-Async-http的浅析就到此了, 因为是浅析, 重在理解主要的实现思路, 很多点并没有展开讲, 有兴趣的童鞋可以自行下载源码去看哦.Android-Async-http源码下载
相关文章推荐
- http服务(一)――apache工作模式
- HTTP协议详解
- 阿里欲瓜分网络文学蛋糕还来得及吗?
- Centos 6.6 x86_64 Basic Server 快速安装httpd
- 所谓的HTTP请求,到底都干了什么
- tcp 粘包
- http协议详解
- 获取xmlhttpQequest对象
- Android学习 - 网络编程的理解
- linux网络编程之二TCP套接口编程
- Java TCP网络编程 简单实例
- SQL数据库— <3>高级查询、常用函数 --摘录网络
- android5.0网络之策略路由
- java.lang.ClassNotFoundException: org.apache.http.util.Args
- nginx强制使用https访问(http跳转到https)
- 第8章双路由双网段网络互通实验(初级篇)
- linux网络编程学习之一网络字节序
- HttpClient基础
- Java UDP网络编程 简单实例
- Android Volley完全解析(二),使用Volley加载网络图片