流行网络库第(二)篇---Volley源码解析
来源:互联网 发布:php字符串截取添加 编辑:程序博客网 时间:2024/05/18 00:36
在上一篇,流行网络库第(一)篇—Volley用法解析中了解了Volley的基本使用,但是对于Volley可能有些朋友还不是特别清楚。我也是结合源码与其他人的分析,才真正弄清楚Volley的工作原理。
先看一个Demo,Volley十几行代码就完成了一次HTPP请求,我们看看这段代码内部究竟发生了什么?
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); RequestQueue mQueue = Volley.newRequestQueue(MainActivity.this); mQueue.add(getStringRequest()); } public StringRequest getStringRequest() { return new StringRequest("https://suggest.taobao.com/sug?code=utf-8&q=beizi", new Response.Listener<String>() { @Override public void onResponse(String response) { Log.e(getClass().getName(), response); } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { Log.e(getClass().getName(), error.getMessage()); } } ); }}
首先第一句代码,创建一个消息队列
RequestQueue mQueue = Volley.newRequestQueue(MainActivity.this);
创建消息队列的逻辑,被封装在Volley中,以静态方法暴露出来,供外部调用,总共有3个重载方法,上面使用的是第一个方法(只有一个参数),第一个方法会调用第二个方法(两个参数),第二个方法会调用第三个方法(三个参数)。
public static RequestQueue newRequestQueue(Context context) { return newRequestQueue(context, null);}public static RequestQueue newRequestQueue(Context context, HttpStack stack){ return newRequestQueue(context, stack, -1);}public static RequestQueue newRequestQueue(Context context, int maxDiskCacheBytes) { return newRequestQueue(context, null, maxDiskCacheBytes);}
现在看第三个构造方法中做了什么?,这是第一个重点。
public static RequestQueue newRequestQueue(Context context, HttpStack stack, int maxDiskCacheBytes) { //应用的默认缓存目录,DEFAULT_CACHE_DIR值是volley,即文件cacheDir的文件名是volley File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR); // 通过应用程序的包名和版本信息来完善userAgent字符串 String userAgent = "volley/0"; try { String packageName = context.getPackageName(); PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0); userAgent = packageName + "/" + info.versionCode; } catch (NameNotFoundException e) { } //如果stack为NULL,就建立一个默认的HttpStack if (stack == null) { if (Build.VERSION.SDK_INT >= 9) { //版本在2.3之后,创建HurlStack,其内部用了HttpURLConnection stack = new HurlStack(); } else { //版本在2.3之前,创建HttpClientStack,其内部用了HttpClient stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent)); } } //将stack传到Network中,Network是负责网络请求的 Network network = new BasicNetwork(stack); RequestQueue queue; //创建RequestQueue时要传入一个Cache对象,用来做磁盘缓存 if (maxDiskCacheBytes <= -1) { // No maximum size specified queue = new RequestQueue(new DiskBasedCache(cacheDir), network); } else{ // Disk cache size specified queue = new RequestQueue(new DiskBasedCache(cacheDir, maxDiskCacheBytes), network); } // 开始请求队列 queue.start(); return queue; }
注释都写清楚了,主要就是在创建请求队列的时候,做一些初始化的工作。
- 1、创建应用缓存目录,这个目录是volley;
- 2、 通过应用程序的包名和版本信息来完善userAgent字符串,这个userAgent只有在使用HttpClientStack才有用到;
- 3、如果用户没有建立stack,就建立一个默认的Stack,这里根据版本号判断使用 HttpURLConnection还是使用HttpClient创建Stack;
- 4、创建Stack的目的是作为参数创建Network,Network是负责网络请求的;
- 5、有了Network和DiskBasedCache,最后创建消息队列并且开启消息队列。
到这里你可能还不知道,做这些初始化操作的目的是什么,继续看。
看到最后返回的是RequestQueue对象,我们现在看RequestQueue是怎么工作的。先看看RequestQueue里面几个重要的成员变量。
public class RequestQueue { //等待队列,当之前已经存在了与本次请求相同的请求时,会把相同的请求放在队列中,以URL为关键字(key) private final Map<String, Queue<Request<?>>> mWaitingRequests = new HashMap<String, Queue<Request<?>>>(); //存放所有请求,包括已经运行的和正在等待的请求(Request) private final Set<Request<?>> mCurrentRequests = new HashSet<Request<?>>(); //表示目前缓存的请求队列,这个队列会被传入到CacheDispatcher做判断。 private final PriorityBlockingQueue<Request<?>> mCacheQueue = new PriorityBlockingQueue<Request<?>>(); //表示请求网络连接的队列,里面的请求都会向服务器进行请求(这才是唯一和服务器打交道的队列)。 private final PriorityBlockingQueue<Request<?>> mNetworkQueue = new PriorityBlockingQueue<Request<?>>(); // Volley类中调用的构造函数,传入了默认的网络线程数,DEFAULT_NETWORK_THREAD_POOL_SIZE = 4(默认是4个线程) private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4; //缓存相关 private final Cache mCache; //网络请求相关 private final Network mNetwork; //请求结果分发器,会把响应得到的结果发送到主线程的回调方法中,最终传递给监听器。 private final ResponseDelivery mDelivery; //阻塞式线程数组,不断循环处理Request。 private NetworkDispatcher[] mDispatchers; //阻塞式线程,不断循环处理Request。 private CacheDispatcher mCacheDispatcher; //请求完成时的监听数组 private List<RequestFinishedListener> mFinishedListeners = new ArrayList<RequestFinishedListener>(); public static interface RequestFinishedListener<T> { public void onRequestFinished(Request<T> request); }}
上面的成员变量代表的意思,请看注释,现在看如何创建一个RequestQueue。这是第二个重点。
public RequestQueue(Cache cache, Network network, int threadPoolSize, ResponseDelivery delivery) { //初始化了network,dispatchers,delivery,cache对象,这里的cache是磁盘缓存的对象 //初始化完成后,立刻调用了start()方法 mCache = cache; mNetwork = network; mDispatchers = new NetworkDispatcher[threadPoolSize]; mDelivery = delivery; } public RequestQueue(Cache cache, Network network, int threadPoolSize) { this(cache, network, threadPoolSize, new ExecutorDelivery(new Handler(Looper.getMainLooper()))); } public RequestQueue(Cache cache, Network network) { this(cache, network, DEFAULT_NETWORK_THREAD_POOL_SIZE); } public void start() { //先停止两个阻塞式线程 stop(); //创建一个缓存线程(缓存分发器),不断的处理Request mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery); mCacheDispatcher.start(); //创建4个网络线程(网络分发器),不断的处理Request for (int i = 0; i < mDispatchers.length; i++) { NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork, mCache, mDelivery); mDispatchers[i] = networkDispatcher; networkDispatcher.start(); } } //停止5个阻塞式线程,一个缓存线程,4个网络线程 public void stop() { if (mCacheDispatcher != null) { mCacheDispatcher.quit(); } for (int i = 0; i < mDispatchers.length; i++) { if (mDispatchers[i] != null) { mDispatchers[i].quit(); } } }
现在看看,其实
RequestQueue mQueue = Volley.newRequestQueue(MainActivity.this);
这一行代码,原来做了这么多的事情,用最后两行代码来概括
RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir),network); queue.start();
就是创建了一个RequestQueue对象,调用start对象,开启了4个线程,同时我们要记清楚,RequestQueue并不是一个简单的Queue,内部有好几个Queue,其中mCurrentRequests 存放所有请求,包括已经运行的和正在等待的请求;mCacheQueue (Request)表示目前缓存的请求队列,这个队列会被传入到CacheDispatcher做判断;mNetworkQueue 表示请求网络连接的队列,里面的请求都会向服务器进行请求(这才是唯一和服务器打交道的队列)。并且RequestQueue中还创建了5个线程,其中CacheDispatcher是缓存线程,数量为1,NetworkDispatcher是网络线程,存放在mDispatchers中,数量默认为4。
RequestQueue准备好了,现在该看看,往里面add了。这是第三个重点。
mQueue.add(getStringRequest());
add方法如下
public <T> Request<T> add(Request<T> request) { //每个request的内部维持着一个RequestQueue,先给内部维持的RequestQueue赋值 request.setRequestQueue(this); synchronized (mCurrentRequests) { //mCurrentRequests中保存了所有的request,所以一上来就添加 mCurrentRequests.add(request); } //设置request的序列号 request.setSequence(getSequenceNumber()); // 设置request当前的真正进行的事件,标记现在是被添加到队列中了 request.addMarker("add-to-queue"); // 如果不允许有磁盘缓存,跳过mCacheQueue,直接把request添加到networkQueue中,然后去请求网络 if (!request.shouldCache()) { // 添加到mNetworkQueue中,进行网络交互 mNetworkQueue.add(request); return request; } // Insert request into stage if there's already a request with the same cache key in flight. //mWaitingRequests保存了当前正在执行的请求与等待的请求 synchronized (mWaitingRequests) { //这个cacheKey是请求的url和请求方式Method拼接起来的,保证了Key的唯一性 String cacheKey = request.getCacheKey(); // 判断等待队列中是否已经有这个request,如果有了就需要过滤重复请求 if (mWaitingRequests.containsKey(cacheKey)) { // There is already a request in flight. Queue up. // mWaitingRequests的类型是HashMap<String, Queue<Request<?>>> Queue<Request<?>> stagedRequests = mWaitingRequests.get(cacheKey); if (stagedRequests == null) { stagedRequests = new LinkedList<Request<?>>(); } stagedRequests.add(request); mWaitingRequests.put(cacheKey, stagedRequests); if (VolleyLog.DEBUG) { VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold.", cacheKey); } } else { // Insert 'null' queue for this cacheKey, indicating there is now a request in // flight. mWaitingRequests.put(cacheKey, null); mCacheQueue.add(request); } return request; } }
请注意mWaitingRequests,mWaitingRequests的类型是HashMap
public void start() { stop(); mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery); //先开启缓存线程 mCacheDispatcher.start(); for (int i = 0; i < mDispatchers.length; i++) { NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork, mCache, mDelivery); mDispatchers[i] = networkDispatcher; //在开启网络线程 networkDispatcher.start(); } }
所以我们看一下,当一个request已经添加到了mWaitingRequests中了,缓存线程mCacheDispatcher是怎么处理它的。注意CacheDispatcher接收了mCacheQueue, mNetworkQueue, mCache, mDelivery这些参数。
先看看CacheDispatcher类的成员。
public class CacheDispatcher extends Thread { private static final boolean DEBUG = VolleyLog.DEBUG; //缓存队列 private final BlockingQueue<Request<?>> mCacheQueue; //网路请求队列 private final BlockingQueue<Request<?>> mNetworkQueue; /** The cache to read from. */ private final Cache mCache; //将响应分发到主线程 private final ResponseDelivery mDelivery; /** Used for telling us to die. */ private volatile boolean mQuit = false; public CacheDispatcher( BlockingQueue<Request<?>> cacheQueue, BlockingQueue<Request<?>> networkQueue, Cache cache, ResponseDelivery delivery) { mCacheQueue = cacheQueue; mNetworkQueue = networkQueue; mCache = cache; mDelivery = delivery; }}
主要看CacheDispatcher线程中的run方法,这是第四个重点。
@Override public void run() { if (DEBUG) VolleyLog.v("start new dispatcher"); Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); // 初始化缓存区,就把之前文件中所保存的对象,一个一个读出来,保存在Map<String, CacheHeader> mEntries这个变量中 mCache.initialize(); Request<?> request; while (true) { // release previous request object to avoid leaking request object when mQueue is drained. request = null; try { // 从cache队列中取出一个request对象 request = mCacheQueue.take(); } catch (InterruptedException e) { // We may have been interrupted because it was time to quit. if (mQuit) { return; } continue; } try { // 设置当前事件名称,标记缓存队列中取出的 request.addMarker("cache-queue-take"); // 如果这个request已经被取消了,就直接finish掉,进行下一次循环 if (request.isCanceled()) { request.finish("cache-discard-canceled"); continue; } //上面缓存区已经被初始化,所以可以从缓冲区中取出一个缓存,从mCache内部维持的mEntries中取 Cache.Entry entry = mCache.get(request.getCacheKey()); if (entry == null) { // 设置当前事件名称 request.addMarker("cache-miss"); // Cache miss; send off to the network dispatcher. //加入到mNetworkQueue中,进行网络请求,进行下一次循环,处理下一次request mNetworkQueue.put(request); continue; } // entry不为null,但是已经完全过期了,让它重新访问网络 // If it is completely expired, just send it to the network. if (entry.isExpired()) { //设置当前事件名称,标记缓存命中且过期 request.addMarker("cache-hit-expired"); request.setCacheEntry(entry); //加入到mNetworkQueue中,进行下一次循环,处理下一次request mNetworkQueue.put(request); continue; } //设置当前事件名称,标记缓存命中 request.addMarker("cache-hit"); Response<?> response = request.parseNetworkResponse( new NetworkResponse(entry.data, entry.responseHeaders)); //设置当前事件名称,标记这个request已经被解析了 request.addMarker("cache-hit-parsed"); // 判断是否需要刷新 if (!entry.refreshNeeded()) { // 发送到response中 mDelivery.postResponse(request, response); } else { // Soft-expired cache hit. We can deliver the cached response, // but we need to also send the request to the network for // refreshing. //设置当前事件名称,标记缓存需要刷新 request.addMarker("cache-hit-refresh-needed"); request.setCacheEntry(entry); // Mark the response as intermediate. response.intermediate = true; // Post the intermediate response back to the user and have // the delivery then forward the request along to the network. final Request<?> finalRequest = request; mDelivery.postResponse(request, response, new Runnable() { @Override public void run() { try { mNetworkQueue.put(finalRequest); } catch (InterruptedException e) { // Not much we can do about this. } } }); } } catch (Exception e) { VolleyLog.e(e, "Unhandled exception %s", e.toString()); } } }
经过一系列的操作得到的request,最终会把一个request解析成一个Response,调用的是parseNetworkResponse方法。不同的request有不同的parseNetworkResponse方法,我们最上面的Demo用的是StringRequest。
@Override protected Response<String> parseNetworkResponse(NetworkResponse response) { String parsed; try { parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers)); } catch (UnsupportedEncodingException e) { parsed = new String(response.data); } return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response)); }
得到了Response之后,将这个Response交给mDelivery,mDelivery称为数据分发器,最开始由构造函数传进来的。关于数据的分发,这是第五个重点。
mDelivery.postResponse(request, response);
在RequestQueue构造中,mDelivery被初始化,是个ExecutorDelivery对象。
public RequestQueue(Cache cache, Network network, int threadPoolSize, ResponseDelivery delivery) { mCache = cache; mNetwork = network; mDispatchers = new NetworkDispatcher[threadPoolSize]; mDelivery = delivery; } public RequestQueue(Cache cache, Network network, int threadPoolSize) { this(cache, network, threadPoolSize, new ExecutorDelivery(new Handler(Looper.getMainLooper()))); }
ExecutorDelivery是个接口,继承ResponseDelivery
public interface ResponseDelivery { /** * Parses a response from the network or cache and delivers it. */ public void postResponse(Request<?> request, Response<?> response); /** * Parses a response from the network or cache and delivers it. The provided * Runnable will be executed after delivery. */ public void postResponse(Request<?> request, Response<?> response, Runnable runnable); /** * Posts an error for the given request. */ public void postError(Request<?> request, VolleyError error);}
ExecutorDelivery内部有一个Runnable,/用于分发网络的响应结果到主线程的,这是第六个重点。
private class ResponseDeliveryRunnable implements Runnable { private final Request mRequest; private final Response mResponse; private final Runnable mRunnable; public ResponseDeliveryRunnable(Request request, Response response, Runnable runnable) { mRequest = request; mResponse = response; mRunnable = runnable; } @SuppressWarnings("unchecked") @Override public void run() { // 看看是否已经被取消了,如果被取消了,结束它,不做分发处理 if (mRequest.isCanceled()) { //设置当前事件名称,标记request被取消 mRequest.finish("canceled-at-delivery"); return; } // 分发一个的响应结果 if (mResponse.isSuccess()) { //成功回调 mRequest.deliverResponse(mResponse.result); } else { //错误回调 mRequest.deliverError(mResponse.error); } // If this is an intermediate response, add a marker, otherwise we're done // and the request can be finished. if (mResponse.intermediate) { mRequest.addMarker("intermediate-response"); } else { //设置当前事件名称,标记request已经完成 mRequest.finish("done"); } // If we have been provided a post-delivery runnable, run it. if (mRunnable != null) { mRunnable.run(); } } }
mListener,mErrorListener就是我们Demo中设置的回调接口。
@Override protected void deliverResponse(String response) { if (mListener != null) { mListener.onResponse(response); } }
public void deliverError(VolleyError error) { if (mErrorListener != null) { mErrorListener.onErrorResponse(error); } }
现在再看那张图
其中蓝色部分代表主线程,绿色部分代表缓存线程,橙色部分代表网络线程。我们在主线程中调用RequestQueue的add()方法来添加一条网络请求,这条请求会先被加入到缓存队列当中,如果发现可以找到相应的缓存结果就直接读取缓存并解析,然后回调给主线程。如果在缓存中没有找到结果,则将这条请求加入到网络请求队列中,然后处理发送HTTP请求,解析响应结果,写入缓存,并回调主线程。
Volley工作流程总结:http://www.cnblogs.com/tianzhijiexian/p/4264468.html
1. 当一个RequestQueue被成功申请后会开启一个CacheDispatcher(缓存调度器)和4个(默认)NetworkDispatcher(网络请求调度器);
2. CacheDispatcher缓存调度器最为第一层缓冲,开始工作后阻塞的从缓存序列mCacheQueue中取得请求:
a. 对于已经取消了的请求,直接标记为跳过并结束这个请求
b. 全新或过期的请求,直接丢入mNetworkQueue中交由N个NetworkDispatcher进行处理
c. 已获得缓存信息(网络应答)却没有过期的请求,交由Request的parseNetworkResponse进行解析,从而确定此应答是否成功。然后将请求和应答交由Delivery分发者进行处理,如果需要更新缓存那么该请求还会被放入mNetworkQueue中
3. 用户将请求Request add到RequestQueue之后:
a. 对于不需要缓存的请求(需要额外设置,默认是需要缓存)直接丢入mNetworkQueue交由N个NetworkDispatcher处理;
b. 对于需要缓存的,全新的请求加入到mCacheQueue中给CacheDispatcher处理
c. 需要缓存,但是缓存列表中已经存在了相同URL的请求,放在mWaitingQueue中做暂时雪藏,待之前的请求完毕后,再重新添加到mCacheQueue中;
4. 网络请求调度器NetworkDispatcher作为网络请求真实发生的地方,对消息交给BasicNetwork进行处理,同样的,请求和结果都交由Delivery分发者进行处理;
5. Delivery分发者实际上已经是对网络请求处理的最后一层了,在Delivery对请求处理之前,Request已经对网络应答进行过解析,此时应答成功与否已经设定。而后Delivery根据请求所获得的应答情况做不同处理:
a. 若应答成功,则触发deliverResponse方法,最终会触发开发者为Request设定的Listener
b. 若应答失败,则触发deliverError方法,最终会触发开发者为Request设定的ErrorListener
处理完后,一个Request的生命周期就结束了,Delivery会调用Request的finish操作,将其从mRequestQueue中移除,与此同时,如果等待列表中存在相同URL的请求,则会将剩余的层级请求全部丢入mCacheQueue交由CacheDispatcher进行处理。
一个Request的生命周期:
1. 通过add加入mRequestQueue中,等待请求被执行;
2. 请求执行后,调用自身的parseNetworkResponse对网络应答进行处理,并判断这个应答是否成功;
3. 若成功,则最终会触发自身被开发者设定的Listener;若失败,最终会触发自身被开发者设定的ErrorListener。
参考链接:
http://www.cnblogs.com/tianzhijiexian/p/4264468.html
http://blog.csdn.net/guolin_blog/article/details/17656437
http://blog.csdn.net/ttdevs/article/details/17764351
- 流行网络库第(二)篇---Volley源码解析
- 流行网络库第(一)篇---Volley用法解析
- Volley 源码解析(二)
- Volley源码解析(二)
- Volley源码解析(二)
- Volley源码解析(二)
- Volley源码解析(二):RequestQueue
- 4、Volley解析(二),源码的深入分析一,缓存线程和网络请求线程
- Volley源码解析与Okhttp+Rxjava组合构建网络库
- Volley源码解析(一):网络请求内容
- Android网络编程(四)从源码解析Volley
- Volley框架使用与源码解析(二)
- Volley源码解析(二)——CacheDispatcher
- Android网络通信Volley框架源码浅析(二)
- [置顶] Android网络通信Volley框架源码浅析(二)
- Volley源码解析 --- Volley组成(1)
- Volley源码解析<六> HttpStack网络请求
- Volley源码解析(三)网络请求流程
- gdb调试跟踪多进程
- 升级ios10默认ruby版本
- Redis的使用 —— Java 使用 Redis
- 【常用模板】 线段树单点操作
- spring ioc原理
- 流行网络库第(二)篇---Volley源码解析
- MultipartFile类 文件处理
- 写一个把控件丢到垃圾桶的动画其实很简单
- 分布式锁的简单设计
- 天開萬朵祥雲 丙申年十一初五 2016年 福州
- 简单C程序的仔细分析
- Flutter基础—绘画效果之不透明度
- 运行时候出现问题Error:warning: Ignoring InnerClasses attribute for an anonymous inner class
- 蓝桥杯BASIC-20(数的读法)