android Xutils Http模块分析

XUtils下载地址 http://www.oschina.net/p/xutils

官方介绍

  • xUtils 包含了很多实用的android工具.
  • xUtils 支持超大文件(超过2G)上传,更全面的http请求协议支持(11种谓词),拥有更加灵活的ORM,更多的事件注解支持且不受混淆影响...
  • xUtils 最低兼容Android 4.0 (api level 14). (Android 2.3?)
  • xUtils3变化较多所以建立了新的项目不在旧版(github.com/wyouflf/xUtils)上继续维护, 相对于旧版本:
    1. HTTP实现替换HttpClient为UrlConnection, 自动解析回调泛型, 更安全的断点续传策略.
    2. 支持标准的Cookie策略, 区分domain, path...
    3. 事件注解去除不常用的功能, 提高性能.
    4. 数据库api简化提高性能, 达到和greenDao一致的性能.
    5. 图片绑定支持gif(受系统兼容性影响, 部分gif文件只能静态显示), webp; 支持圆角, 圆形, 方形等裁剪, 支持自动旋转...

常见问题:

  1. 更好的管理图片缓存: https://github.com/wyouflf/xUtils3/issues/149
  2. Cookie的使用: https://github.com/wyouflf/xUtils3/issues/125
  3. 关于query参数? http请求可以通过 header, url, body(请求体)传参; query参数是url中问号(?)后面的参数.
  4. 关于body参数? body参数只有PUT, POST, PATCH, DELETE(老版本RFC2616文档没有明确指出它是否支持, 所以暂时支持)请求支持.



以下是对demo的分析

进入HttpFragment

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. /** 
  2.         * 自定义实体参数类请参考: 
  3.         * 请求注解 {@link org.xutils.http.annotation.HttpRequest} 
  4.         * 请求注解处理模板接口 {@link org.xutils.http.app.ParamsBuilder} 
  5.         * 
  6.         * 需要自定义类型作为callback的泛型时, 参考: 
  7.         * 响应注解 {@link org.xutils.http.annotation.HttpResponse} 
  8.         * 响应注解处理模板接口 {@link org.xutils.http.app.ResponseParser} 
  9.         * 
  10.         * 示例: 查看 org.xutils.sample.http 包里的代码 
  11.         */  
  12.        BaiduParams params = new BaiduParams();  
  13.        params.wd = "xUtils";  
  14.        // 有上传文件时使用multipart表单, 否则上传原始文件流.  
  15.        // params.setMultipart(true);  
  16.        // 上传文件方式 1  
  17.        // params.uploadFile = new File("/sdcard/test.txt");  
  18.        // 上传文件方式 2  
  19.        // params.addBodyParameter("uploadFile", new File("/sdcard/test.txt"));  
  20.        Callback.Cancelable cancelable  
  21.                = x.http().get(params,  
  22.                /** 
  23.                 * 1. callback的泛型: 
  24.                 * callback参数默认支持的泛型类型参见{@link org.xutils.http.loader.LoaderFactory}, 
  25.                 * 例如: 指定泛型为File则可实现文件下载, 使用params.setSaveFilePath(path)指定文件保存的全路径. 
  26.                 * 默认支持断点续传(采用了文件锁和尾端校验续传文件的一致性). 
  27.                 * 其他常用类型可以自己在LoaderFactory中注册, 
  28.                 * 也可以使用{@link org.xutils.http.annotation.HttpResponse} 
  29.                 * 将注解HttpResponse加到自定义返回值类型上, 实现自定义ResponseParser接口来统一转换. 
  30.                 * 如果返回值是json形式, 那么利用第三方的json工具将十分容易定义自己的ResponseParser. 
  31.                 * 如示例代码{@link org.xutils.sample.http.BaiduResponse}, 可直接使用BaiduResponse作为 
  32.                 * callback的泛型. 
  33.                 * 
  34.                 * @HttpResponse 注解 和 ResponseParser接口仅适合做json, xml等文本类型数据的解析, 
  35.                 * 如果需要其他数据类型的解析可参考: 
  36.                 * {@link org.xutils.http.loader.LoaderFactory} 
  37.                 * 和 {@link org.xutils.common.Callback.PrepareCallback}. 
  38.                 * LoaderFactory提供PrepareCallback第一个泛型参数类型的自动转换, 
  39.                 * 第二个泛型参数需要在prepare方法中实现. 
  40.                 * (LoaderFactory中已经默认提供了部分常用类型的转换实现, 其他类型需要自己注册.) 
  41.                 * 
  42.                 * 2. callback的组合: 
  43.                 * 可以用基类或接口组合个种类的Callback, 见{@link org.xutils.common.Callback}. 
  44.                 * 例如: 
  45.                 * a. 组合使用CacheCallback将使请求检测缓存或将结果存入缓存(仅GET请求生效). 
  46.                 * b. 组合使用PrepareCallback的prepare方法将为callback提供一次后台执行耗时任务的机会, 
  47.                 * 然后将结果给onCache或onSuccess. 
  48.                 * c. 组合使用ProgressCallback将提供进度回调. 
  49.                 * ...(可参考{@link org.xutils.image.ImageLoader} 
  50.                 * 或 示例代码中的 {@link org.xutils.sample.download.DownloadCallback}) 
  51.                 * 
  52.                 * 3. 请求过程拦截或记录日志: 参考 {@link org.xutils.http.app.RequestTracker} 
  53.                 * 
  54.                 * 4. 请求Header获取: 参考 {@link org.xutils.http.app.RequestInterceptListener} 
  55.                 * 
  56.                 * 5. 其他(线程池, 超时, 重定向, 重试, 代理等): 参考 {@link org.xutils.http.RequestParams} 
  57.                 * 
  58.                 **/  
  59.                new Callback.CommonCallback<List<BaiduResponse>>() {  
  60.                    @Override  
  61.                    public void onSuccess(List<BaiduResponse> result) {  
  62.                        Toast.makeText(x.app(), result.get(0).toString(), Toast.LENGTH_LONG).show();  
  63.                    }  
  64.   
  65.                    @Override  
  66.                    public void onError(Throwable ex, boolean isOnCallback) {  
  67.                        Toast.makeText(x.app(), ex.getMessage(), Toast.LENGTH_LONG).show();  
  68.                        if (ex instanceof HttpException) { // 网络错误  
  69.                            HttpException httpEx = (HttpException) ex;  
  70.                            int responseCode = httpEx.getCode();  
  71.                            String responseMsg = httpEx.getMessage();  
  72.                            String errorResult = httpEx.getResult();  
  73.                            // ...  
  74.                        } else { // 其他错误  
  75.                            // ...  
  76.                        }  
  77.                    }  
  78.   
  79.                    @Override  
  80.                    public void onCancelled(CancelledException cex) {  
  81.                        Toast.makeText(x.app(), "cancelled", Toast.LENGTH_LONG).show();  
  82.                    }  
  83.   
  84.                    @Override  
  85.                    public void onFinished() {  
  86.   
  87.                    }  
  88.                });  
  89.   
  90.        // cancelable.cancel(); // 取消请求  
这块代码实现了一个简单的网络请求,一下是对代码的具体分析

此处对注解进行简单解释:
定义:注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。
它们都不会直接影响到程序的语义,只是作为注解(标识)存在,我们可以通过反射机制编程实现对这些元数据(用来描述数据的数据)的访问。另外,你可以在编译时选择代码里的注解是否只存在于源代码级,或者它也能在class文件、或者运行时中出现

注解的作用:

             1、生成文档。这是最常见的,也是Java 最早提供的注解。常用的有@see @param @return 等

             2、跟踪代码依赖性,实现替代配置文件功能。比较常见的是spring 2.5 开始的基于注解配置。作用就是减少配置。现在的框架基本都使用了这种配置来减少配置文件的数量。以后java的程序开发,最多的也将实现注解配置,具有很大用处;

             3、在编译时进行格式检查。如@override 放在方法前,如果你这个方法并不是覆盖了超类方法,则编译时就能检查出。


百度百科对注解的解释和简单实用介绍:
http://baike.baidu.com/link?url=xo0Ujicy5_XrmAlniHDrqBPmvbVnC5ieALuq4LvUoqLU1KlkW_6YsWM4g6OzZ5fXolXSZ48QFFNLgcObrUvr1q

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. Java代码  
  2. import java.lang.annotation.Annotation;  
  3. import java.lang.reflect.Method;  
  4.    
  5. //读取注解信息  
  6. public class ReadAnnotationInfoTest {  
  7.     public static void main(String[] args) throws Exception {  
  8.         // 测试AnnotationTest类,得到此类的类对象  
  9.         Class c = Class.forName("com.iwtxokhtd.annotation.AnnotationTest");  
  10.         // 获取该类所有声明的方法  
  11.         Method[] methods = c.getDeclaredMethods();  
  12.         // 声明注解集合  
  13.         Annotation[] annotations;  
  14.         // 遍历所有的方法得到各方法上面的注解信息  
  15.         for (Method method : methods) {  
  16.             // 获取每个方法上面所声明的所有注解信息  
  17.             annotations = method.getDeclaredAnnotations();  
  18.             // 再遍历所有的注解,打印其基本信息  
  19.             System.out.println(method.getName());  
  20.             for (Annotation an : annotations) {  
  21.                 System.out.println("方法名为:" + method.getName() + "其上面的注解为:" + an.annotationType().getSimpleName());  
  22.                 Method[] meths = an.annotationType().getDeclaredMethods();  
  23.                 // 遍历每个注解的所有变量  
  24.                 for (Method meth : meths) {  
  25.                     System.out.println("注解的变量名为:" + meth.getName());  
  26.                 }  
  27.             }  
  28.         }  
  29.     }  
  30. }  

此处说明了注解的使用方式


public @interface HttpRequest  对url、缓存key、签名、协议、请求参数等信息进行定义

public @interface HttpResponse      对请求参数以及结果解析方式进行定义注册








进入 public class BaiduParams extends RequestParams 可以看到Baiduparams继承自requestparams
并添加了新的字段
进入RequestParams 
[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. /** 
  2.  * Created by wyouflf on 15/7/17. 
  3.  * 网络请求参数实体 
  4.  */  
  5. public class RequestParams {  
  6.   
  7.     // 注解及其扩展参数  
  8.     private HttpRequest httpRequest;  
  9.     private final String uri;  
  10.     private final String[] signs;  
  11.     private final String[] cacheKeys;  
  12.     private ParamsBuilder builder;  
  13.     private String buildUri;  
  14.     private String buildCacheKey;  
  15.     private SSLSocketFactory sslSocketFactory;  
  16.   
  17.     // 请求体内容  
  18.     private HttpMethod method;  
  19.     private String bodyContent;  
  20.     private RequestBody requestBody;  
  21.     private final List<Header> headers = new ArrayList<Header>();  
  22.     private final List<KeyValue> queryStringParams = new ArrayList<KeyValue>();  
  23.     private final List<KeyValue> bodyParams = new ArrayList<KeyValue>();  
  24.     private final List<KeyValue> fileParams = new ArrayList<KeyValue>();  
  25.   
  26.     // 扩展参数  
  27.     private Proxy proxy; // 代理  
  28.     private String charset = "UTF-8";  
  29.     private boolean useCookie = true// 是否在请求过程中启用cookie  
  30.     private String cacheDirName; // 缓存文件夹名称  
  31.     private long cacheSize; // 缓存文件夹大小  
  32.     private long cacheMaxAge; // 默认缓存存活时间, 单位:毫秒.(如果服务没有返回有效的max-age或Expires)  
  33.     private boolean asJsonContent = false// 用json形式的bodyParams上传  
  34.     private Executor executor; // 自定义线程池  
  35.     private Priority priority = Priority.DEFAULT; // 请求优先级  
  36.     private int connectTimeout = 1000 * 15// 连接超时时间  
  37.     private boolean autoResume = true// 是否在下载是自动断点续传  
  38.     private boolean autoRename = false// 是否根据头信息自动命名文件  
  39.     private int maxRetryCount = 2// 最大请求错误重试次数  
  40.     private String saveFilePath; // 下载文件时文件保存的路径和文件名  
  41.     private boolean multipart = false// 是否强制使用multipart表单  
  42.     private boolean cancelFast = false// 是否可以被立即停止, true: 为请求创建新的线程, 取消时请求线程被立即中断.  
  43.     private int loadingUpdateMaxTimeSpan = 300// 进度刷新最大间隔时间(ms)  
  44.     private HttpRetryHandler httpRetryHandler; // 自定义HttpRetryHandler  
  45.     private RedirectHandler redirectHandler; // 自定义重定向接口, 默认系统自动重定向.  
  46.     private RequestTracker requestTracker; // 自定义日志记录接口.  

RequestParams封装了网络请求的参数以及缓存等信息的管理,并未发起网络请求


[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. @HttpResponse(parser = JsonResponseParser.class)  
  2. public class BaiduResponse {  

BaiduResponse使用了jsonResponseParse进行解析


系统提供了解析器的结构并未提供实现

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. /** 
  2.  * Created by wyouflf on 15/8/4. 
  3.  * {@link org.xutils.http.annotation.HttpResponse} 注解的返回值转换模板 
  4.  */  
  5. public interface ResponseParser {  
  6.   
  7.     /** 
  8.      * 检查请求相应头等处理 
  9.      * 
  10.      * @param request 
  11.      * @throws Throwable 
  12.      */  
  13.     void checkResponse(UriRequest request) throws Throwable;  
  14.   
  15.     /** 
  16.      * 转换result为resultType类型的对象 
  17.      * 
  18.      * @param resultType  返回值类型(可能带有泛型信息) 
  19.      * @param resultClass 返回值类型 
  20.      * @param result      字符串数据 
  21.      * @return 
  22.      * @throws Throwable 
  23.      */  
  24.     Object parse(Type resultType, Class<?> resultClass, String result) throws Throwable;  
  25. }  

由 JsonResponseParser implements ResponseParser可以看出自定义解析器还是很方便的

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. /** 
  2.  * Created by wyouflf on 15/11/5. 
  3.  */  
  4. public class JsonResponseParser implements ResponseParser {  
  5.   
  6.     @Override  
  7.     public void checkResponse(UriRequest request) throws Throwable {  
  8.         // custom check ?  
  9.         // check header ?  
  10.     }  
  11.   
  12.     /** 
  13.      * 转换result为resultType类型的对象 
  14.      * 
  15.      * @param resultType  返回值类型(可能带有泛型信息) 
  16.      * @param resultClass 返回值类型 
  17.      * @param result      字符串数据 
  18.      * @return 
  19.      * @throws Throwable 
  20.      */  
  21.     @Override  
  22.     public Object parse(Type resultType, Class<?> resultClass, String result) throws Throwable {  
  23.         // TODO: json to java bean  
  24.         if (resultClass == List.class) {  
  25.             // 这里只是个示例, 不做json转换.  
  26.             List<BaiduResponse> list = new ArrayList<BaiduResponse>();  
  27.             BaiduResponse baiduResponse = new BaiduResponse();  
  28.             baiduResponse.setTest(result);  
  29.             list.add(baiduResponse);  
  30.             return list;  
  31.             // fastJson:  
  32.             // return JSON.parseArray(result,  
  33.             // (Class<?>) ParameterizedTypeUtil.getParameterizedType(resultType, List.class, 0));  
  34.         } else {  
  35.             // 这里只是个示例, 不做json转换.  
  36.             BaiduResponse baiduResponse = new BaiduResponse();  
  37.             baiduResponse.setTest(result);  
  38.             return baiduResponse;  
  39.             // fastjson:  
  40.             // return JSON.parseObject(result, resultClass);  
  41.         }  
  42.   
  43.     }  
  44. }  


然后进入网络请求和返回值代码块

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. Callback.Cancelable cancelable  
  2.                = x.http().get(params,  
  3.                new Callback.CommonCallback<List<BaiduResponse>>() {  
  4.                    @Override  
  5.                    public void onSuccess(List<BaiduResponse> result) {  
  6.                        Toast.makeText(x.app(), result.get(0).toString(), Toast.LENGTH_LONG).show();  
  7.               
  8.                    }  
  9.                     
  10.                    @Override  
  11.                    public void onError(Throwable ex, boolean isOnCallback) {  
  12.                        Toast.makeText(x.app(), ex.getMessage(), Toast.LENGTH_LONG).show();  
  13.                        if (ex instanceof HttpException) { // 网络错误  
  14.                            HttpException httpEx = (HttpException) ex;  
  15.                            int responseCode = httpEx.getCode();  
  16.                            String responseMsg = httpEx.getMessage();  
  17.                            String errorResult = httpEx.getResult();  
  18.                            // ...  
  19.                        } else { // 其他错误  
  20.                            // ...  
  21.                        }  
  22.                    }  
  23.   
  24.                    @Override  
  25.                    public void onCancelled(CancelledException cex) {  
  26.                        Toast.makeText(x.app(), "cancelled", Toast.LENGTH_LONG).show();  
  27.                    }  
  28.   
  29.                    @Override  
  30.                    public void onFinished() {  
  31.   
  32.                    }  
  33.                });  

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public static HttpManager http() {  
  2.         if (Ext.httpManager == null) {  
  3.             HttpManagerImpl.registerInstance();  
  4.         }  
  5.         return Ext.httpManager;  
  6.     }  
此处创建http管理器单例
HttpManager接口对http 异步、同步请求进行定义

最终调用

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. @Override  
  2.     public <T> Callback.Cancelable request(HttpMethod method, RequestParams entity, Callback.CommonCallback<T> callback) {  
  3.         entity.setMethod(method);  
  4.         Callback.Cancelable cancelable = null;  
  5.         if (callback instanceof Callback.Cancelable) {  
  6.             cancelable = (Callback.Cancelable) callback;  
  7.         }  
  8.         HttpTask<T> task = new HttpTask<T>(entity, cancelable, callback);  
  9.         return x.task().start(task);  
  10.     }  

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. @Override  
  2.   public <T> T requestSync(HttpMethod method, RequestParams entity, Callback.TypedCallback<T> callback) throws Throwable {  
  3.       entity.setMethod(method);  
  4.       HttpTask<T> task = new HttpTask<T>(entity, null, callback);  
  5.       return x.task().startSync(task);  
  6.   }  

注意异步调用的返回值都是Callback.Cancelable 接口,是为了取消异步操作

Callback接口针对不同的请求定义了不同的处理方式,具体信息查看接口源码,注意此处的ResultType为泛型


不管是同步还是异步真正发起网络请求的是

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. HttpTask<T> task = new HttpTask<T>(entity, cancelable, callback);  
  2.        return x.task().start(task);  


进入httpTask
[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. /** 
  2.  * Created by wyouflf on 15/7/23. 
  3.  * http 请求任务 
  4.  */  
  5. public class HttpTask<ResultType> extends AbsTask<ResultType> implements ProgressHandler {  
  6.   
  7.     // 请求相关  
  8.     private RequestParams params;  
  9.     private UriRequest request;  
  10.     private RequestWorker requestWorker;  
  11.     private final Executor executor;  
  12.     private final Callback.CommonCallback<ResultType> callback;  
  13.   
  14.     // 缓存控制  
  15.     private Object rawResult = null;  
  16.     private final Object cacheLock = new Object();  
  17.     private volatile Boolean trustCache = null;  
  18.   
  19.     // 扩展callback  
  20.     private Callback.CacheCallback<ResultType> cacheCallback;  
  21.     private Callback.PrepareCallback prepareCallback;  
  22.     private Callback.ProgressCallback progressCallback;  
  23.     private RequestInterceptListener requestInterceptListener;  
  24.   
  25.     // 日志追踪  
  26.     private RequestTracker tracker;  
  27.   
  28.     // 文件下载线程数限制  
  29.     private Type loadType;  
  30.     private final static int MAX_FILE_LOAD_WORKER = 3;  
  31.     private final static AtomicInteger sCurrFileLoadCount = new AtomicInteger(0);  
  32.   
  33.     // 文件下载任务  
  34.     private static final HashMap<String, WeakReference<HttpTask<?>>>  
  35.             DOWNLOAD_TASK = new HashMap<String, WeakReference<HttpTask<?>>>(1);  
  36.   
  37.     private static final PriorityExecutor HTTP_EXECUTOR = new PriorityExecutor(5true);  
  38.     private static final PriorityExecutor CACHE_EXECUTOR = new PriorityExecutor(5true);  
此处对网络请求进行总体封装,包括网络请求参数、请求的发送和接收、请求发送和加载数据线程等

httpTask由TaskControllerImpl进行管理

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. /** 
  2.  * Created by wyouflf on 15/6/5. 
  3.  * 异步任务的管理类 
  4.  */  
  5. public final class TaskControllerImpl implements TaskController {  
  6.   
  7.     private TaskControllerImpl() {  
  8.     }  
  9.   
  10.     private static TaskController instance;  
  11.   
  12.     public static void registerInstance() {  
  13.         if (instance == null) {  
  14.             synchronized (TaskController.class) {  
  15.                 if (instance == null) {  
  16.                     instance = new TaskControllerImpl();  
  17.                 }  
  18.             }  
  19.         }  
  20.         x.Ext.setTaskController(instance);  
  21.     }  

调用x.task().start(task)时进入

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. /** 
  2.     * run task 
  3.     * 
  4.     * @param task 
  5.     * @param <T> 
  6.     * @return 
  7.     */  
  8.    @Override  
  9.    public <T> AbsTask<T> start(AbsTask<T> task) {  
  10.        TaskProxy<T> proxy = null;  
  11.        if (task instanceof TaskProxy) {  
  12.            proxy = (TaskProxy<T>) task;  
  13.        } else {  
  14.            proxy = new TaskProxy<T>(task);  
  15.        }  
  16.        try {  
  17.            proxy.doBackground();  
  18.        } catch (Throwable ex) {  
  19.            LogUtil.e(ex.getMessage(), ex);  
  20.        }  
  21.        return proxy;  
  22.    }  

可以看出httptask会转化成TaskProxy

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. /** 
  2.  * 异步任务的代理类(仅在task包内可用) 
  3.  * 
  4.  * @param <ResultType> 
  5.  */  
  6. /*package*/ class TaskProxy<ResultType> extends AbsTask<ResultType> {  
  7.   
  8.     /*package*/ static final InternalHandler sHandler = new InternalHandler();  
  9.     /*package*/ static final PriorityExecutor sDefaultExecutor = new PriorityExecutor(true);  
  10.   
  11.     private final AbsTask<ResultType> task;  
  12.     private final Executor executor;  
  13.     private volatile boolean callOnCanceled = false;  
  14.     private volatile boolean callOnFinished = false;  

从TaskProxy中可以得到内部handler、线程池


回到TaskControllerImpl的start方法中看到proxy.doBackground()为正式调用网络请求控制,进入该方法

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. @Override  
  2.     protected final ResultType doBackground() throws Throwable {  
  3.         this.onWaiting();  
  4.         PriorityRunnable runnable = new PriorityRunnable(  
  5.                 task.getPriority(),  
  6.                 new Runnable() {  
  7.                     @Override  
  8.                     public void run() {  
  9.                         try {  
  10.                             // 等待过程中取消  
  11.                             if (callOnCanceled || TaskProxy.this.isCancelled()) {  
  12.                                 throw new Callback.CancelledException("");  
  13.                             }  
  14.   
  15.                             // start running  
  16.                             TaskProxy.this.onStarted();  
  17.   
  18.                             if (TaskProxy.this.isCancelled()) { // 开始时取消  
  19.                                 throw new Callback.CancelledException("");  
  20.                             }  
  21.   
  22.                             // 执行task, 得到结果.  
  23.                             task.setResult(task.doBackground());  
  24.                             TaskProxy.this.setResult(task.getResult());  
  25.   
  26.                             // 未在doBackground过程中取消成功  
  27.                             if (TaskProxy.this.isCancelled()) {  
  28.                                 throw new Callback.CancelledException("");  
  29.                             }  
  30.   
  31.                             // 执行成功  
  32.                             TaskProxy.this.onSuccess(task.getResult());  
  33.                         } catch (Callback.CancelledException cex) {  
  34.                             TaskProxy.this.onCancelled(cex);  
  35.                         } catch (Throwable ex) {  
  36.                             TaskProxy.this.onError(ex, false);  
  37.                         } finally {  
  38.                             TaskProxy.this.onFinished();  
  39.                         }  
  40.                     }  
  41.                 });  
  42.         this.executor.execute(runnable);  
  43.         return null;  
  44.     }  

可以看到this.executor.execute(runnable)最终执行了任务线程

在任务线程 


在构造函数中可以看出

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. /*package*/ TaskProxy(AbsTask<ResultType> task) {  
  2.        super(task);  
  3.        this.task = task;  
  4.        this.task.setTaskProxy(this);  
  5.        this.setTaskProxy(null);  
  6.        Executor taskExecutor = task.getExecutor();  
  7.        if (taskExecutor == null) {  
  8.            taskExecutor = sDefaultExecutor;  
  9.        }  
  10.        this.executor = taskExecutor;  
  11.    }  
executor若为null,那么使用系统自带的PriorityExecutor

进入PriorityExecutor可以看出这是一个使用优先级的线程池管理类

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. /** 
  2.     * @param poolSize 工作线程数 
  3.     * @param fifo     优先级相同时, 等待队列的是否优先执行先加入的任务. 
  4.     */  
  5.    public PriorityExecutor(int poolSize, boolean fifo) {  
  6.        BlockingQueue<Runnable> mPoolWorkQueue =  
  7.                new PriorityBlockingQueue<Runnable>(MAXIMUM_POOL_SIZE, fifo ? FIFO_CMP : FILO_CMP);  
  8.        mThreadPoolExecutor = new ThreadPoolExecutor(  
  9.                poolSize,  
  10.                MAXIMUM_POOL_SIZE,  
  11.                KEEP_ALIVE,  
  12.                TimeUnit.SECONDS,  
  13.                mPoolWorkQueue,  
  14.                sThreadFactory);  
  15.    }  
此处根据优先级进行了队列的排序方式


回到taskProxy doBackground方法

如果请求已经开启并在中途取消异步操作那么抛出异常终止网络请求

如果出现异常或者请求完成那么调用handler

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. @Override  
  2.    protected void onSuccess(ResultType result) {  
  3.        this.setState(State.SUCCESS);  
  4.        sHandler.obtainMessage(MSG_WHAT_ON_SUCCESS, this).sendToTarget();  
  5.    }  
handler接收到后通知httptask

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. switch (msg.what) {  
  2.                    case MSG_WHAT_ON_WAITING: {  
  3.                        taskProxy.task.onWaiting();  
  4.                        break;  
  5.                    }  
  6.                    case MSG_WHAT_ON_START: {  
  7.                        taskProxy.task.onStarted();  
  8.                        break;  
  9.                    }  
  10.                    case MSG_WHAT_ON_SUCCESS: {  
  11.                        taskProxy.task.onSuccess(taskProxy.getResult());  
  12.                        break;  
  13.                    }  
  14.                    case MSG_WHAT_ON_ERROR: {  
  15.                        assert args != null;  
  16.                        Throwable throwable = (Throwable) args[0];  
  17.                        LogUtil.d(throwable.getMessage(), throwable);  
  18.                        taskProxy.task.onError(throwable, false);  
  19.                        break;  
  20.                    }  
  21.                    case MSG_WHAT_ON_UPDATE: {  
  22.                        taskProxy.task.onUpdate(msg.arg1, args);  
  23.                        break;  
  24.                    }  
  25.                    case MSG_WHAT_ON_CANCEL: {  
  26.                        if (taskProxy.callOnCanceled) return;  
  27.                        taskProxy.callOnCanceled = true;  
  28.                        assert args != null;  
  29.                        taskProxy.task.onCancelled((org.xutils.common.Callback.CancelledException) args[0]);  
  30.                        break;  
  31.                    }  
  32.                    case MSG_WHAT_ON_FINISHED: {  
  33.                        if (taskProxy.callOnFinished) return;  
  34.                        taskProxy.callOnFinished = true;  
  35.                        taskProxy.task.onFinished();  
  36.                        break;  
  37.                    }  
  38.                    default: {  
  39.                        break;  
  40.                    }  

在httpTask接收到返回后调用callback更新界面

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. @Override  
  2. protected void onSuccess(ResultType result) {  
  3.     if (tracker != null) {  
  4.         tracker.onSuccess(request, result);  
  5.     }  
  6.     if (result != null) {  
  7.         callback.onSuccess(result);  
  8.     }  
  9. }  

但是到现在并未对真正意义上的网络请求进行分析,到此只是分析了一个大体流程

由taskProxy的doBackground()中可以看出网络请求的完成是由  task.setResult(task.doBackground())代码完成的,进入httptask的doBackground方法

在这个方法中才对注解、请求参数、请求线程、缓存等进行操作管理

一下是对该放方法的具体分析

首先进入

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. // 解析loadType  
  2.    private void resolveLoadType() {  
  3.        Class<?> callBackType = callback.getClass();  
  4.        if (callback instanceof Callback.TypedCallback) {  
  5.            loadType = ((Callback.TypedCallback) callback).getLoadType();  
  6.        } else if (callback instanceof Callback.PrepareCallback) {  
  7.            loadType = ParameterizedTypeUtil.getParameterizedType(callBackType, Callback.PrepareCallback.class0);  
  8.        } else {  
  9.            loadType = ParameterizedTypeUtil.getParameterizedType(callBackType, Callback.CommonCallback.class0);  
  10.        }  
  11.    }  
Callback接口针对不同的请求定义了不同的处理方式

然后调用

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. // 初始化请求参数  
  2.    private UriRequest createNewRequest() throws Throwable {  
  3.        // init request  
  4.        params.init();  
  5.        UriRequest result = UriRequestFactory.getUriRequest(params, loadType);  
  6.        result.setCallingClassLoader(callback.getClass().getClassLoader());  
  7.        result.setProgressHandler(this);  
  8.        this.loadingUpdateMaxTimeSpan = params.getLoadingUpdateMaxTimeSpan();  
  9.        this.update(FLAG_REQUEST_CREATED, result);  
  10.        return result;  
  11.    }  

在parmas.init中对请求参数进行了解析

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. // invoke via HttpTask#createNewRequest  
  2.    /*package*/ void init() throws Throwable {  
  3.        if (!TextUtils.isEmpty(buildUri)) return;  
  4.   
  5.        if (TextUtils.isEmpty(uri) && getHttpRequest() == null) {  
  6.            throw new IllegalStateException("uri is empty && @HttpRequest == null");  
  7.        }  
  8.   
  9.        // init params from entity  
  10.        initEntityParams();  
  11.   
  12.        // build uri & cacheKey  
  13.        buildUri = uri;  
  14.        HttpRequest httpRequest = this.getHttpRequest();  
  15.        if (httpRequest != null) {  
  16.            builder = httpRequest.builder().newInstance();  
  17.            buildUri = builder.buildUri(httpRequest);  
  18.            builder.buildParams(this);  
  19.            builder.buildSign(this, httpRequest.signs());  
  20.            if (sslSocketFactory == null) {  
  21.                sslSocketFactory = builder.getSSLSocketFactory();  
  22.            }  
  23.        } else if (this.builder != null) {  
  24.            builder.buildParams(this);  
  25.            builder.buildSign(this, signs);  
  26.            if (sslSocketFactory == null) {  
  27.                sslSocketFactory = builder.getSSLSocketFactory();  
  28.            }  
  29.        }  
  30.    }  


UriRequest请求发送和数据接收是由 Uri请求创建工厂UriRequestFactory根据Callback类型和请求参数产生的

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public static UriRequest getUriRequest(RequestParams params, Type loadType) throws Throwable {  
  2.        String uri = params.getUri();  
  3.        if (uri.startsWith("http")) {  
  4.            return new HttpRequest(params, loadType);  
  5.        } else if (uri.startsWith("assets://")) {  
  6.            if (assetsRequestCls != null) {  
  7.                Constructor<? extends AssetsRequest> constructor  
  8.                        = assetsRequestCls.getConstructor(RequestParams.class, Class.class);  
  9.                return constructor.newInstance(params, loadType);  
  10.            } else {  
  11.                return new AssetsRequest(params, loadType);  
  12.            }  
  13.        } else if (uri.startsWith("file:") || uri.startsWith("/")) {  
  14.            return new LocalFileRequest(params, loadType);  
  15.        } else {  
  16.            throw new IllegalArgumentException("The url not be support: " + uri);  
  17.        }  
  18.    }  

然后进入UriRequest此处有个Loader,并由LoaderFactory.getLoader产生,进入此方法

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public final class LoaderFactory {  
  2.   
  3.     private LoaderFactory() {  
  4.     }  
  5.   
  6.     /** 
  7.      * key: loadType 
  8.      */  
  9.     private static final HashMap<Type, Loader> converterHashMap = new HashMap<Type, Loader>();  
  10.   
  11.     static {  
  12.         converterHashMap.put(JSONObject.classnew JSONObjectLoader());  
  13.         converterHashMap.put(JSONArray.classnew JSONArrayLoader());  
  14.         converterHashMap.put(String.classnew StringLoader());  
  15.         converterHashMap.put(File.classnew FileLoader());  
  16.         converterHashMap.put(byte[].classnew ByteArrayLoader());  
  17.         BooleanLoader booleanLoader = new BooleanLoader();  
  18.         converterHashMap.put(boolean.class, booleanLoader);  
  19.         converterHashMap.put(Boolean.class, booleanLoader);  
  20.         IntegerLoader integerLoader = new IntegerLoader();  
  21.         converterHashMap.put(int.class, integerLoader);  
  22.         converterHashMap.put(Integer.class, integerLoader);  
  23.     }  
  24.   
  25.     @SuppressWarnings("unchecked")  
  26.     public static Loader<?> getLoader(Type type, RequestParams params) {  
  27.         Loader<?> result = converterHashMap.get(type);  
  28.         if (result == null) {  
  29.             result = new ObjectLoader(type);  
  30.         } else {  
  31.             result = result.newInstance();  
  32.         }  
  33.         result.setParams(params);  
  34.         return result;  
  35.     }  
  36.   
  37.     public static <T> void registerLoader(Type type, Loader<T> loader) {  
  38.         converterHashMap.put(type, loader);  
  39.     }  
  40. }  

此类包含了各种加载器,

进入抽象loader类

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. **  
  2.  * Author: wyouflf  
  3.  * Time: 2014/05/26  
  4.  */  
  5. public abstract class Loader<T> {  
  6.   
  7.     protected RequestParams params;  
  8.     protected ProgressHandler progressHandler;  
  9.   
  10.     public void setParams(final RequestParams params) {  
  11.         this.params = params;  
  12.     }  
  13.   
  14.     public void setProgressHandler(final ProgressHandler callbackHandler) {  
  15.         this.progressHandler = callbackHandler;  
  16.     }  
  17.   
  18.     protected void saveStringCache(UriRequest request, String resultStr) {  
  19.         if (!TextUtils.isEmpty(resultStr)) {  
  20.             DiskCacheEntity entity = new DiskCacheEntity();  
  21.             entity.setKey(request.getCacheKey());  
  22.             entity.setLastAccess(System.currentTimeMillis());  
  23.             entity.setEtag(request.getETag());  
  24.             entity.setExpires(request.getExpiration());  
  25.             entity.setLastModify(new Date(request.getLastModified()));  
  26.             entity.setTextContent(resultStr);  
  27.             LruDiskCache.getDiskCache(request.getParams().getCacheDirName()).put(entity);  
  28.         }  
  29.     }  
  30.   
  31.     public abstract Loader<T> newInstance();  
  32.   
  33.     public abstract T load(final InputStream in) throws Throwable;  
  34.   
  35.     public abstract T load(final UriRequest request) throws Throwable;  
  36.   
  37.     public abstract T loadFromCache(final DiskCacheEntity cacheEntity) throws Throwable;  
  38.   
  39.     public abstract void save2Cache(final UriRequest request);  
  40. }  
类此定义了各种数据加载类型()


LoaderFactory中数据处理类型

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. converterHashMap.put(JSONObject.classnew JSONObjectLoader());  
  2.        converterHashMap.put(JSONArray.classnew JSONArrayLoader());  
  3.        converterHashMap.put(String.classnew StringLoader());  
  4.        converterHashMap.put(File.classnew FileLoader());  
  5.        converterHashMap.put(byte[].classnew ByteArrayLoader());  
  6.        BooleanLoader booleanLoader = new BooleanLoader();  
  7.        converterHashMap.put(boolean.class, booleanLoader);  
  8.        converterHashMap.put(Boolean.class, booleanLoader);  
  9.        IntegerLoader integerLoader = new IntegerLoader();  
  10.        converterHashMap.put(int.class, integerLoader);  
  11.        converterHashMap.put(Integer.class, integerLoader);  


进入JsonArrayLoader

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. /** 
  2.  * Author: wyouflf 
  3.  * Time: 2014/06/16 
  4.  */  
  5. /*package*/ class JSONArrayLoader extends Loader<JSONArray> {  
  6.   
  7.     private String charset = "UTF-8";  
  8.     private String resultStr = null;  
  9.   
  10.     @Override  
  11.     public Loader<JSONArray> newInstance() {  
  12.         return new JSONArrayLoader();  
  13.     }  
  14.   
  15.     @Override  
  16.     public void setParams(final RequestParams params) {  
  17.         if (params != null) {  
  18.             String charset = params.getCharset();  
  19.             if (!TextUtils.isEmpty(charset)) {  
  20.                 this.charset = charset;  
  21.             }  
  22.         }  
  23.     }  
  24.   
  25.     @Override  
  26.     public JSONArray load(final InputStream in) throws Throwable {  
  27.         resultStr = IOUtil.readStr(in, charset);  
  28.         return new JSONArray(resultStr);  
  29.     }  
  30.   
  31.     @Override  
  32.     public JSONArray load(final UriRequest request) throws Throwable {  
  33.         request.sendRequest();  
  34.         return this.load(request.getInputStream());  
  35.     }  
  36.   
  37.     @Override  
  38.     public JSONArray loadFromCache(final DiskCacheEntity cacheEntity) throws Throwable {  
  39.         if (cacheEntity != null) {  
  40.             String text = cacheEntity.getTextContent();  
  41.             if (!TextUtils.isEmpty(text)) {  
  42.                 return new JSONArray(text);  
  43.             }  
  44.         }  
  45.   
  46.         return null;  
  47.     }  
  48.   
  49.     @Override  
  50.     public void save2Cache(UriRequest request) {  
  51.         saveStringCache(request, resultStr);  
  52.     }  
  53. }  


[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. @Override  
  2.     public JSONArray load(final UriRequest request) throws Throwable {  
  3.         request.sendRequest();  
  4.         return this.load(request.getInputStream());  
  5.     }  
此处调起了网络请求,进入网络请求详情HttpRequest的sendRequest方法(实现数据请求方式包括AssetsRequest、HttpRequest、LocalFileRequest)

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. /** 
  2.      * invoke via Loader 
  3.      * 
  4.      * @throws IOException 
  5.      */  
  6.     @Override  
  7.     @TargetApi(Build.VERSION_CODES.KITKAT)  
  8.     public void sendRequest() throws IOException {  
  9.         isLoading = false;  
  10.   
  11.         URL url = new URL(queryUrl);  
  12.         { // init connection  
  13.             Proxy proxy = params.getProxy();  
  14.             if (proxy != null) {  
  15.                 connection = (HttpURLConnection) url.openConnection(proxy);  
  16.             } else {  
  17.                 connection = (HttpURLConnection) url.openConnection();  
  18.             }  
  19.   
  20.             // try to fix bug: accidental EOFException before API 19  
  21.             if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {  
  22.                 connection.setRequestProperty("Connection""close");  
  23.             }  
  24.   
  25.             connection.setReadTimeout(params.getConnectTimeout());  
  26.             connection.setConnectTimeout(params.getConnectTimeout());  
  27.             connection.setInstanceFollowRedirects(params.getRedirectHandler() == null);  
  28.             if (connection instanceof HttpsURLConnection) {  
  29.                 SSLSocketFactory sslSocketFactory = params.getSslSocketFactory();  
  30.                 if (sslSocketFactory != null) {  
  31.                     ((HttpsURLConnection) connection).setSSLSocketFactory(sslSocketFactory);  
  32.                 }  
  33.             }  
  34.         }  
  35.   
  36.         if (params.isUseCookie()) {// add cookies  
  37.             try {  
  38.                 Map<String, List<String>> singleMap =  
  39.                         COOKIE_MANAGER.get(url.toURI(), new HashMap<String, List<String>>(0));  
  40.                 List<String> cookies = singleMap.get("Cookie");  
  41.                 if (cookies != null) {  
  42.                     connection.setRequestProperty("Cookie", TextUtils.join(";", cookies));  
  43.                 }  
  44.             } catch (Throwable ex) {  
  45.                 LogUtil.e(ex.getMessage(), ex);  
  46.             }  
  47.         }  
  48.   
  49.         {// add headers  
  50.             List<RequestParams.Header> headers = params.getHeaders();  
  51.             if (headers != null) {  
  52.                 for (RequestParams.Header header : headers) {  
  53.                     String name = header.key;  
  54.                     String value = header.getValueStr();  
  55.                     if (!TextUtils.isEmpty(name) && !TextUtils.isEmpty(value)) {  
  56.                         if (header.setHeader) {  
  57.                             connection.setRequestProperty(name, value);  
  58.                         } else {  
  59.                             connection.addRequestProperty(name, value);  
  60.                         }  
  61.                     }  
  62.                 }  
  63.             }  
  64.         }  
  65.   
  66.         { // write body  
  67.             HttpMethod method = params.getMethod();  
  68.             connection.setRequestMethod(method.toString());  
  69.             if (HttpMethod.permitsRequestBody(method)) {  
  70.                 RequestBody body = params.getRequestBody();  
  71.                 if (body != null) {  
  72.                     if (body instanceof ProgressBody) {  
  73.                         ((ProgressBody) body).setProgressHandler(progressHandler);  
  74.                     }  
  75.                     String contentType = body.getContentType();  
  76.                     if (!TextUtils.isEmpty(contentType)) {  
  77.                         connection.setRequestProperty("Content-Type", contentType);  
  78.                     }  
  79.                     long contentLength = body.getContentLength();  
  80.                     if (contentLength < 0) {  
  81.                         connection.setChunkedStreamingMode(256 * 1024);  
  82.                     } else {  
  83.                         if (contentLength < Integer.MAX_VALUE) {  
  84.                             connection.setFixedLengthStreamingMode((int) contentLength);  
  85.                         } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {  
  86.                             connection.setFixedLengthStreamingMode(contentLength);  
  87.                         } else {  
  88.                             connection.setChunkedStreamingMode(256 * 1024);  
  89.                         }  
  90.                     }  
  91.                     connection.setRequestProperty("Content-Length", String.valueOf(contentLength));  
  92.                     connection.setDoOutput(true);  
  93.                     body.writeTo(connection.getOutputStream());  
  94.                 }  
  95.             }  
  96.         }  
  97.   
  98.         if (params.isUseCookie()) { // save cookies  
  99.             try {  
  100.                 Map<String, List<String>> headers = connection.getHeaderFields();  
  101.                 if (headers != null) {  
  102.                     COOKIE_MANAGER.put(url.toURI(), headers);  
  103.                 }  
  104.             } catch (Throwable ex) {  
  105.                 LogUtil.e(ex.getMessage(), ex);  
  106.             }  
  107.         }  
  108.   
  109.         // check response code  
  110.         responseCode = connection.getResponseCode();  
  111.         if (responseCode >= 300) {  
  112.             HttpException httpException = new HttpException(responseCode, this.getResponseMessage());  
  113.             try {  
  114.                 httpException.setResult(IOUtil.readStr(this.getInputStream(), params.getCharset()));  
  115.             } catch (Throwable ignored) {  
  116.             }  
  117.             LogUtil.e(httpException.toString() + ", url: " + queryUrl);  
  118.             throw httpException;  
  119.         }  
  120.   
  121.         isLoading = true;  
  122.     }  

此处发起网络请求并设置请求参数,设置输入输出流

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. @Override  
  2.    public JSONArray load(final InputStream in) throws Throwable {  
  3.        resultStr = IOUtil.readStr(in, charset);  
  4.        return new JSONArray(resultStr);  
  5.    }  
对流进行处理
[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. public static String readStr(InputStream in, String charset) throws IOException {  
  2.        if (TextUtils.isEmpty(charset)) charset = "UTF-8";  
  3.   
  4.        if (!(in instanceof BufferedInputStream)) {  
  5.            in = new BufferedInputStream(in);  
  6.        }  
  7.        Reader reader = new InputStreamReader(in, charset);  
  8.        StringBuilder sb = new StringBuilder();  
  9.        char[] buf = new char[1024];  
  10.        int len;  
  11.        while ((len = reader.read(buf)) >= 0) {  
  12.            sb.append(buf, 0, len);  
  13.        }  
  14.        return sb.toString().trim();  
  15.    }  
到此网络请求处理完成



再次回到HttpTask doBackground中

创建UriRequest后

然后检测

[java]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. // 文件下载冲突检测  
  2.     private void checkDownloadTask() {  
  3.         if (File.class == loadType) {  
  4.             synchronized (DOWNLOAD_TASK) {  
  5.                 String downloadTaskKey = this.params.getSaveFilePath();  
  6.                 /*{ 
  7.                     // 不处理缓存文件下载冲突, 
  8.                     // 缓存文件下载冲突会抛出FileLockedException异常, 
  9.                     // 使用异常处理控制是否重新尝试下载. 
  10.                     if (TextUtils.isEmpty(downloadTaskKey)) { 
  11.                         downloadTaskKey = this.request.getCacheKey(); 
  12.                     } 
  13.                 }*/  
  14.                 if (!TextUtils.isEmpty(downloadTaskKey)) {  
  15.                     WeakReference<HttpTask<?>> taskRef = DOWNLOAD_TASK.get(downloadTaskKey);  
  16.                     if (taskRef != null) {  
  17.                         HttpTask<?> task = taskRef.get();  
  18.                         if (task != null) {  
  19.                             task.cancel();  
  20.                             task.closeRequestSync();  
  21.                         }  
  22.                         DOWNLOAD_TASK.remove(downloadTaskKey);  
  23.                     }  
  24.                     DOWNLOAD_TASK.put(downloadTaskKey, new WeakReference<HttpTask<?>>(this));  
  25.                 } // end if (!TextUtils.isEmpty(downloadTaskKey))  
  26.             }  
  27.         }  
  28.     }  
如果当前请求下载的是文件并且正在执行那么停止该任务再次重新放入下载列表

然后设置请求的重连次数最大为3

然后从缓存中取如果缓存有值直接返回数据,然后没有那么将会建立请求发送和加载数据线程RequestWorker,此线程调用Loader.load()发起网络请求,并拿到返回值,如果callback实现了Callback.CacheCallback<ResultType>则缓存请求数据

关于http缓存的存取还是比较容易理解的,请自己查看


到此Xutils的http模块异步请求基本分析完毕,不懂的地方请自己查看源码


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值