android-async-http 源码原理解析

发布于 2025-01-26 13:39:19 字数 21553 浏览 9 评论 0

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

优点:

个人觉得优点就是

  1. 该框架是基于回调的,各种回调类型的处理都有
  2. 直接放到主线程使用匿名调用即可
  3. 可以扩展自己的回调处理
  4. 支持处理重定向,文档的续传
  5. 可以保存 Cookie, 可以添加请求凭证保存,单次输入,多次使用
  6. 根据常用网络请求异常进行黑白名单查询,针对性重试机构
  7. 整个请求处理是使用 gzip 的,节省流量,速度快 用起来方便。

2. 总体设计

总体设计图

art2
图 2-1 总体设计图

以上是 android-async-http 的总体设计图。

  • 整个请求子线程 AsyncHttpRequest 处于的 参数封装是在主类 AsyncHttpClient 中进行的。 处理完成之后将该请求线程 提交至 threadPool 中由线程池调度完成。
  • 请求的处理是在子线程 AsyncHttpRequest 中处理的,其中包含了 ResponseHandlerInterface 的响应回调处理大接口, 也处理了由于各种原因造成的访问失败的智能重试。
  • 请求的响应实现 ResponseHandlerInterface 接口的 AsyncHttpResponseHandler 类,类似于 BaseAdapter 模式,基本上使用 Handler 消息处理了数据处理的各种情况。 其中 AsyncHttpResponseHandler 的各种子类负责了对应各种数据类型的解析与处理。

3. 流程图

3.1. 总体流程图

art_flow

图 3-1 总体流程图

3.2. 请求线程流程图

thread

图 3-2 请求线程流程图

3.3. makeRequestWithRetries 函数模块调用

methodreq

图 3-3 请求函数模块调用

3.4. makeRequest 函数模块调用

method2

图 3-4 请求函数模块调用

3.5 整体回调顺序

callbackseq

图 3-5 请求函数整体回调顺序

4. 详细设计

4.1 总类关系图

art
图 4-1

4.2 核心类功能介绍

4.2.1 类详细介绍(未完成)

  1. ResponseHandlerInterface 整个框架中网络访问返回数据的处理接口,集合了包括消息发送,数据处理回调在内的共 16 个函数。
  2. AsyncHttpClient.class 整个框架的调用集合类与各个参数的组装调用类。
  3. AsyncHttpRequest.class 网络访问子线程类
  4. AsyncHttpResponseHandler.class 网络响应处理主类

4.1 AsyncHttpResponseHandler 类图

responsehandler

图 4.1-1 AsyncHttpResponseHandler 类图

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

http://stackoverflow.com/questions/9818407/out-of-memory-error-in-android-due-to-heap-size-increasing

  1. RequestParams 请求参数的组装,实现 Serializable 接口。

  2. 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);
  1. 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 要准备收回该对象占用内存

  1. SyncHttpClient.class 异步网络请求类

  2. 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 中

  1. DataAsyncHttpResponseHandler 数据的异步下载处理

  2. 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 中含有的状态回调函数。

  1. BinaryHttpResponseHandler 二进制数据的下载处理

  2. 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) 的区别

  1. JsonHttpResponseHandler Json 数据的加载解析处理

  2. 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); // 需要子类来实现具体的解析方式.

  1. JsonStreamerEntity Json 流的处理,该类适合 Json base 64 编码的上传操作, 节省内存,可以适合大文件。

  2. SaxAsyncHttpResponseHandler xml 的解析处理,继承自 AsyncHttpResponseHandler 使用的 sax 方法解析。

  3. PreemptiveAuthorizationHttpRequestInterceptor 预验证拦截器类

  4. MyRedirectHandler 重定向处理器类, 源码中标识该类引用自 stackoverflow https://stackoverflow.com/questions/3420767/httpclient-redirecting-to-url-with-spaces-throwing-exception

  5. Base64 二进制数据的 Base 64 的编码与解码

  6. Base64DataException Base 64 编码的操作异常类

  7. Base64OutputStream Base 64 输出流操作类

  8. HttpDelete HttpDelete 操作类,继承自 HttpEntityEnclosingRequestBase 类

  9. HttpGet HttpGet 操作类,继承自 HttpEntityEnclosingRequestBase 类

  10. HttpPatch HttpPatch 操作类,继承自 HttpEntityEnclosingRequestBase 类

  11. JsonValueInterface 接口,可以中 App 来封装完整的封装 JSON 的值

  12. SerializableCookie 配角, 在 PersistentCookieStore 类中序列化 Cookie 的值时使用

  13. SimpleMultipartEntity 简单的多部分实体,主要用于发送一个或多个文档

  14. Utils 工具类

5. 杂谈总结

第一次大胆分析大神的源码,整个分析过程一共使用了三天 8 月 6 号就完成了,只是一直忙于工作没有上传,十分抱歉,文章中包括与设计图的制作,中间还 有一些部分不太了解,有很多不对的地方,请大家批评指正 。框架整个设计框架巧妙的使用了基于 Apache HttpClient 框架的回调。充分使用了 Httpclient 中的各种 handler , interceptor 将整个数据响应过程通过接口使其可扩展化,过程化,结构化。 而框架整体看起来又浑然一体,的确很牛气。

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

戏蝶舞

暂无简介

文章
评论
27 人气
更多

推荐作者

笑脸一如从前

文章 0 评论 0

mnbvcxz

文章 0 评论 0

真是无聊啊

文章 0 评论 0

旧城空念

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文