现在主流的网络框架越来越多, Volley, okHttp, Android-Async-http. Android-Async-http是出现比较久的网络框架,用的人相当多. 后来Google结合HttpClient和HttpUrlConnection的优点又推出同样优秀的框架Volley.这些都是优秀的框架,值得我们去阅读源码学习. Android-Async-http我用过很长事件,但都没去看源码(鄙视下自己).直到最近有个朋友来问我这个框架内为何定义了HttpGet, 这个和系统的HttpGet有何区别? 我不能贸然回答他, 于是决定去看看源码了.
该框架的类源码很多, 浏览了大部分方法名和一些关键方法的实现. 主要是学习下大神的思路.下面通过几个关键类去了解:
AsyncHttpClient类
这应该是在使用过程中接触最多的类,当然还有各种回调的handler.所以应该猜到,这应该是集成度很高的类,里面有很多方法.看构造方法,AsyncHttpClient支持SSL,但是默认的构造方法是省略对SSL认证的支持.如果要添加SSL证书,只需要在构建AsyncHttpClient的方法中
- new AsyncHttpClient(fixNoHttpResponseException, httpPort, httpsPort)
将参数fixNoHttpResponseException设为true即可支持返回信任的安全套接字SSLSocketFactory.
扒一扒设置Cookie的方法:
-
-
-
-
-
-
- public void setCookieStore(CookieStore cookieStore) {
- httpContext.setAttribute(ClientContext.COOKIE_STORE, cookieStore);
- }
接着看构建AsyncHttpClient的所有构建方法,最终都会调用该方法(代码没贴全,有兴趣可以去看源码):
-
-
-
-
-
- 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);
-
- }
在这个方法中, 所有参数一目了然, 请求超时时间, 连接超时时间,最大连接数,header的添加等等参数都在这里得到设置处理,但很快就会发现一个重要的对象,threadPool. 这是获取的默认的线程池,为什么重要?快速浏览该类,发现一些蛛丝马迹:
- 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;
-
- 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.
- protected AsyncHttpRequest newAsyncHttpRequest(DefaultHttpClient client, HttpContext httpContext, HttpUriRequest uriRequest, String contentType, ResponseHandlerInterface responseHandler, Context context) {
- return new AsyncHttpRequest(client, httpContext, uriRequest, responseHandler);
- }
既然是实现Runnable接口,重点应该放在run方法中:
- @Override
- public void run() {
- if (isCancelled()) {
- return;
- }
-
-
- 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;
- }
-
-
- onPostProcessRequest(this);
-
- isFinished = true;
- }
思路大概是,首先检查这个request是否被取消,如果被取消则不发送请求直接返回,否则就用responseHandler去发请求了.由于ResponseHandlerInterface接口的方法在各个handler中已经实现,所以这里直接调用即可.代码看起来相当简洁!同时也看到run方法中好几个地方都去isCanCelled()检查请求是否取消.能比较有效管理请求.并不是说,只在开始run的时候检查一次是否取消后面就不再检查.因为用户可是随时都有可能取消操作的.
同时通过接收异常,方法中还有针对异常处理,和重新发请求的机制.代码简洁有力,逻辑清晰,再次赞叹大神的能力.
由于大家经常用该框架的接口方法,其实到了这里,我们对Android-Async-http框架已有初步的印象.知道比如访问超时,连接超时等这些参数在哪里被设置,网络请求如何被发送和被取消,取消和重发的机制又是怎样的等等. 下面我们看看Cookie如何被保存.
PersistentCookieStore类
框架中已经针对Cookie做了处理, 封装成PersistentCookieStore对象, 会保存在SharePreference中.进入的构建方法,会发现是这么实现的:
-
-
-
-
-
- public PersistentCookieStore(Context context) {
- cookiePrefs = context.getSharedPreferences(COOKIE_PREFS, 0);
- cookies = new ConcurrentHashMap<String, Cookie>();
-
-
- 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);
- }
- }
- }
-
-
- 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 {
-
- if (!Thread.currentThread().isInterrupted()) {
- StatusLine status = response.getStatusLine();
- byte[] responseBody;
- responseBody = getResponseData(response.getEntity());
-
- 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类似.原理是一样的.
对待一个框架, 一开始不建议逐个类盲目去看, 特别当框架中类也多,代码也多, 简直无从入手. 如果还逐行代码去看必然花费大量时间.优秀的框架, 优秀的方法代码, 必然在设计上是思路清晰的, 并且有关键的注释帮助理解. 这样不但方便以后自己持续的维护项目代码, 也帮助后来进入的开发者能快速的理解代码进入项目的设计中. 另一方面, 我们以后去设计功能模块, 敲代码, 关键的地方也请写上必要的注释, 在设计尽量上将功能模块之间处理的合理, 比如提高可复用, 降藕等等.