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目录下
相关文章推荐
- HBase写数据过程
- 二叉排序树
- 154 Which three statements are true about persistent configuration? (Choose three.) A. A user cannot
- Pet(hdu 4707 BFS)
- 聊聊 Statsd 和 Collectd 那点事!
- 面试常见问题小结
- BestCoder Round #62 (div.2)(hdu5562,hdu5563,hdu5564(动态规划))
- ubuntu 无法解析主机的解决方法
- CFileDialog使用注意事项
- ip快速设置.bat
- tomcat html htm静态文件乱码utf-8的有关问题正文
- Proguard语法及常用proguard.cfg代码段
- osgearth访问几种数据的几种方式
- 系列文章--HTML5学习系列链接
- https://msdn.microsoft.com/en-us/library/ff648951.aspx
- Android仿微信语音聊天功能
- 需求 - 12 - 图片缓存
- Activity的生命周期
- Swift 中随机数
- labview做上位机+3D显示+MPU6050