android中好的开源框架(volley okhttp retrofit glide rxjava dragger2 eventbus leakcanary butterknife)很多,但我总觉得自己在框架上花费些功夫,没有达到自己预想中的效果,或者是由于自己仅仅只是在项目中参照其他开发同学使用的模版,直接拿过来用;又或者是自己想提升下自己的技能,在各大技术社区看大牛们的源码分享技术文章,一篇一篇的接着看下去,好的情况,看得明白,心里暗自窃喜,这个网络框架的源码我懂了,以后不管是在同事面前和面试官面前,都可以吹一波,谁不知道,等你一个月之后,让你自己再讲一遍,也就说出个百分之二三十;更糟糕的情况,有些原理性较强或者需要扎实理论基础的开源框架,看技术博客,开始你都会看不太懂,(当然,不乏有很多技术大牛的文章,文笔流畅,思路清晰),我记得我看retrofit rxjava,各种操作符,看得我心情很糟糕,看不明白。但是要成为一名合格的程序员,离不开这些开源框架的使用,提高我们在移动项目中的开发效率,同时深入分析这些源代码,里面用到的设计模式、巧妙的流程设计,更快的提高我们自身的技能,更重要的是当领略到框架中用到的设计模式及流程,你会惊叹于设计者的牛叉,进而激励自己学以致用。
一句话来总结就是:结合项目中使用,及遇到的问题,再对框架的源码进行一一剖析,通过流程图、XML类结构图来梳理整个框架的设计,并通过技术文章的形式记录下来,自己对框架的理解和吸收,会更加透彻。
接下来就正式进入正题:volley的源码分析。
先来走一遍volley的流程图:
Volley设计图:
一:volley的简单使用
1//创建请求队列
2 RequestQueue queue = Volley.newRequestQueue(this);
3
4 //创建请求(ImageRequest StringRequest JsonObjectRequest JsonArrayRequest)
5 StringRequest stringRequest = new StringRequest("https://www.baidu.com/",
6 new Response.Listener<String>() {//网络请求成功的回调
7 @Override
8 public void onResponse(String response) {
9 //此方法工作在UI线程,可直接进行UI数据更新,通过获取主线程的handler来实现
10 Log.i("MainActivity","response = " + response);
11 }
12 }
13 , new Response.ErrorListener() {
14 @Override
15 public void onErrorResponse(VolleyError error) {//网络请求失败的回调
16 Log.i("MainActivity","error = " + error.getMessage());
17 }
18 });
19 //请求添加到请求队列
20 queue.add(stringRequest);
二:逐一分析
1.创建请求队列,通过Volley.newRequestQueue(this)来创建,进入源码可知,会走到带有三个参数的方法,如下:
1public static RequestQueue newRequestQueue(Context context, HttpStack stack, int maxDiskCacheBytes) {
2 //Volley网络请求缓存数据的路径(data/data/包名/cache/volley)
3 File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);
4
5 String userAgent = "volley/0";
6 try {
7 String packageName = context.getPackageName();
8 PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);
9 userAgent = packageName + "/" + info.versionCode;
10 } catch (NameNotFoundException e) {
11 }
12
13 if (stack == null) {
14 if (Build.VERSION.SDK_INT >= 9) {
15 //构建HttpURLConnection的网络请求封装
16 stack = new HurlStack();
17 } else {
18 //android sdk 9之前,基于HttpClient来进行网络请求封装
19 HttpClientStack(AndroidHttpClient.newInstance(userAgent));
20 }
21 }
22 //封装了得网络请求
23 Network network = new BasicNetwork(stack);
24
25 RequestQueue queue;
26 if (maxDiskCacheBytes <= -1)
27 {
28 // 使用默认的缓存大小,5*1024*1024 Byte=5MB
29 queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
30 }
31 else
32 {
33 // 指定了缓存大小
34 queue = new RequestQueue(new DiskBasedCache(cacheDir, maxDiskCacheBytes), network);
35 }
36 //队列开始执行,接下来分析这里面的操作
37 queue.start();
38
39 return queue;
40}
简而言之,上面这个方法做的事情就是:1).创建网络请求响应数据的缓存路径;2).根据android sdk版本来创建基于HttpClient还是HttpURLConnection的网络封装类NetWork;3).基于前面两点来创建请求队列;4).请求队列开始执行。
2.请求队列开始执行
1 public void start() {
2 stop(); // Make sure any currently running dispatchers are stopped.
3
4 // 缓存分发器
5 mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
6 mCacheDispatcher.start();
7
8 // 网络分发器,这里的fo循环对应的是线程池
9 for (int i = 0; i < mDispatchers.length; i++) {
10 NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,
11 mCache, mDelivery);
12 mDispatchers[i] = networkDispatcher;
13 networkDispatcher.start();
14 }
15}
3.缓存分发器和网络分发器
缓存分发器,顾名思义就是从缓存中去拿请求的数据,1)缓存中没有,加入网络请求队列;2)缓存过期,加入网络请求队列;3)缓存中有值且无需刷新,转发缓存中的数据;4)缓存中有值,且需要刷新,转发缓存数据,且加入网络请求队列进行数据刷新。
1public void run() {
2 if (DEBUG) VolleyLog.v("start new dispatcher");
3 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
4 // Make a blocking call to initialize the cache.
5 mCache.initialize();
6
7 Request<?> request;
8 while (true) {
9 // release previous request object to avoid leaking request object when mQueue is drained.
10 request = null;
11 try {
12 // 从缓存队列中取相应的请求
13 request = mCacheQueue.take();
14 } catch (InterruptedException e) {
15 // We may have been interrupted because it was time to quit.
16 if (mQuit) {
17 return;
18 }
19 continue;
20 }
21 try {
22 request.addMarker("cache-queue-take");
23
24 // If the request has been canceled, don't bother dispatching it.
25 if (request.isCanceled()) {
26 request.finish("cache-discard-canceled");
27 continue;
28 }
29
30 // 从缓存中根据key来取对应的缓存数据,其中key是由"mMethod + ':' + mUrl"来组成
31 Cache.Entry entry = mCache.get(request.getCacheKey());
32 if (entry == null) {
33 request.addMarker("cache-miss");
34 // 缓存命中失败,加入网络请求队列
35 mNetworkQueue.put(request);
36 continue;
37 }
38
39 // 缓存已过期,加入网络请求队列
40 if (entry.isExpired()) {
41 request.addMarker("cache-hit-expired");
42 request.setCacheEntry(entry);
43 mNetworkQueue.put(request);
44 continue;
45 }
46
47 // 缓存命中,进行数据解析
48 request.addMarker("cache-hit");
49 Response<?> response = request.parseNetworkResponse(
50 new NetworkResponse(entry.data, entry.responseHeaders));
51 request.addMarker("cache-hit-parsed");
52
53 if (!entry.refreshNeeded()) {
54 // 无须刷新缓存命中,回传数据,通过获取主线程的handler进行回传,在UI线程上进行数据UI更新
55 mDelivery.postResponse(request, response);
56 } else {
57 // 缓存命中且需要刷新,回传缓存数据?(这里有疑问)且同时进行网络请求刷新数据
58 request.addMarker("cache-hit-refresh-needed");
59 request.setCacheEntry(entry);
60
61 // Mark the response as intermediate.
62 response.intermediate = true;
63
64 // Post the intermediate response back to the user and have
65 // the delivery then forward the request along to the network.
66 final Request<?> finalRequest = request;
67 mDelivery.postResponse(request, response, new Runnable() {
68 @Override
69 public void run() {
70 try {
71 mNetworkQueue.put(finalRequest);
72 } catch (InterruptedException e) {
73 // Not much we can do about this.
74 }
75 }
76 });
77 }
78 } catch (Exception e) {
79 VolleyLog.e(e, "Unhandled exception %s", e.toString());
80 }
81 }
82}
网络分发器,顾名思义,通过线程池不断的去查询网络请求队列,进行网络请求。1)执行网络请求,得到网络回传的数据。2)如需缓存,把解析后的数据进行缓存处理,并通过主线程的handler进行数据回传,最后走到mListener.onResponse(response)中。
1public void run() {
2 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
3 Request<?> request;
4 while (true) {
5 //省略部分代码
6 // 执行网络请求
7 NetworkResponse networkResponse = mNetwork.performRequest(request);
8 request.addMarker("network-http-complete");
9
10 // If the server returned 304 AND we delivered a response already,
11 // we're done -- don't deliver a second identical response.
12 if (networkResponse.notModified && request.hasHadResponseDelivered()) {
13 request.finish("not-modified");
14 continue;
15 }
16
17 // 解析网络响应数据
18 Response<?> response = request.parseNetworkResponse(networkResponse);
19 request.addMarker("network-parse-complete");
20
21 // 如需缓存,写入缓存
22 if (request.shouldCache() && response.cacheEntry != null) {
23 mCache.put(request.getCacheKey(), response.cacheEntry);
24 request.addMarker("network-cache-written");
25 }
26
27 // 回传网络请求回来的数据,通过获取主线程的handler进行回传,在UI线程上进行数据UI更新
28 request.markDelivered();
29 mDelivery.postResponse(request, response);
30 } catch (VolleyError volleyError) {
31 volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
32 parseAndDeliverNetworkError(request, volleyError);
33 } catch (Exception e) {
34 //省略部分代码
35 mDelivery.postError(request, volleyError);
36 }
37 }
38}
4.添加构建的请求到请求队列中去
该请求无需缓存,直接添加到网络请求队列中去;该请求已经在等待的队列中,排队等候;否则加入等待的队列中,并加入缓存队列。
1public <T> Request<T> add(Request<T> request) {
2 //省略部分代码
3 // 该请求无需缓存,直接添加到网络队列
4 if (!request.shouldCache()) {
5 mNetworkQueue.add(request);
6 return request;
7 }
8
9 // 如果已经有一个正在运行的具有相同缓存键的请求,则将请求插入到stage。
10 synchronized (mWaitingRequests) {
11 String cacheKey = request.getCacheKey();
12 if (mWaitingRequests.containsKey(cacheKey)) {
13 // 已经有一个请求,排队
14 Queue<Request<?>> stagedRequests = mWaitingRequests.get(cacheKey);
15 if (stagedRequests == null) {
16 stagedRequests = new LinkedList<Request<?>>();
17 }
18 stagedRequests.add(request);
19 mWaitingRequests.put(cacheKey, stagedRequests);
20 if (VolleyLog.DEBUG) {
21 VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold.", cacheKey);
22 }
23 } else {
24 // 加入等待队列,并加入缓存队列
25 // flight.
26 mWaitingRequests.put(cacheKey, null);
27 mCacheQueue.add(request);
28 }
29 return request;
30 }
31}
Volley的流程到这就结束了,这篇文章花费了将近4个小时的时间,自己前前后后也读了几遍,感觉有点啰嗦,提炼一句话就是:创建包含缓存分发器和网络分发器的请求队列,加入创建的请求,这个请求就会进入到不断轮询请求的两个分发器(缓存和网络)中,进而显示出相应的请求结果。
感谢读到这里的朋友,如文章中有存在错误的地方或者不严谨之处,指出来;同时也对我的文章描述思路或者写作,提出改进的地方,不胜感激。