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)上继续维护, 相对于旧版本:
- HTTP实现替换HttpClient为UrlConnection, 自动解析回调泛型, 更安全的断点续传策略.
- 支持标准的Cookie策略, 区分domain, path...
- 事件注解去除不常用的功能, 提高性能.
- 数据库api简化提高性能, 达到和greenDao一致的性能.
- 图片绑定支持gif(受系统兼容性影响, 部分gif文件只能静态显示), webp; 支持圆角, 圆形, 方形等裁剪, 支持自动旋转...
常见问题:
- 更好的管理图片缓存: https://github.com/wyouflf/xUtils3/issues/149
- Cookie的使用: https://github.com/wyouflf/xUtils3/issues/125
- 关于query参数? http请求可以通过 header, url, body(请求体)传参; query参数是url中问号(?)后面的参数.
- 关于body参数? body参数只有PUT, POST, PATCH, DELETE(老版本RFC2616文档没有明确指出它是否支持, 所以暂时支持)请求支持.
以下是对demo的分析
进入HttpFragment
- /**
- * 自定义实体参数类请参考:
- * 请求注解 {@link org.xutils.http.annotation.HttpRequest}
- * 请求注解处理模板接口 {@link org.xutils.http.app.ParamsBuilder}
- *
- * 需要自定义类型作为callback的泛型时, 参考:
- * 响应注解 {@link org.xutils.http.annotation.HttpResponse}
- * 响应注解处理模板接口 {@link org.xutils.http.app.ResponseParser}
- *
- * 示例: 查看 org.xutils.sample.http 包里的代码
- */
- BaiduParams params = new BaiduParams();
- params.wd = "xUtils";
- // 有上传文件时使用multipart表单, 否则上传原始文件流.
- // params.setMultipart(true);
- // 上传文件方式 1
- // params.uploadFile = new File("/sdcard/test.txt");
- // 上传文件方式 2
- // params.addBodyParameter("uploadFile", new File("/sdcard/test.txt"));
- Callback.Cancelable cancelable
- = x.http().get(params,
- /**
- * 1. callback的泛型:
- * callback参数默认支持的泛型类型参见{@link org.xutils.http.loader.LoaderFactory},
- * 例如: 指定泛型为File则可实现文件下载, 使用params.setSaveFilePath(path)指定文件保存的全路径.
- * 默认支持断点续传(采用了文件锁和尾端校验续传文件的一致性).
- * 其他常用类型可以自己在LoaderFactory中注册,
- * 也可以使用{@link org.xutils.http.annotation.HttpResponse}
- * 将注解HttpResponse加到自定义返回值类型上, 实现自定义ResponseParser接口来统一转换.
- * 如果返回值是json形式, 那么利用第三方的json工具将十分容易定义自己的ResponseParser.
- * 如示例代码{@link org.xutils.sample.http.BaiduResponse}, 可直接使用BaiduResponse作为
- * callback的泛型.
- *
- * @HttpResponse 注解 和 ResponseParser接口仅适合做json, xml等文本类型数据的解析,
- * 如果需要其他数据类型的解析可参考:
- * {@link org.xutils.http.loader.LoaderFactory}
- * 和 {@link org.xutils.common.Callback.PrepareCallback}.
- * LoaderFactory提供PrepareCallback第一个泛型参数类型的自动转换,
- * 第二个泛型参数需要在prepare方法中实现.
- * (LoaderFactory中已经默认提供了部分常用类型的转换实现, 其他类型需要自己注册.)
- *
- * 2. callback的组合:
- * 可以用基类或接口组合个种类的Callback, 见{@link org.xutils.common.Callback}.
- * 例如:
- * a. 组合使用CacheCallback将使请求检测缓存或将结果存入缓存(仅GET请求生效).
- * b. 组合使用PrepareCallback的prepare方法将为callback提供一次后台执行耗时任务的机会,
- * 然后将结果给onCache或onSuccess.
- * c. 组合使用ProgressCallback将提供进度回调.
- * ...(可参考{@link org.xutils.image.ImageLoader}
- * 或 示例代码中的 {@link org.xutils.sample.download.DownloadCallback})
- *
- * 3. 请求过程拦截或记录日志: 参考 {@link org.xutils.http.app.RequestTracker}
- *
- * 4. 请求Header获取: 参考 {@link org.xutils.http.app.RequestInterceptListener}
- *
- * 5. 其他(线程池, 超时, 重定向, 重试, 代理等): 参考 {@link org.xutils.http.RequestParams}
- *
- **/
- new Callback.CommonCallback<List<BaiduResponse>>() {
- @Override
- public void onSuccess(List<BaiduResponse> result) {
- Toast.makeText(x.app(), result.get(0).toString(), Toast.LENGTH_LONG).show();
- }
- @Override
- public void onError(Throwable ex, boolean isOnCallback) {
- Toast.makeText(x.app(), ex.getMessage(), Toast.LENGTH_LONG).show();
- if (ex instanceof HttpException) { // 网络错误
- HttpException httpEx = (HttpException) ex;
- int responseCode = httpEx.getCode();
- String responseMsg = httpEx.getMessage();
- String errorResult = httpEx.getResult();
- // ...
- } else { // 其他错误
- // ...
- }
- }
- @Override
- public void onCancelled(CancelledException cex) {
- Toast.makeText(x.app(), "cancelled", Toast.LENGTH_LONG).show();
- }
- @Override
- public void onFinished() {
- }
- });
- // cancelable.cancel(); // 取消请求
注解的作用:
1、生成文档。这是最常见的,也是Java 最早提供的注解。常用的有@see @param @return 等
2、跟踪代码依赖性,实现替代配置文件功能。比较常见的是spring 2.5 开始的基于注解配置。作用就是减少配置。现在的框架基本都使用了这种配置来减少配置文件的数量。以后java的程序开发,最多的也将实现注解配置,具有很大用处;
3、在编译时进行格式检查。如@override 放在方法前,如果你这个方法并不是覆盖了超类方法,则编译时就能检查出。
- Java代码
- import java.lang.annotation.Annotation;
- import java.lang.reflect.Method;
- //读取注解信息
- public class ReadAnnotationInfoTest {
- public static void main(String[] args) throws Exception {
- // 测试AnnotationTest类,得到此类的类对象
- Class c = Class.forName("com.iwtxokhtd.annotation.AnnotationTest");
- // 获取该类所有声明的方法
- Method[] methods = c.getDeclaredMethods();
- // 声明注解集合
- Annotation[] annotations;
- // 遍历所有的方法得到各方法上面的注解信息
- for (Method method : methods) {
- // 获取每个方法上面所声明的所有注解信息
- annotations = method.getDeclaredAnnotations();
- // 再遍历所有的注解,打印其基本信息
- System.out.println(method.getName());
- for (Annotation an : annotations) {
- System.out.println("方法名为:" + method.getName() + "其上面的注解为:" + an.annotationType().getSimpleName());
- Method[] meths = an.annotationType().getDeclaredMethods();
- // 遍历每个注解的所有变量
- for (Method meth : meths) {
- System.out.println("注解的变量名为:" + meth.getName());
- }
- }
- }
- }
- }
此处说明了注解的使用方式
- /**
- * Created by wyouflf on 15/7/17.
- * 网络请求参数实体
- */
- public class RequestParams {
- // 注解及其扩展参数
- private HttpRequest httpRequest;
- private final String uri;
- private final String[] signs;
- private final String[] cacheKeys;
- private ParamsBuilder builder;
- private String buildUri;
- private String buildCacheKey;
- private SSLSocketFactory sslSocketFactory;
- // 请求体内容
- private HttpMethod method;
- private String bodyContent;
- private RequestBody requestBody;
- private final List<Header> headers = new ArrayList<Header>();
- private final List<KeyValue> queryStringParams = new ArrayList<KeyValue>();
- private final List<KeyValue> bodyParams = new ArrayList<KeyValue>();
- private final List<KeyValue> fileParams = new ArrayList<KeyValue>();
- // 扩展参数
- private Proxy proxy; // 代理
- private String charset = "UTF-8";
- private boolean useCookie = true; // 是否在请求过程中启用cookie
- private String cacheDirName; // 缓存文件夹名称
- private long cacheSize; // 缓存文件夹大小
- private long cacheMaxAge; // 默认缓存存活时间, 单位:毫秒.(如果服务没有返回有效的max-age或Expires)
- private boolean asJsonContent = false; // 用json形式的bodyParams上传
- private Executor executor; // 自定义线程池
- private Priority priority = Priority.DEFAULT; // 请求优先级
- private int connectTimeout = 1000 * 15; // 连接超时时间
- private boolean autoResume = true; // 是否在下载是自动断点续传
- private boolean autoRename = false; // 是否根据头信息自动命名文件
- private int maxRetryCount = 2; // 最大请求错误重试次数
- private String saveFilePath; // 下载文件时文件保存的路径和文件名
- private boolean multipart = false; // 是否强制使用multipart表单
- private boolean cancelFast = false; // 是否可以被立即停止, true: 为请求创建新的线程, 取消时请求线程被立即中断.
- private int loadingUpdateMaxTimeSpan = 300; // 进度刷新最大间隔时间(ms)
- private HttpRetryHandler httpRetryHandler; // 自定义HttpRetryHandler
- private RedirectHandler redirectHandler; // 自定义重定向接口, 默认系统自动重定向.
- private RequestTracker requestTracker; // 自定义日志记录接口.
RequestParams封装了网络请求的参数以及缓存等信息的管理,并未发起网络请求
- @HttpResponse(parser = JsonResponseParser.class)
- public class BaiduResponse {
BaiduResponse使用了jsonResponseParse进行解析
系统提供了解析器的结构并未提供实现
- /**
- * Created by wyouflf on 15/8/4.
- * {@link org.xutils.http.annotation.HttpResponse} 注解的返回值转换模板
- */
- public interface ResponseParser {
- /**
- * 检查请求相应头等处理
- *
- * @param request
- * @throws Throwable
- */
- void checkResponse(UriRequest request) throws Throwable;
- /**
- * 转换result为resultType类型的对象
- *
- * @param resultType 返回值类型(可能带有泛型信息)
- * @param resultClass 返回值类型
- * @param result 字符串数据
- * @return
- * @throws Throwable
- */
- Object parse(Type resultType, Class<?> resultClass, String result) throws Throwable;
- }
由 JsonResponseParser implements ResponseParser可以看出自定义解析器还是很方便的
- /**
- * Created by wyouflf on 15/11/5.
- */
- public class JsonResponseParser implements ResponseParser {
- @Override
- public void checkResponse(UriRequest request) throws Throwable {
- // custom check ?
- // check header ?
- }
- /**
- * 转换result为resultType类型的对象
- *
- * @param resultType 返回值类型(可能带有泛型信息)
- * @param resultClass 返回值类型
- * @param result 字符串数据
- * @return
- * @throws Throwable
- */
- @Override
- public Object parse(Type resultType, Class<?> resultClass, String result) throws Throwable {
- // TODO: json to java bean
- if (resultClass == List.class) {
- // 这里只是个示例, 不做json转换.
- List<BaiduResponse> list = new ArrayList<BaiduResponse>();
- BaiduResponse baiduResponse = new BaiduResponse();
- baiduResponse.setTest(result);
- list.add(baiduResponse);
- return list;
- // fastJson:
- // return JSON.parseArray(result,
- // (Class<?>) ParameterizedTypeUtil.getParameterizedType(resultType, List.class, 0));
- } else {
- // 这里只是个示例, 不做json转换.
- BaiduResponse baiduResponse = new BaiduResponse();
- baiduResponse.setTest(result);
- return baiduResponse;
- // fastjson:
- // return JSON.parseObject(result, resultClass);
- }
- }
- }
然后进入网络请求和返回值代码块
- Callback.Cancelable cancelable
- = x.http().get(params,
- new Callback.CommonCallback<List<BaiduResponse>>() {
- @Override
- public void onSuccess(List<BaiduResponse> result) {
- Toast.makeText(x.app(), result.get(0).toString(), Toast.LENGTH_LONG).show();
- }
- @Override
- public void onError(Throwable ex, boolean isOnCallback) {
- Toast.makeText(x.app(), ex.getMessage(), Toast.LENGTH_LONG).show();
- if (ex instanceof HttpException) { // 网络错误
- HttpException httpEx = (HttpException) ex;
- int responseCode = httpEx.getCode();
- String responseMsg = httpEx.getMessage();
- String errorResult = httpEx.getResult();
- // ...
- } else { // 其他错误
- // ...
- }
- }
- @Override
- public void onCancelled(CancelledException cex) {
- Toast.makeText(x.app(), "cancelled", Toast.LENGTH_LONG).show();
- }
- @Override
- public void onFinished() {
- }
- });
- public static HttpManager http() {
- if (Ext.httpManager == null) {
- HttpManagerImpl.registerInstance();
- }
- return Ext.httpManager;
- }
HttpManager接口对http 异步、同步请求进行定义
最终调用
- @Override
- public <T> Callback.Cancelable request(HttpMethod method, RequestParams entity, Callback.CommonCallback<T> callback) {
- entity.setMethod(method);
- Callback.Cancelable cancelable = null;
- if (callback instanceof Callback.Cancelable) {
- cancelable = (Callback.Cancelable) callback;
- }
- HttpTask<T> task = new HttpTask<T>(entity, cancelable, callback);
- return x.task().start(task);
- }
和
- @Override
- public <T> T requestSync(HttpMethod method, RequestParams entity, Callback.TypedCallback<T> callback) throws Throwable {
- entity.setMethod(method);
- HttpTask<T> task = new HttpTask<T>(entity, null, callback);
- return x.task().startSync(task);
- }
注意异步调用的返回值都是Callback.Cancelable 接口,是为了取消异步操作
Callback接口针对不同的请求定义了不同的处理方式,具体信息查看接口源码,注意此处的ResultType为泛型
不管是同步还是异步真正发起网络请求的是
- HttpTask<T> task = new HttpTask<T>(entity, cancelable, callback);
- return x.task().start(task);
- /**
- * Created by wyouflf on 15/7/23.
- * http 请求任务
- */
- public class HttpTask<ResultType> extends AbsTask<ResultType> implements ProgressHandler {
- // 请求相关
- private RequestParams params;
- private UriRequest request;
- private RequestWorker requestWorker;
- private final Executor executor;
- private final Callback.CommonCallback<ResultType> callback;
- // 缓存控制
- private Object rawResult = null;
- private final Object cacheLock = new Object();
- private volatile Boolean trustCache = null;
- // 扩展callback
- private Callback.CacheCallback<ResultType> cacheCallback;
- private Callback.PrepareCallback prepareCallback;
- private Callback.ProgressCallback progressCallback;
- private RequestInterceptListener requestInterceptListener;
- // 日志追踪
- private RequestTracker tracker;
- // 文件下载线程数限制
- private Type loadType;
- private final static int MAX_FILE_LOAD_WORKER = 3;
- private final static AtomicInteger sCurrFileLoadCount = new AtomicInteger(0);
- // 文件下载任务
- private static final HashMap<String, WeakReference<HttpTask<?>>>
- DOWNLOAD_TASK = new HashMap<String, WeakReference<HttpTask<?>>>(1);
- private static final PriorityExecutor HTTP_EXECUTOR = new PriorityExecutor(5, true);
- private static final PriorityExecutor CACHE_EXECUTOR = new PriorityExecutor(5, true);
httpTask由TaskControllerImpl进行管理
- /**
- * Created by wyouflf on 15/6/5.
- * 异步任务的管理类
- */
- public final class TaskControllerImpl implements TaskController {
- private TaskControllerImpl() {
- }
- private static TaskController instance;
- public static void registerInstance() {
- if (instance == null) {
- synchronized (TaskController.class) {
- if (instance == null) {
- instance = new TaskControllerImpl();
- }
- }
- }
- x.Ext.setTaskController(instance);
- }
调用x.task().start(task)时进入
- /**
- * run task
- *
- * @param task
- * @param <T>
- * @return
- */
- @Override
- public <T> AbsTask<T> start(AbsTask<T> task) {
- TaskProxy<T> proxy = null;
- if (task instanceof TaskProxy) {
- proxy = (TaskProxy<T>) task;
- } else {
- proxy = new TaskProxy<T>(task);
- }
- try {
- proxy.doBackground();
- } catch (Throwable ex) {
- LogUtil.e(ex.getMessage(), ex);
- }
- return proxy;
- }
可以看出httptask会转化成TaskProxy
- /**
- * 异步任务的代理类(仅在task包内可用)
- *
- * @param <ResultType>
- */
- /*package*/ class TaskProxy<ResultType> extends AbsTask<ResultType> {
- /*package*/ static final InternalHandler sHandler = new InternalHandler();
- /*package*/ static final PriorityExecutor sDefaultExecutor = new PriorityExecutor(true);
- private final AbsTask<ResultType> task;
- private final Executor executor;
- private volatile boolean callOnCanceled = false;
- private volatile boolean callOnFinished = false;
从TaskProxy中可以得到内部handler、线程池
回到TaskControllerImpl的start方法中看到proxy.doBackground()为正式调用网络请求控制,进入该方法
- @Override
- protected final ResultType doBackground() throws Throwable {
- this.onWaiting();
- PriorityRunnable runnable = new PriorityRunnable(
- task.getPriority(),
- new Runnable() {
- @Override
- public void run() {
- try {
- // 等待过程中取消
- if (callOnCanceled || TaskProxy.this.isCancelled()) {
- throw new Callback.CancelledException("");
- }
- // start running
- TaskProxy.this.onStarted();
- if (TaskProxy.this.isCancelled()) { // 开始时取消
- throw new Callback.CancelledException("");
- }
- // 执行task, 得到结果.
- task.setResult(task.doBackground());
- TaskProxy.this.setResult(task.getResult());
- // 未在doBackground过程中取消成功
- if (TaskProxy.this.isCancelled()) {
- throw new Callback.CancelledException("");
- }
- // 执行成功
- TaskProxy.this.onSuccess(task.getResult());
- } catch (Callback.CancelledException cex) {
- TaskProxy.this.onCancelled(cex);
- } catch (Throwable ex) {
- TaskProxy.this.onError(ex, false);
- } finally {
- TaskProxy.this.onFinished();
- }
- }
- });
- this.executor.execute(runnable);
- return null;
- }
可以看到this.executor.execute(runnable)最终执行了任务线程
在任务线程
在构造函数中可以看出
- /*package*/ TaskProxy(AbsTask<ResultType> task) {
- super(task);
- this.task = task;
- this.task.setTaskProxy(this);
- this.setTaskProxy(null);
- Executor taskExecutor = task.getExecutor();
- if (taskExecutor == null) {
- taskExecutor = sDefaultExecutor;
- }
- this.executor = taskExecutor;
- }
进入PriorityExecutor可以看出这是一个使用优先级的线程池管理类
- /**
- * @param poolSize 工作线程数
- * @param fifo 优先级相同时, 等待队列的是否优先执行先加入的任务.
- */
- public PriorityExecutor(int poolSize, boolean fifo) {
- BlockingQueue<Runnable> mPoolWorkQueue =
- new PriorityBlockingQueue<Runnable>(MAXIMUM_POOL_SIZE, fifo ? FIFO_CMP : FILO_CMP);
- mThreadPoolExecutor = new ThreadPoolExecutor(
- poolSize,
- MAXIMUM_POOL_SIZE,
- KEEP_ALIVE,
- TimeUnit.SECONDS,
- mPoolWorkQueue,
- sThreadFactory);
- }
回到taskProxy doBackground方法
如果请求已经开启并在中途取消异步操作那么抛出异常终止网络请求
如果出现异常或者请求完成那么调用handler
- @Override
- protected void onSuccess(ResultType result) {
- this.setState(State.SUCCESS);
- sHandler.obtainMessage(MSG_WHAT_ON_SUCCESS, this).sendToTarget();
- }
- switch (msg.what) {
- case MSG_WHAT_ON_WAITING: {
- taskProxy.task.onWaiting();
- break;
- }
- case MSG_WHAT_ON_START: {
- taskProxy.task.onStarted();
- break;
- }
- case MSG_WHAT_ON_SUCCESS: {
- taskProxy.task.onSuccess(taskProxy.getResult());
- break;
- }
- case MSG_WHAT_ON_ERROR: {
- assert args != null;
- Throwable throwable = (Throwable) args[0];
- LogUtil.d(throwable.getMessage(), throwable);
- taskProxy.task.onError(throwable, false);
- break;
- }
- case MSG_WHAT_ON_UPDATE: {
- taskProxy.task.onUpdate(msg.arg1, args);
- break;
- }
- case MSG_WHAT_ON_CANCEL: {
- if (taskProxy.callOnCanceled) return;
- taskProxy.callOnCanceled = true;
- assert args != null;
- taskProxy.task.onCancelled((org.xutils.common.Callback.CancelledException) args[0]);
- break;
- }
- case MSG_WHAT_ON_FINISHED: {
- if (taskProxy.callOnFinished) return;
- taskProxy.callOnFinished = true;
- taskProxy.task.onFinished();
- break;
- }
- default: {
- break;
- }
在httpTask接收到返回后调用callback更新界面
- @Override
- protected void onSuccess(ResultType result) {
- if (tracker != null) {
- tracker.onSuccess(request, result);
- }
- if (result != null) {
- callback.onSuccess(result);
- }
- }
但是到现在并未对真正意义上的网络请求进行分析,到此只是分析了一个大体流程
由taskProxy的doBackground()中可以看出网络请求的完成是由 task.setResult(task.doBackground())代码完成的,进入httptask的doBackground方法
在这个方法中才对注解、请求参数、请求线程、缓存等进行操作管理
一下是对该放方法的具体分析
首先进入
- // 解析loadType
- private void resolveLoadType() {
- Class<?> callBackType = callback.getClass();
- if (callback instanceof Callback.TypedCallback) {
- loadType = ((Callback.TypedCallback) callback).getLoadType();
- } else if (callback instanceof Callback.PrepareCallback) {
- loadType = ParameterizedTypeUtil.getParameterizedType(callBackType, Callback.PrepareCallback.class, 0);
- } else {
- loadType = ParameterizedTypeUtil.getParameterizedType(callBackType, Callback.CommonCallback.class, 0);
- }
- }
然后调用
- // 初始化请求参数
- private UriRequest createNewRequest() throws Throwable {
- // init request
- params.init();
- UriRequest result = UriRequestFactory.getUriRequest(params, loadType);
- result.setCallingClassLoader(callback.getClass().getClassLoader());
- result.setProgressHandler(this);
- this.loadingUpdateMaxTimeSpan = params.getLoadingUpdateMaxTimeSpan();
- this.update(FLAG_REQUEST_CREATED, result);
- return result;
- }
在parmas.init中对请求参数进行了解析
- // invoke via HttpTask#createNewRequest
- /*package*/ void init() throws Throwable {
- if (!TextUtils.isEmpty(buildUri)) return;
- if (TextUtils.isEmpty(uri) && getHttpRequest() == null) {
- throw new IllegalStateException("uri is empty && @HttpRequest == null");
- }
- // init params from entity
- initEntityParams();
- // build uri & cacheKey
- buildUri = uri;
- HttpRequest httpRequest = this.getHttpRequest();
- if (httpRequest != null) {
- builder = httpRequest.builder().newInstance();
- buildUri = builder.buildUri(httpRequest);
- builder.buildParams(this);
- builder.buildSign(this, httpRequest.signs());
- if (sslSocketFactory == null) {
- sslSocketFactory = builder.getSSLSocketFactory();
- }
- } else if (this.builder != null) {
- builder.buildParams(this);
- builder.buildSign(this, signs);
- if (sslSocketFactory == null) {
- sslSocketFactory = builder.getSSLSocketFactory();
- }
- }
- }
UriRequest请求发送和数据接收是由 Uri请求创建工厂UriRequestFactory根据Callback类型和请求参数产生的
- public static UriRequest getUriRequest(RequestParams params, Type loadType) throws Throwable {
- String uri = params.getUri();
- if (uri.startsWith("http")) {
- return new HttpRequest(params, loadType);
- } else if (uri.startsWith("assets://")) {
- if (assetsRequestCls != null) {
- Constructor<? extends AssetsRequest> constructor
- = assetsRequestCls.getConstructor(RequestParams.class, Class.class);
- return constructor.newInstance(params, loadType);
- } else {
- return new AssetsRequest(params, loadType);
- }
- } else if (uri.startsWith("file:") || uri.startsWith("/")) {
- return new LocalFileRequest(params, loadType);
- } else {
- throw new IllegalArgumentException("The url not be support: " + uri);
- }
- }
然后进入UriRequest此处有个Loader,并由LoaderFactory.getLoader产生,进入此方法
- public final class LoaderFactory {
- private LoaderFactory() {
- }
- /**
- * key: loadType
- */
- private static final HashMap<Type, Loader> converterHashMap = new HashMap<Type, Loader>();
- static {
- converterHashMap.put(JSONObject.class, new JSONObjectLoader());
- converterHashMap.put(JSONArray.class, new JSONArrayLoader());
- converterHashMap.put(String.class, new StringLoader());
- converterHashMap.put(File.class, new FileLoader());
- converterHashMap.put(byte[].class, new ByteArrayLoader());
- BooleanLoader booleanLoader = new BooleanLoader();
- converterHashMap.put(boolean.class, booleanLoader);
- converterHashMap.put(Boolean.class, booleanLoader);
- IntegerLoader integerLoader = new IntegerLoader();
- converterHashMap.put(int.class, integerLoader);
- converterHashMap.put(Integer.class, integerLoader);
- }
- @SuppressWarnings("unchecked")
- public static Loader<?> getLoader(Type type, RequestParams params) {
- Loader<?> result = converterHashMap.get(type);
- if (result == null) {
- result = new ObjectLoader(type);
- } else {
- result = result.newInstance();
- }
- result.setParams(params);
- return result;
- }
- public static <T> void registerLoader(Type type, Loader<T> loader) {
- converterHashMap.put(type, loader);
- }
- }
此类包含了各种加载器,
进入抽象loader类
- **
- * Author: wyouflf
- * Time: 2014/05/26
- */
- public abstract class Loader<T> {
- protected RequestParams params;
- protected ProgressHandler progressHandler;
- public void setParams(final RequestParams params) {
- this.params = params;
- }
- public void setProgressHandler(final ProgressHandler callbackHandler) {
- this.progressHandler = callbackHandler;
- }
- protected void saveStringCache(UriRequest request, String resultStr) {
- if (!TextUtils.isEmpty(resultStr)) {
- DiskCacheEntity entity = new DiskCacheEntity();
- entity.setKey(request.getCacheKey());
- entity.setLastAccess(System.currentTimeMillis());
- entity.setEtag(request.getETag());
- entity.setExpires(request.getExpiration());
- entity.setLastModify(new Date(request.getLastModified()));
- entity.setTextContent(resultStr);
- LruDiskCache.getDiskCache(request.getParams().getCacheDirName()).put(entity);
- }
- }
- public abstract Loader<T> newInstance();
- public abstract T load(final InputStream in) throws Throwable;
- public abstract T load(final UriRequest request) throws Throwable;
- public abstract T loadFromCache(final DiskCacheEntity cacheEntity) throws Throwable;
- public abstract void save2Cache(final UriRequest request);
- }
LoaderFactory中数据处理类型
- converterHashMap.put(JSONObject.class, new JSONObjectLoader());
- converterHashMap.put(JSONArray.class, new JSONArrayLoader());
- converterHashMap.put(String.class, new StringLoader());
- converterHashMap.put(File.class, new FileLoader());
- converterHashMap.put(byte[].class, new ByteArrayLoader());
- BooleanLoader booleanLoader = new BooleanLoader();
- converterHashMap.put(boolean.class, booleanLoader);
- converterHashMap.put(Boolean.class, booleanLoader);
- IntegerLoader integerLoader = new IntegerLoader();
- converterHashMap.put(int.class, integerLoader);
- converterHashMap.put(Integer.class, integerLoader);
进入JsonArrayLoader
- /**
- * Author: wyouflf
- * Time: 2014/06/16
- */
- /*package*/ class JSONArrayLoader extends Loader<JSONArray> {
- private String charset = "UTF-8";
- private String resultStr = null;
- @Override
- public Loader<JSONArray> newInstance() {
- return new JSONArrayLoader();
- }
- @Override
- public void setParams(final RequestParams params) {
- if (params != null) {
- String charset = params.getCharset();
- if (!TextUtils.isEmpty(charset)) {
- this.charset = charset;
- }
- }
- }
- @Override
- public JSONArray load(final InputStream in) throws Throwable {
- resultStr = IOUtil.readStr(in, charset);
- return new JSONArray(resultStr);
- }
- @Override
- public JSONArray load(final UriRequest request) throws Throwable {
- request.sendRequest();
- return this.load(request.getInputStream());
- }
- @Override
- public JSONArray loadFromCache(final DiskCacheEntity cacheEntity) throws Throwable {
- if (cacheEntity != null) {
- String text = cacheEntity.getTextContent();
- if (!TextUtils.isEmpty(text)) {
- return new JSONArray(text);
- }
- }
- return null;
- }
- @Override
- public void save2Cache(UriRequest request) {
- saveStringCache(request, resultStr);
- }
- }
- @Override
- public JSONArray load(final UriRequest request) throws Throwable {
- request.sendRequest();
- return this.load(request.getInputStream());
- }
- /**
- * invoke via Loader
- *
- * @throws IOException
- */
- @Override
- @TargetApi(Build.VERSION_CODES.KITKAT)
- public void sendRequest() throws IOException {
- isLoading = false;
- URL url = new URL(queryUrl);
- { // init connection
- Proxy proxy = params.getProxy();
- if (proxy != null) {
- connection = (HttpURLConnection) url.openConnection(proxy);
- } else {
- connection = (HttpURLConnection) url.openConnection();
- }
- // try to fix bug: accidental EOFException before API 19
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
- connection.setRequestProperty("Connection", "close");
- }
- connection.setReadTimeout(params.getConnectTimeout());
- connection.setConnectTimeout(params.getConnectTimeout());
- connection.setInstanceFollowRedirects(params.getRedirectHandler() == null);
- if (connection instanceof HttpsURLConnection) {
- SSLSocketFactory sslSocketFactory = params.getSslSocketFactory();
- if (sslSocketFactory != null) {
- ((HttpsURLConnection) connection).setSSLSocketFactory(sslSocketFactory);
- }
- }
- }
- if (params.isUseCookie()) {// add cookies
- try {
- Map<String, List<String>> singleMap =
- COOKIE_MANAGER.get(url.toURI(), new HashMap<String, List<String>>(0));
- List<String> cookies = singleMap.get("Cookie");
- if (cookies != null) {
- connection.setRequestProperty("Cookie", TextUtils.join(";", cookies));
- }
- } catch (Throwable ex) {
- LogUtil.e(ex.getMessage(), ex);
- }
- }
- {// add headers
- List<RequestParams.Header> headers = params.getHeaders();
- if (headers != null) {
- for (RequestParams.Header header : headers) {
- String name = header.key;
- String value = header.getValueStr();
- if (!TextUtils.isEmpty(name) && !TextUtils.isEmpty(value)) {
- if (header.setHeader) {
- connection.setRequestProperty(name, value);
- } else {
- connection.addRequestProperty(name, value);
- }
- }
- }
- }
- }
- { // write body
- HttpMethod method = params.getMethod();
- connection.setRequestMethod(method.toString());
- if (HttpMethod.permitsRequestBody(method)) {
- RequestBody body = params.getRequestBody();
- if (body != null) {
- if (body instanceof ProgressBody) {
- ((ProgressBody) body).setProgressHandler(progressHandler);
- }
- String contentType = body.getContentType();
- if (!TextUtils.isEmpty(contentType)) {
- connection.setRequestProperty("Content-Type", contentType);
- }
- long contentLength = body.getContentLength();
- if (contentLength < 0) {
- connection.setChunkedStreamingMode(256 * 1024);
- } else {
- if (contentLength < Integer.MAX_VALUE) {
- connection.setFixedLengthStreamingMode((int) contentLength);
- } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
- connection.setFixedLengthStreamingMode(contentLength);
- } else {
- connection.setChunkedStreamingMode(256 * 1024);
- }
- }
- connection.setRequestProperty("Content-Length", String.valueOf(contentLength));
- connection.setDoOutput(true);
- body.writeTo(connection.getOutputStream());
- }
- }
- }
- if (params.isUseCookie()) { // save cookies
- try {
- Map<String, List<String>> headers = connection.getHeaderFields();
- if (headers != null) {
- COOKIE_MANAGER.put(url.toURI(), headers);
- }
- } catch (Throwable ex) {
- LogUtil.e(ex.getMessage(), ex);
- }
- }
- // check response code
- responseCode = connection.getResponseCode();
- if (responseCode >= 300) {
- HttpException httpException = new HttpException(responseCode, this.getResponseMessage());
- try {
- httpException.setResult(IOUtil.readStr(this.getInputStream(), params.getCharset()));
- } catch (Throwable ignored) {
- }
- LogUtil.e(httpException.toString() + ", url: " + queryUrl);
- throw httpException;
- }
- isLoading = true;
- }
此处发起网络请求并设置请求参数,设置输入输出流
- @Override
- public JSONArray load(final InputStream in) throws Throwable {
- resultStr = IOUtil.readStr(in, charset);
- return new JSONArray(resultStr);
- }
- public static String readStr(InputStream in, String charset) throws IOException {
- if (TextUtils.isEmpty(charset)) charset = "UTF-8";
- if (!(in instanceof BufferedInputStream)) {
- in = new BufferedInputStream(in);
- }
- Reader reader = new InputStreamReader(in, charset);
- StringBuilder sb = new StringBuilder();
- char[] buf = new char[1024];
- int len;
- while ((len = reader.read(buf)) >= 0) {
- sb.append(buf, 0, len);
- }
- return sb.toString().trim();
- }
再次回到HttpTask doBackground中
创建UriRequest后
然后检测
- // 文件下载冲突检测
- private void checkDownloadTask() {
- if (File.class == loadType) {
- synchronized (DOWNLOAD_TASK) {
- String downloadTaskKey = this.params.getSaveFilePath();
- /*{
- // 不处理缓存文件下载冲突,
- // 缓存文件下载冲突会抛出FileLockedException异常,
- // 使用异常处理控制是否重新尝试下载.
- if (TextUtils.isEmpty(downloadTaskKey)) {
- downloadTaskKey = this.request.getCacheKey();
- }
- }*/
- if (!TextUtils.isEmpty(downloadTaskKey)) {
- WeakReference<HttpTask<?>> taskRef = DOWNLOAD_TASK.get(downloadTaskKey);
- if (taskRef != null) {
- HttpTask<?> task = taskRef.get();
- if (task != null) {
- task.cancel();
- task.closeRequestSync();
- }
- DOWNLOAD_TASK.remove(downloadTaskKey);
- }
- DOWNLOAD_TASK.put(downloadTaskKey, new WeakReference<HttpTask<?>>(this));
- } // end if (!TextUtils.isEmpty(downloadTaskKey))
- }
- }
- }
然后设置请求的重连次数最大为3
然后从缓存中取如果缓存有值直接返回数据,然后没有那么将会建立请求发送和加载数据线程RequestWorker,此线程调用Loader.load()发起网络请求,并拿到返回值,如果callback实现了Callback.CacheCallback<ResultType>则缓存请求数据
关于http缓存的存取还是比较容易理解的,请自己查看
到此Xutils的http模块异步请求基本分析完毕,不懂的地方请自己查看源码