1. 功能介绍
功能介绍:
android-async-http 是一个基于回调的并基于Apache's httpClient libraries 为Android 开发的一个框架。 所有的请求在非UI线程中处理,而回调是在Handler里面处理。你也可以在Service或者后台线程使用, lib 会自动识别 并在context里处理。 具体使用方法可见 http://loopj.com/android-async-http/
概念
Http请求 (AsyncHttpRequest):又可以成为Http请求的单线程对象,通过将该单线程模型submit至 线程池中统一管理。
Http响应处理者(HttpResponseHandler): 可以针对性的生成不同数据类型的相应处理者,包含Json, String, binary, file ...的处理操作。
重试处理者(RetryHandler):可以针对不同的异常情况根据自身预设定的white&black exception List 进行智能筛选,选择需要重试的请求。
拦截器(Interceptor): 在该框架中的应用类似于Spring中的 aop(Aspect Oriented Programming), 多次设定于Httpclient属性中。
特点:
异步发送HTTP请求,在回调函数中处理响应
HTTP请求过程不在UI线程进行
使用线程池来管理并发数
支持GET/POST请求参数单独设置
无需其他库上传序列化JSON数据
处理重定向
体积小,只有90K
针对不同的网络连接对重试次数进行智能优化
支持gzip
二进制通信协议使用BinaryHttpResponseHandler处理
内置Json解析,使用JsonHttpResponseHandler对响应进行处理
使用FileAsyncHttpResponseHandler直接将响应保存到文件中
动态保存Cookie,将Cookie保存到应用的SharedPreferences中
使用BaseJsonHttpResponseHandler可以搭配Jackson JSON,Gson或者其他的Json反序列化库
支持SAX解析,使用SaxAsyncHttpResponseHandler
支持多语言多种编码方式,不只是UTF-8
优点:
个人觉得优点就是
该框架是基于回调的,各种回调类型的处理都有
直接放到主线程使用匿名调用即可
可以扩展自己的回调处理
支持处理重定向,文档的续传
可以保存Cookie, 可以添加请求凭证保存,单次输入,多次使用
根据常用网络请求异常进行黑白名单查询,针对性重试机构
整个请求处理是使用gzip的,节省流量,速度快 用起来方便。
2. 总体设计
总体设计图
以上是android-async-http 的总体设计图。
整个请求子线程 AsyncHttpRequest处于的 参数封装是在主类AsyncHttpClient 中进行的。 处理完成之后将该请求线程 提交至 threadPool 中由线程池调度完成。
请求的处理是在子线程AsyncHttpRequest中处理的,其中包含了ResponseHandlerInterface 的响应回调处理大接口, 也处理了由于各种原因造成的访问失败的智能重试。
请求的响应实现ResponseHandlerInterface接口的AsyncHttpResponseHandler类,类似于BaseAdapter模式,基本上使用Handler消息处理了数据处理的各种情况。 其中AsyncHttpResponseHandler的各种子类负责了对应各种数据类型的解析与处理。
3. 流程图
3.1. 总体流程图
3.2. 请求线程流程图
3.3. makeRequestWithRetries函数模块调用
3.4. makeRequest函数模块调用
3.5 整体回调顺序
4. 详细设计
4.1 核心类功能介绍
4.2.1 类详细介绍(未完成)
ResponseHandlerInterface 整个框架中网络访问返回数据的处理接口,集合了包括消息发送,数据处理回调在内的共16个函数。
AsyncHttpClient.class 整个框架的调用集合类与各个参数的组装调用类。
AsyncHttpRequest.class 网络访问子线程类
AsyncHttpResponseHandler.class 网络响应处理主类
4.2 成员变量
private Handler handler; /// 内部类 ResponderHandler 的赋值对象, 负责发送,处理消息。 private boolean useSynchronousMode; private boolean usePoolThread; private URI requestURI = null; private Header[] requestHeaders = null; /// 请求头信息 private Looper looper = null; /// 默认如果looper为null,则赋值为Looper.myLooper() 也就是获取的主线程中的Looper ;
4.3 重要函数
// Methods which emulate android's Handler and Message methods 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); } }
分析:
接收处理 请求访问以及返回过程中的所有状态, 包含 SUCCESS_MESSAGE, FAILURE_MESSAGE, START_MESSAGE, FINISH_MESSAGE, PROGRESS_MESSAGE,RETRY_MESSAGE, CANCEL_MESSAGE 在内的各种情况,以及回调。
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]; long count = 0; int l; // 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, (contentLength <= 0 ? 1 : contentLength)); } } finally { AsyncHttpClient.silentCloseInputStream(instream); AsyncHttpClient.endEntityViaReflection(entity); } responseBody = buffer.toByteArray(); } catch (OutOfMemoryError e) { System.gc(); throw new IOException("File too large to fit into available memory"); } } } return responseBody; }
分析:
sendMessage中包含了 所有 的发送消息通用函数, 调用handler的消息发送函数,send Or post 至ResponderHandler 内部, ResponderHandler内部调用AsyncHttpResponseHandler 内部 handleMessage 函数,来处理各种情况。
protected void sendMessage(Message msg) ; protected void postRunnable(Runnable runnable);
接收返回HttpEntity 实体内部 content , 并按照BufferSize来读取数据, append至 ByteArrayBuffer 内部, 同时发布进度状态sendProgressMessage ,回调onProgress 函数。 最后返回 ByteArrayBuffer 的byte[] .
NOTE: 其中ByteArrayBuffer 属于自增长型的缓冲数据类型, code中 的默认大小为4096也就是 1KB大小 , 而ByteArrayBuffer 过大也会导致 OutOfMemoryError , 超过 VM heapsize 的分配内存之后就会报内存溢出异常。
if (contentLength > Integer.MAX_VALUE) { throw new IllegalArgumentException("HTTP entity too large to be buffered in memory"); }
后边 Int 的数据类型范围: -2147483648~2147483647 最大值 2147483647 = 2^10 也就是 1G , 上边code写的, 也就是最大不能超1GB , 这样的话但是后边的强转之后直接将int设置到ByteArrayBuffer中, 显然作者是将这个问题交给了系统自动去检测了。
Runtime rt = Runtime.getRuntime(); long maxMemory = rt.maxMemory(); Log.v("onCreate", "maxMemory:" + Long.toString(maxMemory));
个人认为,由于每一种机型的型号不同,系统分配的 HeapSize也不同, 可以根据这个来进行匹配,检测。 链接见:http://stackoverflow.com/questions/2630158/detect-application-heap-size-in-android/9428660#9428660
RequestParams 请求参数的组装,实现Serializable 接口。
RetryHandler.class 重试处理类
6.1 成员变量
黑白名单列表:
HashSet<Class<?>> exceptionWhitelist = new HashSet<Class<?>>(); HashSet<Class<?>> exceptionBlacklist = new HashSet<Class<?>>();
默认黑白名单处理策略:
static { // Retry if the server dropped connection on us exceptionWhitelist.add(NoHttpResponseException.class); // retry-this, since it may happens as part of a Wi-Fi to 3G failover exceptionWhitelist.add(UnknownHostException.class); // retry-this, since it may happens as part of a Wi-Fi to 3G failover exceptionWhitelist.add(SocketException.class); // never retry timeouts exceptionBlacklist.add(InterruptedIOException.class); // never retry SSL handshake failures exceptionBlacklist.add(SSLException.class); }
6.2 重要函数
public boolean retryRequest(IOException exception, int executionCount, HttpContext context){ boolean retry = true; Boolean b = (Boolean) context.getAttribute(ExecutionContext.HTTP_REQ_SENT); boolean sent = (b != null && b); if (executionCount > maxRetries) { // Do not retry if over max retry count retry = false; } else if (isInList(exceptionWhitelist, exception)) { // immediately retry if error is whitelisted retry = true; } else if (isInList(exceptionBlacklist, exception)) { // immediately cancel retry if the error is blacklisted retry = false; } else if (!sent) { // for most other errors, retry only if request hasn't been fully sent yet retry = true; } if (retry) { // resend all idempotent requests HttpUriRequest currentReq = (HttpUriRequest) context.getAttribute(ExecutionContext.HTTP_REQUEST); if (currentReq == null) { return false; } } if (retry) { SystemClock.sleep(retrySleepTimeMS); } else { exception.printStackTrace(); } return retry; }
分析: 整体异常处理策略就是,根据参数IOException 的类型,在对应的黑白名单列表中进行轮询操作,如果发现匹配项目,则进行相应的操作, 例如: 如果异常出现在白名单列表中,也就是在网络访问中常发生的,不可避免的,由用户网络环境造成的异常, 则返回可以重试标识。反之,则结束重试。
6.3 其他函数
添加黑白名单的方法,轮询方法等。
addClassToWhitelist(Class<?> cls); addClassToBlacklist(Class<?> cls); boolean isInList(HashSet<Class<?>> list, Throwable error);
RequestHandle 请求句柄类,例如 请求任务取消,是否完成,垃圾回收操作, 也挺重要的。
7.1成员变量
这个类貌似只有这么一个成员变量 就是 AsyncHttpRequest 的弱引用, 看来作者对于内存溢出考虑也挺周全。
private final WeakReference<AsyncHttpRequest> request;
7.2重要函数
public boolean cancel(final boolean mayInterruptIfRunning) { final AsyncHttpRequest _request = request.get(); if (_request != null) { if (Looper.myLooper() == Looper.getMainLooper()) { new Thread(new Runnable() { @Override public void run() { _request.cancel(mayInterruptIfRunning); } }).start(); } else { _request.cancel(mayInterruptIfRunning); } } return false; }
分析:
取消请求,直接将AsyncHttpRequest 内部的iscancel 置为false, 调用HttpUriRequest的abort 函数, 中断请求,发送任务取消Notification。
NOTE : 这里我发现 mayInterruptIfRunning参数木有用到,我找了半天没有找到。
7.3 其他函数
public boolean isFinished() ; // 判断是否请求结束。 public boolean isCancelled() ; // 判断任务是否已经取消过了。 public boolean shouldBeGarbageCollected() // 这货好像是这个版本才出现的, 作用是 告诉 gc要准备收回该对象占用内存
SyncHttpClient.class 异步网络请求类
PersistentCookieStore 本地的cookie的存储类
9.1 成员变量
private final ConcurrentHashMap<String, Cookie> cookies; /// 运行时维护的一个cookie的Map private final SharedPreferences cookiePrefs; /// cookie 保存在了sharedPreference中
9.2 成员函数
public void addCookie(Cookie cookie) ;/// 添加cookie public void clear() ; /// 清除cookie public boolean clearExpired(Date date); // 清除过期的cookie public void deleteCookie(Cookie cookie); // 删除指定名称的cookie 从sharedPreference protected Cookie decodeCookie(String cookieString); // 将cookie以16进制编码的方式通过 SerializableCookie readObject(ObjectInputStream in)函数添加至Cookie中,返回Cookie protected String encodeCookie(SerializableCookie cookie); // 将Cookie对象序列化至String中
DataAsyncHttpResponseHandler 数据的异步下载处理
FileAsyncHttpResponseHandler 文件的异步下载处理
14.1成员变量
protected final File mFile; /// 临时文档的操作对象。 protected final boolean append; /// 文档操作对象是否在上一次写的基础上追加数据。
14.2重要函数
@Override protected byte[] getResponseData(HttpEntity entity) throws IOException { if (entity != null) { InputStream instream = entity.getContent(); long contentLength = entity.getContentLength(); FileOutputStream buffer = new FileOutputStream(getTargetFile(), this.append); if (instream != null) { 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.write(tmp, 0, l); sendProgressMessage(count, contentLength); } } finally { AsyncHttpClient.silentCloseInputStream(instream); buffer.flush(); AsyncHttpClient.silentCloseOutputStream(buffer); } } } return null; }
分析: 该方法是处理接收的HttpEntiry 的Content 内容,使用流写至一个临时的file, 同时使用父类AsyncHttpResponseHandler的Handler 发送进度, 最后关闭流。
14.3 其他函数
public boolean deleteTargetFile() ;// 删除目标文件; protected File getTemporaryFile(Context context);// 获取临时创建的文件;
以及其他父类 AsyncHttpResponseHandler中含有的状态回调函数。
BinaryHttpResponseHandler 二进制数据的下载处理
TextHttpResponseHandler 文本数据的加载处理
16.1 成员变量
private String responseCharset = DEFAULT_CHARSET;/// 相应数据编码,默认UTF-8
16.2 成员函数
public static String getResponseString(byte[] stringBytes, String charset) { try { String toReturn = (stringBytes == null) ? null : new String(stringBytes, charset); if (toReturn != null && toReturn.startsWith(UTF8_BOM)) { return toReturn.substring(1); } return toReturn; } catch (UnsupportedEncodingException e) { Log.e(LOG_TAG, "Encoding response into string failed", e); return null; } }
分析: 该方法是处理接收byte[] , 通过使用父类AsyncHttpResponseHandler的Handler 中onSuccess方法回传的byte[] ,添加指定的charset(默认编码UTF-8),返回从第一个字符开始截取的string对象。
NOTE:中间有个常量 UTF8_BOM,内容是 发现BOM的作用是识别UTF-8编码的作用,具体请参考链接:utf-8与utf-8(无BOM)的区别
JsonHttpResponseHandler Json数据的加载解析处理
BaseJsonHttpResponseHandler 基础Json数据的解析,继承自TextHttpResponseHandler
18.1 成员变量 无
18.2 重要函数
public final void onSuccess(final int statusCode, final Header[] headers, final String responseString) { if (statusCode != HttpStatus.SC_NO_CONTENT) { Runnable parser = new Runnable() { @Override public void run() { try { final JSON_TYPE jsonResponse = parseResponse(responseString, false); postRunnable(new Runnable() { @Override public void run() { onSuccess(statusCode, headers, responseString, jsonResponse); } }); } catch (final Throwable t) { Log.d(LOG_TAG, "parseResponse thrown an problem", t); postRunnable(new Runnable() { @Override public void run() { onFailure(statusCode, headers, t, responseString, null); } }); } } }; if (!getUseSynchronousMode() && !getUsePoolThread()) { new Thread(parser).start(); } else { // In synchronous mode everything should be run on one thread parser.run(); } } else { onSuccess(statusCode, headers, null, null); } }
分析:
使用TextHttpResponseHandler 中onSuccess的Text返回方法, 在返回String内容不为null的情况下, 直接解析responseString, 并调用抽象函数parseResponse ,解析的过程在子线程中运行,这取决于用户的是操作方式,是异步还是同步.
18.3 其他函数
protected abstract JSON_TYPE parseResponse(String rawJsonData, boolean isFailure); // 需要子类来实现具体的解析方式.
JsonStreamerEntity Json流的处理,该类适合Json base 64 编码的上传操作, 节省内存,可以适合大文件。
SaxAsyncHttpResponseHandler xml 的解析处理,继承自AsyncHttpResponseHandler 使用的sax方法解析。
PreemptiveAuthorizationHttpRequestInterceptor 预验证拦截器类
MyRedirectHandler 重定向处理器类, 源码中标识该类引用自stackoverflowhttps://stackoverflow.com/questions/3420767/httpclient-redirecting-to-url-with-spaces-throwing-exception
Base64 二进制数据的 Base 64 的编码与解码
Base64DataException Base 64 编码的操作异常类
Base64OutputStream Base 64 输出流操作类
HttpDelete HttpDelete 操作类,继承自HttpEntityEnclosingRequestBase类
HttpGet HttpGet 操作类,继承自HttpEntityEnclosingRequestBase类
HttpPatch HttpPatch 操作类,继承自HttpEntityEnclosingRequestBase类
JsonValueInterface 接口,可以中App来封装完整的封装JSON的值
SerializableCookie 配角, 在PersistentCookieStore类中序列化Cookie的值时使用
SimpleMultipartEntity 简单的多部分实体,主要用于发送一个或多个文档
Utils 工具类
5. 杂谈总结
第一次大胆分析大神的源码,整个分析过程一共使用了三天8月6号就完成了,只是一直忙于工作没有上传,十分抱歉,文章中包括与设计图的制作,中间还有一些部分不太了解,有很多不对的地方,请大家批评指正 。框架整个设计框架巧妙的使用了基于Apache HttpClient框架的回调。充分使用了Httpclient 中的各种 handler , interceptor 将整个数据响应过程通过接口使其可扩展化,过程化,结构化。 而框架整体看起来又浑然一体,的确很牛气。
共同学习,写下你的评论
评论加载中...
作者其他优质文章