Volley源码分析

     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个小时的时间,自己前前后后也读了几遍,感觉有点啰嗦,提炼一句话就是:创建包含缓存分发器和网络分发器的请求队列,加入创建的请求,这个请求就会进入到不断轮询请求的两个分发器(缓存和网络)中,进而显示出相应的请求结果。

    感谢读到这里的朋友,如文章中有存在错误的地方或者不严谨之处,指出来;同时也对我的文章描述思路或者写作,提出改进的地方,不胜感激。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值