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)上继续维护, 相对于旧版本号:
- 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工具将十分easy定义自己的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;
}此处创建http管理器单例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;
}
executor若为null,那么使用系统自带的PriorityExecutor进入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();
}handler接收到后通知httptask
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);
}
}Callback接口针对不同的请求定义了不同的处理方式然后调用
// 初始化请求參数
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());
}此处调起了网络请求,进入网络请求详情HttpRequest的sendRequest方法(实现数据请求方式包含AssetsRequest、HttpRequest、LocalFileRequest)
/**
* 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缓存的存取还是比較easy理解的,请自己查看
到此Xutils的http模块异步请求基本分析完成,不懂的地方请自己查看源代码
浙公网安备 33010602011771号