您的位置:首页 > 其它

VolleySupport使用说明

2015-12-10 17:23 627 查看

1、概述

VolleySupport是基于Google的Volley框架,在其基础上简单封装完成的。添加的代码主要位于manager目录下。Volley的源码已经加入到Android Source中,路径为
frameworks/volley


2 Volley源码分析

2.1 功能说明

volley提供功能如下:

JSON,图像等的异步下载;
网络请求的排序(scheduling)
网络请求的优先级处理
缓存
多级别取消请求
和Activity和生命周期的联动(Activity结束时同时取消所有网络请求)


2.2 Volley整体设计思路

首先添加实现Request抽象类的任务到RequestQueue中
然后通过Dispatch不断从RequestQueue中取出请求:
优先判断CacheDispatcher中是否有缓存数据
Cache中没有则通过NatworkDispatcher,从网络获取数据并缓存。
最后通过ResponseDelivery分发数据,做回调处理。


如下图所以:



2.3 主要类分析

Volley:Volley 工具类,对外暴露的 API,通过 newRequestQueue(…) 函数新建并启动一个请求队列RequestQueue。这里进行了版本号判断,大于等于9就使得HttpStack对象的实例为HurlStack,小于9则实例为HttpClientStack。

Request:表示一个请求的抽象类。StringRequest、JsonRequest、ImageRequest等都是它的子类,表示某种类型的请求。

RequestQueue:表示请求队列,里面包含一个CacheDispatcher(用于处理走缓存请求的调度线程)、NetworkDispatcher数组(用于处理走网络请求的调度线程),一个ResponseDelivery(返回结果分发接口),通过 start() 函数启动时会启动CacheDispatcher和NetworkDispatchers。

mCacheQueue 缓存请求队列

mNetworkQueue 网络请求队列

mCurrentRequests 正在进行中(正在请求中或者正在分发中),尚未完成的请求集合

mWaitingRequests 等待请求的集合,如果一个请求正在被处理并且可以被缓存,后续的相同 url 的请求,将进入此等待队列

CacheDispatcher:一个线程,用于调度处理缓存请求。启动后会不断从缓存请求队列mCacheQueue中取请求处理:

对于已经取消的请求,标记为跳过并结束这个请求;

新的或者过期的请求,直接放入mNetworkQueue中由N个NetworkDispatcher进行处理;

已获得缓存信息(网络应答)却没有过期的请求,由Request的parseNetworkResponse进行解析,从而确定此应答是否成功。如果需要更新缓存那么该请求还会被放入mNetworkQueue中由N个NetworkDispatcher进行处理。

最后将请求和应答交由Delivery(ExecutorDelivery)分发者进行处理,

NetworkDispatcher:一个线程,用于调度处理走网络的请求。启动后会不断从网络请求队列中取请求处理,队列为空则等待,请求处理结束则将结果传递给ResponseDelivery去执行后续处理,并判断结果是否要进行缓存。Volley中是使用了一个NetworkDispatcher线程数组去处理网络请求的。

ResponseDelivery:返回结果分发接口,最终调用Listener和ErrorListener方法通知UI线程。如果等待列表mWaitingRequests中存在相同URL的请求,则会将剩余的层级请求全部丢入mCacheQueue交由CacheDispatcher进行处理。目前实现类只有基于ExecutorDelivery的在入参 handler 对应线程内进行分发。

HttpStack:处理 Http 请求,返回请求结果。目前 Volley 中有基于 HttpURLConnection 的HurlStack和 基于 Apache HttpClient 的HttpClientStack。

Network:调用HttpStack处理请求,并将结果转换为可被ResponseDelivery处理的NetworkResponse。其实现类为BasicNetwork。

Cache:缓存请求结果,Volley 默认使用的是基于 sdcard 的DiskBasedCache。volley还提供了一个NoCache,也就是无缓存。

NetworkDispatcher得到请求结果后判断是否需要存储在 Cache。

CacheDispatcher会从 Cache 中取缓存结果。

类图如下:



2.4 添加请求

添加请求过程add分析如下



2.5 缓存处理

volley处理请求流程:



2.6 网络请求



2.7 Response转换



2.8 数据分发处理



3对volley的封装

主要位于
manager
包中。

3.1 RequestManager

简单封装了初始化和获取RequesQueue的方法。

public static void init(Context context, String userAgent) {
RequestManager.init(context, userAgent, 4);
}

public static void init(Context context, String userAgent, int threadPool) {
mRequestQueue = Volley.newRequestQueue(context, userAgent, threadPool);
}


3.2 Multipart请求支持

流格式上传文件支持包含三个文件

1、MultipartRequestParams为参数列表,支持两种类型参数

普通键值对
文件或文件流


MultipartRequestParams内部其实是一个FileWrapper,用来封装需要上传的流(文件最终也会被转换成流)。

private static class FileWrappers {
public InputStream[] inputStreams;
public String[] fileNames;
public String contentType;

public FileWrappers(InputStream[] inputStreams, String[] fileNames,
String contentType) {
this.inputStreams = inputStreams;
this.fileNames = fileNames;
this.contentType = contentType;
}

public String getFileName(int i) {
if (fileNames != null) {
return fileNames[i];
} else {
return "nofilename";
}
}
}


MultipartRequestParams提供了两类put方法,一种是类似map的可以存储键值对,这里略过;另一种可以put进来流或者文件,如下。

public void put(String key, File[] files) {
try {
FileInputStream[] inputStreams = new FileInputStream[files.length];
String[] fileNames = new String[files.length];
for (int i = 0; i < files.length; i++) {
if (files[i] != null) {
inputStreams[i] = new FileInputStream(files[i]);
fileNames[i] = files[i].getName();
} else {
inputStreams[i] = null;
fileNames[i] = null;
}
}
put(key, inputStreams, fileNames);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}

public void put(String key, InputStream[] streams, String[] fileNames) {
put(key, streams, fileNames, null);
}

public void put(String key, InputStream[] streams, String fileNames[], String contentType) {
if (key != null && streams != null) {
fileParams.put(key, new FileWrappers(streams, fileNames, contentType));
}
}


最终文件保存在了fileParams中。

MultipartRequestParams还提供了一个方法getEntity,用来将前面添加进来的参数和流封装成HttpEntity。

public HttpEntity getEntity() {
HttpEntity entity = null;
MultipartEntity multipartEntity = new MultipartEntity();

// Add string params
for (ConcurrentHashMap.Entry<String, String> entry : urlParams.entrySet()) {
multipartEntity.addPart(entry.getKey(), entry.getValue());
}

if (!fileParams.isEmpty()) {
// Add file params
int currentIndex = 0;
int lastIndex = fileParams.entrySet().size() - 1;
for (ConcurrentHashMap.Entry<String, FileWrappers> entry : fileParams.entrySet()) {
FileWrappers file = entry.getValue();
if (file.inputStreams != null) {
boolean last = currentIndex == lastIndex;
for (int i = 0; i < file.inputStreams.length; i++) {
boolean isLast;
if (i == file.inputStreams.length - 1) {
isLast = last & true;
} else {
isLast = last & false;
}
if (file.contentType != null) {
multipartEntity.addPart(entry.getKey(), file.getFileName(i), file.inputStreams[i], file.contentType, isLast);
} else {
multipartEntity.addPart(entry.getKey(), file.getFileName(i), file.inputStreams[i], isLast);
}
}
}
currentIndex++;
}
}
entity = multipartEntity;

return entity;
}


2.这里新建了一个MultipartEntity,提供了addPart方法添加流以及普通键值对,最终通过out写入流中。

public void addPart(final String key, final String fileName, final InputStream fin, String type, final boolean isLast) {
writeFirstBoundaryIfNeeds();
try {
type = "Content-Type: " + type + "\r\n";
out.write(("Content-Disposition: form-data; name=\"" + key + "\"; filename=\"" + fileName + "\"\r\n").getBytes());
out.write(type.getBytes());
out.write("Content-Transfer-Encoding: binary\r\n\r\n".getBytes());
final byte[] tmp = new byte[4096];
int l = 0;
if (fin != null) {
while ((l = fin.read(tmp)) != -1) {
out.write(tmp, 0, l);
}
}
if (!isLast) {
out.write(("\r\n--" + boundary + "\r\n").getBytes());
} else {
writeLastBoundaryIfNeeds();
}
out.flush();
} catch (final IOException e) {
e.printStackTrace();
} finally {
try {
if (fin != null) {
fin.close();
}
} catch (final IOException e) {
e.printStackTrace();
}
}
}


3.最终实现了一个MultipartRequest,这里继承的是StringRequest,因为差别不大。重写了
getBody()
方法,把MultipartEntity中的Entry读取出来,通过getBody返回一个字节数组给Http。(默认的getbody是获取Post的参数数据返回字节数组)

public byte[] getBody() throws AuthFailureError {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
if(params != null) {
httpEntity = params.getEntity();
try {
httpEntity.writeTo(baos);
} catch (IOException e) {
e.printStackTrace();
}
}
return baos.toByteArray();
}


4 取消请求

RequestQueue中添加了一个取消请求的方法,可以在不需要这个网络请求的时候通过tag取消它。

public void cancelAll(final Object tag) {
if (tag == null) {
throw new IllegalArgumentException("Cannot cancelAll with a null tag");
}
cancelAll(new RequestFilter() {
@Override
public boolean apply(Request<?> request) {
return request.getTag() == tag;
}
});
}


5 重试机制

volley提供了重试机制接口
RetryPolicy
,且提供了一个默认的实现类
DefaultRetryPolicy


public class DefaultRetryPolicy implements RetryPolicy {
private int mCurrentTimeoutMs;  //当前超时时间毫秒值
private int mCurrentRetryCount; //当前重试次数
private final int mMaxNumRetries;   //最大重试次数
private final float mBackoffMultiplier;//超时重试延时

public static final int DEFAULT_TIMEOUT_MS = 15*1000;
public static final int DEFAULT_MAX_RETRIES = 2;
public static final float DEFAULT_BACKOFF_MULT = 1f;

public DefaultRetryPolicy() {//使用默认的构造方法参数实例化
this(DEFAULT_TIMEOUT_MS, DEFAULT_MAX_RETRIES, DEFAULT_BACKOFF_MULT);
}

public DefaultRetryPolicy(int initialTimeoutMs, int maxNumRetries, float backoffMultiplier) {
mCurrentTimeoutMs = initialTimeoutMs;
mMaxNumRetries = maxNumRetries;
mBackoffMultiplier = backoffMultiplier;
}

@Override
public void retry(VolleyError error) throws VolleyError {
mCurrentRetryCount++;
//超时次数*重试延时 结果为下次重试网络请求时间,支持定制曲线增加时间。
mCurrentTimeoutMs += (mCurrentTimeoutMs * mBackoffMultiplier);
if (!hasAttemptRemaining()) {
throw error;
}
}

protected boolean hasAttemptRemaining() {
return mCurrentRetryCount <= mMaxNumRetries;
}
}


6 地址

maven地址
com.android.common:VolleySupport:1.0.1


源码地址
maven
目录下
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: