Vollery网络框架的源码分析
来源:互联网 发布:淘宝脱毛仪器有效果吗 编辑:程序博客网 时间:2024/06/06 00:54
在上一次我们采用了vollery网络请求的框架写了一个简单的图片加载的DemoVollery加载图片的小Demo ,如果想直接采用vollery进行网络的加载使用,可以直接使用该demo。对于想探求实现的底层原理的,我们这次就仔细的分析源码实现过程。
一、整体实现原理介绍
对于vollery的实现大致分为以下7个过程:
1、Request按照顺序添加到队列中,等待被轮循
2、Request先添加到CacheQueue中,排队等待被CacheDispatcher轮循
3、如果CacheQueue中有缓存,直接从缓存中解析出Response,通过消息处理器抛给主线程的监听器
4、如果本次请求的URL中本地没有缓存,那么就把Request添加到NetworkQueue中,排队等候被NetworkDispatcher轮循
5、Network中维护者4个NetworkDispatcher来轮循NetworkQueue
6、一旦有一个NetworkDispatcher轮循到结果就发起网络请求
7、发起请求后,将Response通过消息处理器抛给主线程的监听器
以上就是volleryqing请求的整体实现过程,下面我们进行详细讲解底层的实现过程。
二、创建RequestQueue的实例
我们在实际的开发中想要获取RequestQueue的实例,都是通过:
mQueue = Volley.newRequestQueue(getApplicationContext());
获取的(从上面的代码中我们可以看出RequestQueue采用单列设计模式),下面我们就从创建RequestQueue的实例的底层源码点击进去看看是干了什么?
public static RequestQueue newRequestQueue(Context context, HttpStack stack) { File cacheDir = new File(context.getCacheDir(), "volley");// // 指定缓存文件夹,这里默认采用data/data/包名/cache/volley作为缓存路径 String userAgent = "volley/0"; try { String network = context.getPackageName(); PackageInfo queue = context.getPackageManager().getPackageInfo(network, 0); userAgent = network + "/" + queue.versionCode; } catch (NameNotFoundException var6) { ; } if(stack == null) { if(VERSION.SDK_INT >= 9) { // 根据当前SDK版本选择Network,2.3以下使用HttpClient,2.3以上选用HttpURLConnection,面试的重点 stack = new HurlStack();//采用httpCollection请求 } else { stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));//采用HttpClient请求 } } BasicNetwork network1 = new BasicNetwork((HttpStack)stack);//构建BasicNetWork RequestQueue queue1 = new RequestQueue(new DiskBasedCache(cacheDir), network1);// 通过DiskBasedCache和BasicNetwork构建RequestQueue实例 queue1.start(); // 开启消息队列 return queue1; }
由上面的源码我们可以看出这里主要是完成3件事:
1、指定本地缓存的地址,默认的指定放在我们的data/data/包名/cache/volley目录下。
2、根据SDK的版本来指定不同的网络请求方式(重点)
3、通过DiskBasedCache和BasicNetwork构建RequestQueue实例
对于前面两步我们大家都比较好理解,第三步是我们探究源码的关键,Volley先构建了BasicNetwork和DiskBasedCache,然后将这两个对象作为参数传递给了RequestQueue,构建出了RequestQueue对象。紧接着,调用了RequestQueue.start()方法,开启了消息队列。我们来看下这个“RequestQueue.start()”中做了什么:
public void start() { this.stop();// 开启之前先停止所有的操作 this.mCacheDispatcher = new CacheDispatcher(this.mCacheQueue, this.mNetworkQueue, this.mCache, this.mDelivery);// 构建缓存线程,并开启缓存线程 this.mCacheDispatcher.start(); for(int i = 0; i < this.mDispatchers.length; ++i) {// 构建网络线程,默认构建了4个网络线程,并依次开启这4个网络线程 NetworkDispatcher networkDispatcher = new NetworkDispatcher(this.mNetworkQueue, this.mNetwork, this.mCache, this.mDelivery); this.mDispatchers[i] = networkDispatcher; networkDispatcher.start(); } }
在“RequestQueue.start()”中,我们先在start之前进行了stop操作,然后先后构建了缓存线程CacheDispatcher和网络线程NetworkDispatcher,并将它们开启了。
PS:为什么CacheDispatcher和NetworkDispatcher可以直接调用start()方法?
这是由于CacheDispatcher和NetworkDispatcher都是Thread的子类
public class CacheDispatcher extends Thread {.....}
public class NetworkDispatcher extends Thread {.....}
因此,调用CacheDispatcher或者NetworkDispatcher对象的start()方法,最终关键逻辑一定是走的是run()方法。下面我们就逐一的讲解这两者中run()方法的底层的具体实现。
三、CacheDispatcher源码解析
public void run() { if(DEBUG) { VolleyLog.v("start new dispatcher", new Object[0]); } Process.setThreadPriority(10); this.mCache.initialize(); while(true) {// 开启了一个死循环,不停地在缓存队列中去Request while(true) { while(true) { while(true) { try { final Request e = (Request)this.mCacheQueue.take();// 从缓存队列中取到Request消息 e.addMarker("cache-queue-take"); if(e.isCanceled()) {// 如果这个Request被取消了,则不做任何操作 e.finish("cache-discard-canceled"); } else { Entry entry = this.mCache.get(e.getCacheKey());// 根据Request获取缓存的数据实体对象 if(entry == null) {// 如果没有这个Request的缓存,则将这个Request加入到网络队列中 e.addMarker("cache-miss"); this.mNetworkQueue.put(e); } else if(!entry.isExpired()) {// 如果能取到Request的缓存,但是缓存已经过期了,也将Request加入到网络队列去 e.addMarker("cache-hit"); Response response = e.parseNetworkResponse(new NetworkResponse(entry.data, entry.responseHeaders));// 能获取到缓存,将缓存中存储的网络响应的原始数据(字节数组)转换成对应的响应数据 e.addMarker("cache-hit-parsed"); if(!entry.refreshNeeded()) { this.mDelivery.postResponse(e, response);// 将Request的响应数据Response分发到主线程 } else { e.addMarker("cache-hit-refresh-needed"); e.setCacheEntry(entry); response.intermediate = true; this.mDelivery.postResponse(e, response, new Runnable() { public void run() { try { CacheDispatcher.this.mNetworkQueue.put(e); } catch (InterruptedException var2) { ; } } }); } } else { e.addMarker("cache-hit-expired"); e.setCacheEntry(entry); this.mNetworkQueue.put(e); } } } catch (InterruptedException var4) { if(this.mQuit) { return; } } } } } } }
总结:
1、可以看出代码中主要是开启一个死循环不停地在缓存队列中去Request
2、取到Request之后,判断Request的状态是否是被取消的状态,若被取消,则不做任何处理
3、若Request没有被取消,则看是否有这个Request对应的缓存数据,若没有缓存数据,则将Request添加到网络队列中去
4、有缓存数据,则判断缓存数据是否已经过期,若数据过期,则添加Request到网络队列
5、若缓存数据没有过期,则将缓存的原始二进制数据解析成对应的Response数据,比如如果请求的是StringRequest就将二进制数据解析成String
6、将解析好的Response响应分发到主线程
那么是怎样将解析好的Response响应分发到主线程中的呢?我们点击源码查看一下:
@Overridepublic void postResponse(Request<?> request, Response<?> response, Runnable runnable) { request.markDelivered(); request.addMarker("post-response"); mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));}
而这个mResponsePoster实际上是一个线程池,它重写了自己的execute方法,在execute方法执行时,将对应的Runnable对象借助Handler分发到了主线程去执行:
ublic class ExecutorDelivery implements ResponseDelivery { /** Used for posting responses, typically to the main thread. */ private final Executor mResponsePoster; /** * Creates a new response delivery interface. * @param handler {@link Handler} to post responses on */ public ExecutorDelivery(final Handler handler) { // Make an Executor that just wraps the handler. mResponsePoster = new Executor() { @Override public void execute(Runnable command) { handler.post(command); } }; } ……}
因此,我们的Response响应最终会在主线程被调用方接收并进行处理。
四、NetworkDispatcher源码解析
@Overridepublic void run() { Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);// 开启死循环,不停的去队列中取Request while (true) { long startTimeMs = SystemClock.elapsedRealtime(); Request<?> request; try { // Take a request from the queue.// 从缓存队列中取到Request消息 request = mQueue.take(); } catch (InterruptedException e) { // We may have been interrupted because it was time to quit. if (mQuit) { return; } continue; } try { request.addMarker("network-queue-take"); // If the request was cancelled already, do not perform the // network request.// 如果这个Request被取消了,则不做任何操作 if (request.isCanceled()) { request.finish("network-discard-cancelled"); continue; } addTrafficStatsTag(request); // Perform the network request.// 传送请求,获取响应 NetworkResponse networkResponse = mNetwork.performRequest(request); request.addMarker("network-http-complete"); // If the server returned 304 AND we delivered a response already, // we're done -- don't deliver a second identical response. if (networkResponse.notModified && request.hasHadResponseDelivered()) { request.finish("not-modified"); continue; } // Parse the response here on the worker thread.//将网络响应的原始数据(字节数组)转换成对应的响应数据 Response<?> response = request.parseNetworkResponse(networkResponse); request.addMarker("network-parse-complete"); // Write to cache if applicable. // TODO: Only update cache metadata instead of entire record for 304s.// 缓存响应数据 if (request.shouldCache() && response.cacheEntry != null) { mCache.put(request.getCacheKey(), response.cacheEntry); request.addMarker("network-cache-written"); } // Post the response back. request.markDelivered();// 将响应的消息分发到主线程 mDelivery.postResponse(request, response); } catch (VolleyError volleyError) { volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs); parseAndDeliverNetworkError(request, volleyError); } catch (Exception e) { VolleyLog.e(e, "Unhandled exception %s", e.toString()); VolleyError volleyError = new VolleyError(e); volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs); mDelivery.postError(request, volleyError); } }}
总结:
1、NetworkDispatcher被开启的时候,内部实际上是维护这一个while(true)死循环,不停的从队列中取Request
2、取到Request之后,判断Request的状态是否是被取消的状态,若被取消,则不做任何处理
3、若Request没有被取消,则根据Request去执行网络请求,获取响应Response
4、将响应的原始二进制数据解析成对应的Response数据,比如如果请求的是StringRequest就将二进制数据解析成String
5、缓存响应的数据Response
6、将响应的信息分发到主线程
执行网络网络请求,获取Request对应的响应Response,调用的是“mNetwork.performRequest(request)”方法,实际上底层执行的是HurlStack或者HttpClientStack中的performRequest方法。若是执行HurlStack的performRequest方法,则底层其实是调用的HttpURLConnection;若是执行HttpClientStack的performRequest方法,则底层其实是调用的HttpConnection。分发Response到主线程的逻辑与CacheDispatcher中的分发逻辑完全一致。
至此,我们对于vollery框架的源码分析就结束了,希望对你有所帮助。
- Vollery网络框架的源码分析
- Android vollery框架的最基本使用
- Vollery网络获取
- SimpleNet网络框架源码分析
- kubernetes 网络框架源码分析
- okhttp网络框架源码分析
- 网络框架okHttp源码分析
- vollery的使用小计
- Vollery的理解
- Vollery框架请求网络去除Header中的IF-MODIFIED-SINCE头标签
- 淘宝网络框架tbnet源码分析
- Android网络框架源码分析一----Volley
- 网络访问框架源码分析--待完善
- android 网络框架volley源码分析
- Vollery的图片载入使用
- Vollery的简单二次封装
- Vollery 框架之StringRequest二次封装
- vollery框架使用及支持https
- JAVASE第4天笔记
- JAVASE第5天笔记
- 动态规划总结
- JAVASE第6天笔记
- install salt & initial configuration(centos7)
- Vollery网络框架的源码分析
- ExtJS框架 使用树控件TreeNode \ TreeLoader
- JAVASE第6天笔记
- JAVASE第7天笔记
- 将.csv格式转换成.txt
- Primary Master Configuration ( /etc/salt/master )
- JAVASE第8天笔记
- java几种基本的排序方法,快速排序,冒泡排序,选择排序,插入排序
- JAVASE第9天笔记