Volley源码分析
来源:互联网 发布:经营杠杆的理解知乎 编辑:程序博客网 时间:2024/06/11 02:39
参加了C轮公司的面试之后, 深感自身知识储备量的不足, 知耻后勇研究下源码先
Volley是一款google推荐的网络开源框架
用过都知道, 首先我们需要构建一个RequestQueue, 通过Volley.newRequestQueue(context)方法创建一个处理用户请求(请求的类别有很多:StringRequest, JsonObjectRequest, ImageRequest, 还有用户自定义的Request等)的队列, 下面我们看看源码, 它到底是如何构建这个队列的
/**构造方法有很多, 不过最终都会调用这一个构造方法*/public static RequestQueue newRequestQueue(Context context, HttpStack stack, int maxDiskCacheBytes) { //创建一个缓存文件的目录 File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR); String userAgent = "volley/0"; try { String packageName = context.getPackageName(); PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0); userAgent = packageName + "/" + info.versionCode; } catch (NameNotFoundException e) { } if (stack == null) { //如果SDK_INT >= 9则采用HttpUrlConnection请求网络 if (Build.VERSION.SDK_INT >= 9) { stack = new HurlStack(); } else { //否则采用HttpClient请求网络 stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent)); } } //使用将请求网络的stack再次封装成一个Network() Network network = new BasicNetwork(stack); RequestQueue queue; if (maxDiskCacheBytes <= -1){ // 若没有指定最大缓存量, 则使用默认值 // 将我们缓存的目录构建成一个磁盘缓存的对象和网络任务的处理者一并传入RequestQueue()构建queue对象 queue = new RequestQueue(new DiskBasedCache(cacheDir), network); } else{ // 指定最大缓存量的情况 queue = new RequestQueue(new DiskBasedCache(cacheDir, maxDiskCacheBytes), network); } // 启动我们的请求队列 queue.start(); return queue; }/**RequestQueue的构造方法(有多个, 笔者将其整合在了一起)*/public RequestQueue(Cache cache, Network network) { // 磁盘缓存的目录 mCache = cache; // 网络任务的处理者 mNetwork = network; // 网络任务的分发者, 用与设置网络任务并发的数量 mDispatchers = new NetworkDispatcher[DEFAULT_NETWORK_THREAD_POOL_SIZE]; // 用于在主线程中回调Request中的实现的接口 mDelivery = new ExecutorDelivery(new Handler(Looper.getMainLooper())));}
到这里我们的RequestQueue已经构建好并且启动了, 那么, 我们看看具体的start()过程
public void start() { //停止所有的线程(CacheDispatcher, NetworkDispatcher) stop(); // 这里的CacheDispatcher是一个线程, 里面有一个死循环, 当mCacheQueue中有request加入时则处理,否则循环等待 mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery); mCacheDispatcher.start(); // 这里的NetworkDispatcher一样是一个线程, 里面仍然有个死循环, 当mNetworkQueue有request加入时则处理request请求, 否则一直处于循环状态等待 for (int i = 0; i < mDispatchers.length; i++) { NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork, mCache, mDelivery); mDispatchers[i] = networkDispatcher; networkDispatcher.start(); } }
现在RequestQueue的启动已经解析完成了, 下面看看我们add(Request)时, 是如何处理用户的Request的
public <T> Request<T> add(Request<T> request) { //设置这个Request加入了哪个队列中 request.setRequestQueue(this); //对mCurrentRequests加锁, 向Set(CurrentRequests)中加入该Request synchronized (mCurrentRequests) { mCurrentRequests.add(request); } //设置该Requet的序列 request.setSequence(getSequenceNumber()); request.addMarker("add-to-queue");//添加标记 // 会判断当前的请求是否可以缓存,如果不能缓存则在直接将这条请求加入网络请求队列 if (!request.shouldCache()) { mNetworkQueue.add(request); return request; } // 默认都是可以缓存的情况 synchronized (mWaitingRequests) { String cacheKey = request.getCacheKey(); //判断等待的请求中是否已存在当前request的CacheKey if (mWaitingRequests.containsKey(cacheKey)) { // 若存在则获取这个cacheKey的request集合 Queue<Request<?>> stagedRequests = mWaitingRequests.get(cacheKey); // 队列为null则新建一个集合 if (stagedRequests == null) { stagedRequests = new LinkedList<Request<?>>(); } // 将该request加入该集合 stagedRequests.add(request); // 更新这个Map中key和value的值 mWaitingRequests.put(cacheKey, stagedRequests); } else { // 若等待的请求Map中没有这个Request则先加入mWaitingRequests mWaitingRequests.put(cacheKey, null); // 再加入mCacheQueue队列 mCacheQueue.add(request); } return request; } }
注意mCacheQueue.add(request);这个方法, 非常重要, 默认情况下, 用户的Request请求是不会直接加入mNetworkQueue, 而是加入mCacheQueue。前面提到CacheDispatcher与NetworkDispatcher都是无限循环的,当mCacheQueue有Request加入时, 我们的CacheDispatcher该线程中的run会开始它的表演:
@Override public void run() { //设置该线程的优先级 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); //初始化本地缓存 mCache.initialize(); Request<?> request; //这里就是我们的无限循环 while (true) { request = null; try { request = mCacheQueue.take(); } catch (InterruptedException e) { if (mQuit) { return; } continue; } try { request.addMarker("cache-queue-take"); if (request.isCanceled()) { request.finish("cache-discard-canceled"); continue; } // 获取缓存值 Cache.Entry entry = mCache.get(request.getCacheKey()); // 缓存值为null, 说明该request尚未从网络上获取 if (entry == null) { request.addMarker("cache-miss"); //于是我们将该Request加入了mNetworkQueue队列中,执行网络加载 mNetworkQueue.put(request); continue; } // 缓存值不为空但是失效了, 也会将该Request加入mNetworkQueue队列中,执行网络加载 if (entry.isExpired()) { request.addMarker("cache-hit-expired"); request.setCacheEntry(entry); mNetworkQueue.put(request); continue; } // 缓存值有效 request.addMarker("cache-hit"); Response<?> response = request.parseNetworkResponse( new NetworkResponse(entry.data, entry.responseHeaders)); request.addMarker("cache-hit-parsed"); //判断是否需要更新缓存中的内容 if (!entry.refreshNeeded()) { // 通过mDelivery(即一个Handler关联了主线程的Looper)将该Response作为实参回调Request中的onResponse接口 mDelivery.postResponse(request, response); } else { ... //将request扔给mNetworkQueue,重新请求后再回调 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()); } }
可以看出一般情况下我们的NetworkDispatcher是在CacheDispatcher工作的过程中被动的执行的, 它处理网络获取Response成功后一样的通过mDelivery.postResponse回调主线程, 失败的话会回调postError(), 至此Volley请求网络的过程就结束了
是不是格外的清晰呢?
- Android-Volley源码分析
- [Android]volley源码分析
- Volley源码分析
- Volley源码分析
- Volley源码分析
- Volley -- 源码分析
- Android-Volley源码分析
- Volley源码流程分析
- Volley源码分析
- Volley源码个人分析
- Volley框架源码分析
- Volley源码分析一
- Volley 源码分析
- Volley源码分析二
- volley源码分析
- volley源码分析
- Volley(2)源码分析
- Volley 源码分析
- 环境变量错误导致Linux指令不可用
- 程序员的自信和骄傲
- SqlSession(四)
- Single Number III问题及解法
- Java中的集合
- Volley源码分析
- Python Web入门:Django学习与实践之三(models)
- Hibernate 之实体类之间的关系
- 埃氏筛法
- 【转】几种常用的优化方法
- springMVC的请求参数乱码问题
- 合并排序的非递归实现(自底向上设计)
- HDU 6170 / 多校1010 Two strings (dp)
- scikit-learn数据预处理fit_transform()与transform()的区别(转)