Volley源码解析(一)

这个章节,我先介绍volley功能介绍、总体设计、类设计、主要流程的源码解析,其他方法,例如图片加载、HurlStack、HttpClientStack、performRequest等,我会在后面进行介绍。

1、功能介绍
1.1 volley是异步网络请求框架和图片加载器。
1.2 优点
1、请求队列和请求优先级。
2、请求Cache和内存管理。
3、扩展性强,大多是基于接口的设计,可配置型强。
4、可以取消请求。
5、提供简单的图片加载工具。

2、总体设计
2.1 总体设计图如下:
这里写图片描述
通过Dispatch Thread不断从RequestQueue中获取请求,根据是否已缓存调用Cache或者Network这两类数据获取接口其中之一,从内存缓存或者是服务器中获取数据,然后交给ResponseDelivery去做结果分发以及回调处理。

2.2 核心功能点概念
volley使用,通过newRequestQueue()函数新建并启动一个请求队列RequestQueue,然后不断向这个队列中添加Request就可以了。
Volley:对外暴露的API,通过newRequestQueue()函数新建并启动一个请求队列RequestQueue。
Request:表示一个请求的抽象类。StringRequest、JsonRequest、ImageRequest都是它的子类,表示某种类型的请求。
RequestQueue:请求队列,里面包含一个CacheDispatcher(缓存请求的线程)、多个NetworkDispatcher(处理网络请求的线程),start函数启动CacheDispatcher和NetworkDispatcher。
CacheDispatcher:用于处理缓存请求。不断从缓存请求队列中获取请求处理,队列为空的时候则等待,请求处理结束后将结果传递给ResponseDelivery去执行后续处理。当结果没有被缓存过、缓存失效等情况下,这个请求都要重新进入NetworkDispatcher去调度处理。
NetworkDispatcher:用于处理网络请求。不断从网络请求队列中获取请求处理,队列为空的时候则等待,请求结果传递给ResponseDelivery去执行后续处理,并判断结果是否要进行缓存。
ResponseDelivery:返回结果分发接口。
HttpStack:处理Http请求,返回请求结果。目前Volley中有基于HttpURLConnection的HurlStack和基于Apache HttpClient的HttpClientStack。
Network:调用HttpStack处理请求,将结果转化为可以被ResponseDelivery处理的NetworkResponse。
Cache:缓存请求结果,默认使用sdcard的DiskBasedCache。NetworkDispatcher得到结果后判断是否需要存在Cache,CacheDispatcher会从Cache中获取缓存结果。
2.3 流程图
这里写图片描述

3、类设计图
这里写图片描述
红色部分,围绕RequestQueue类,将各个功能点以组合
形式结合在一起。每个功能点(Request,CacheDispatcher,NetworkdDispatcher),都是以接口或者抽象类的形式提供。红色外面的部分,为功能点提供具体实现。
多用组合,少用继承;针对接口编程,不针对具体实现编程。

4、主要流程源码解析
4.1.1
使用volley的时候,需要创建一个请求队列RequestQueue,最好在AppApplication中声明一个全局的RequestQueue,使用方法如下:

mRequestQueue = Volley.newRequestQueue(getApplicationContext());

我们看一下源码中Volley中的newRequestQueue方法,就是新建一个RequestQueue类对象,如下:

RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
queue.start();

这段代码,将缓存对象和网络对象加入到RequestQueue队列中(其中DiskBasedCache是Cache的具体实现类,BasicNetWork是NetWork的具体实现类),RequestQueue中第二个参数network实现如下:

if (stack == null) {
            if (Build.VERSION.SDK_INT >= 9) {
                stack = new HurlStack();
            } else {
               stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
            }
     }
Network network = new BasicNetwork(stack);

代码中可以看出来,当API>=9的时候,使用HurlStack,反之,使用HttpClientStack。关于HurlStack和HttpClientStack基于什么来实现的,我会在下一章中做详细介绍。

4.1.2
添加request,使用方法如下:

mRequestQueue.add(request);

我们看一下源码中RequestQueue中的add方法。

if (!request.shouldCache()) {
            mNetworkQueue.add(request);
            return request;}

首先判断当前请求是否可以缓存,如果不能直接添加到网络请求队列,如果可以缓存,就加入缓存队列。
然后判断等待请求mWaitingRequests和添加的request是否有相同的cachekey,如果有的话,将这个request加入具有相同cachekey的等待队列,如下:

stagedRequests.add(request);//  stagedRequests是具有cachekey的队列
mWaitingRequests.put(cacheKey, stagedRequests);//mWaitingRequests是            
                                              //map<String,Queue>类型

如果没有的话,就为cachekey添加一个为null的队列,加入缓存队列mCacheQueue,相当于在内存中重新添加这个request,代码如下:

mWaitingRequests.put(cacheKey, null);
mCacheQueue.add(request);

其中mCacheQueue和mNetworkQueue分别代表缓存请求队列和网络请求队列,mWaitingRequests是一个等待请求的集合,如果一个请求可以被缓存,并且有相同的cachekey,就可以全部进入此等待队列。

4.2 创建JSON请求
volley自带JsonObjectRequest和JsonArrayRequest来处理JSON对象请求和JSON数据请求。创建JsonObjectRequest对象,写好response回调接口,把这个请求放到请求队列中就可以了,使用方法如下(JsonArrayRequest使用方法也类似):

JsonObjectRequest jsonObjReq = new JsonObjectRequest(Method.GET,url, null,
            new Response.Listener<JSONObject>() {
        @Override
        public void onResponse(JSONObject response) {
                Log.d(TAG, response.toString());
             }
        }, new Response.ErrorListener() {
        @Override
        public void onErrorResponse(VolleyError error) {
               VolleyLog.d(TAG, "Error: " + error.getMessage());
             }
        });
mRequestQueue.add(jsonObjReq, tag_json_obj);

我们从源码分析JsonObjectRequest.java文件,JsonObjectRequest继承Response.java,就是一个结构体,里面包含了url,请求方式,onResponse和errorResponse回调。

4.3 创建String请求
volley中有StringRequest类,用来请求string类型的数据,使用方法如下:

StringRequest strReq = new StringRequest(Method.GET,
            url, new Response.Listener<String>() {
                @Override
                public void onResponse(String response) {
                    Log.d(TAG, response.toString());
                    pDialog.hide();
                }
            }, new Response.ErrorListener() {
                @Override
                public void onErrorResponse(VolleyError error) {
                    VolleyLog.d(TAG, "Error: " + error.getMessage());
                    pDialog.hide();
                }
            });
mRequestQueue.add(strReq, tag_json_obj);

StringRequest.java跟JsonRequest结构一样。

4.4 创建POST请求
上面都是GET请求,下面是POST请求,需要将请求类型改为POST类型,并且override Request中的getParams方法就可以了,使用方法如下:

JsonObjectRequest jsonObjReq = new JsonObjectRequest(Method.POST,
            url, null,
            new Response.Listener<JSONObject>() {
                @Override
                public void onResponse(JSONObject response) {
                    Log.d(TAG, response.toString());
                    pDialog.hide();
                }
            }, new Response.ErrorListener() {
                @Override
                public void onErrorResponse(VolleyError error) {
                    VolleyLog.d(TAG, "Error: " + error.getMessage());
                    pDialog.hide();
                }
            }) {

        @Override
        protected Map<String, String> getParams() {
            Map<String, String> params = new HashMap<String, String>();
            params.put("name", "Androidhive");
            params.put("email", "abc@androidhive.info");
            params.put("password", "password123");
            return params;
        }
    };

这里通过重写Request.java中的getParams方法,将POST中的参数传入到request中,类型是Map

JsonObjectRequest jsonObjReq = new JsonObjectRequest(Method.POST,url, null,new Response.Listener<JSONObject>() {
    @Override
    public void onResponse(JSONObject response) {
        Log.d(TAG, response.toString());
        pDialog.hide();
    }
}, new Response.ErrorListener() {
    @Override
    public void onErrorResponse(VolleyError error) {
        VolleyLog.d(TAG, "Error: " + error.getMessage());
        pDialog.hide();
    }
}) {

@Override
public Map<String, String> getHeaders() throws AuthFailureError {
    HashMap<String, String> headers = new HashMap<String, String>();
    headers.put("Content-Type", "application/json");
    headers.put("apiKey", "xxxxxxxxxxxxxxx");
    return headers;
}
};

这里通过重写Request.java中的getHeaders方法,将POST中的参数传入到request中,类型是Map

Cache=mRequestQueue.getCache();
Entry entry = cache.get(url);
if(entry != null){
    try {
        String data = new String(entry.data, "UTF-8");
    } catch (UnsupportedEncodingException e) {      
        e.printStackTrace();
        }
    }
}else{
    // Cached response doesn't exists. Make network call here
}

2)请求缓存失效

mRequestQueue.getCache().invalidate(url, true);

3)关闭cache

StringRequest stringReq = new StringRequest(....);
stringReq.setShouldCache(false);//true就是打开cache

4)将URL的cache删除

mRequestQueue.getCache().remove(url);

5)删除所有cache

mRequestQueue.getCache().clear();

4.8 请求优先级
优先级分为:Normal, Low, Immediate, High

private Priority priority = Priority.HIGH;
StringRequest strReq = new StringRequest(Method.GET,
            Const.URL_STRING_REQ, new Response.Listener<String>() {
                @Override
                public void onResponse(String response) {
                    Log.d(TAG, response.toString());
                    msgResponse.setText(response.toString());
                    hideProgressDialog();

                }
            }, new Response.ErrorListener() {
                @Override
                public void onErrorResponse(VolleyError error) {
                    VolleyLog.d(TAG, "Error: " + error.getMessage());
                    hideProgressDialog();
                }
            }) {
        @Override
        public Priority getPriority() {
            return priority;
        }
    };

4.9 取消请求
addToRequestQueue(request, tag)方法还接受一个tag参数,这个tag就是用来标记某一类请求的,这样就可以取消这个tag的所有请求了。

mRequestQueue.cancelAll("feed_request");

4.10 RequestQueue的start方法
源码如下:

public void start() {
        stop();  // Make sure any currently running dispatchers are stopped.
        // Create the cache dispatcher and start it.
        mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
        mCacheDispatcher.start();

        // Create network dispatchers (and corresponding threads) up to the pool size.
        for (int i = 0; i < mDispatchers.length; i++) {
            NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,
                    mCache, mDelivery);
            mDispatchers[i] = networkDispatcher;
            networkDispatcher.start();
        }
    }

CacheDispatcher是缓存分发器,只有一个,NetworkDispatcher是网络分发器,有多个,然后我再去看一下CacheDispatcher分发器中的start方法,CacheDispatcher继承的Thread,所以直接看run方法,代码如下(注释我写在代码上面):

public void run() {
        if (DEBUG) VolleyLog.v("start new dispatcher");
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);

        // Make a blocking call to initialize the cache.
        mCache.initialize();

        while (true) {
            try {               
                final Request<?> request = mCacheQueue.take();
                request.addMarker("cache-queue-take");

                if (request.isCanceled()) {
                    request.finish("cache-discard-canceled");
                    continue;
                }

                // Attempt to retrieve this item from cache.
                //尝试从缓存中获取响应结果
                Cache.Entry entry = mCache.get(request.getCacheKey());
                //如果缓存中获取结果为空的话,就把这个请求放到网络请求队列中
                if (entry == null) {
                    request.addMarker("cache-miss");
                    // Cache miss; send off to the network dispatcher.
                    mNetworkQueue.put(request);
                    continue;
                }

                //如果缓冲结果不为空的话,判断缓存是否过期
                //如果过期的话,将这个请求放到网络请求队列中.
                if (entry.isExpired()) {
                    request.addMarker("cache-hit-expired");
                    request.setCacheEntry(entry);
                    mNetworkQueue.put(request);
                    continue;
                }
                //直接使用缓存中的数据
                request.addMarker("cache-hit");
                //parseNetworkResponse对数据进行解析
                Response<?> response = request.parseNetworkResponse(
                        new NetworkResponse(entry.data, entry.responseHeaders));
                request.addMarker("cache-hit-parsed");

                //将解析出来的数据进行回调.
                if (!entry.refreshNeeded()) {
                    // Completely unexpired cache hit. Just deliver the response.
                    mDelivery.postResponse(request, response);
                } else {
                    request.addMarker("cache-hit-refresh-needed");
                    request.setCacheEntry(entry);
                    response.intermediate = true;

                    mDelivery.postResponse(request, response, new Runnable() {
                        @Override
                        public void run() {
                            try {
                                mNetworkQueue.put(request);
                            } catch (InterruptedException e) {
                                // Not much we can do about this.
                            }
                        }
                    });
                }

            } catch (InterruptedException e) {                
                if (mQuit) {
                    return;
                }
                continue;
            }
        }

其中cache结构体中的内容,如上面介绍的cache机制的方法向cache中添加信息和内容。
同理,networkDispatcher中run方法源码如下:

 public void run() {
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
        while (true) {
            long startTimeMs = SystemClock.elapsedRealtime();
            Request<?> request;
            try {
                request = mQueue.take();
            } catch (InterruptedException e) {
                if (mQuit) {
                    return;
                }
                continue;
            }

            try {
                request.addMarker("network-queue-take");
                if (request.isCanceled()) {
                    request.finish("network-discard-cancelled");
                    continue;
                }
                addTrafficStatsTag(request);
                //发送网络请求
                NetworkResponse networkResponse = mNetwork.performRequest(request);
                request.addMarker(“network-http-complete");

                if (networkResponse.notModified && request.hasHadResponseDelivered()) {
                    request.finish("not-modified");
                    continue;
                }

            // 在子线程中解析,在Request子类中parseNetworkResponse方法中实现解析
                Response<?> response = request.parseNetworkResponse(networkResponse);
                request.addMarker(“network-parse-complete");

               if (request.shouldCache() && response.cacheEntry != null) {
                    mCache.put(request.getCacheKey(), response.cacheEntry);
                    request.addMarker("network-cache-written");
                }
                // response回调.
                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);
            }
        }
    }

代码比较多,但是真正网络请求的核心代码应该是

NetworkResponse networkResponse = mNetwork.performRequest(request);

最核心的是performRequest方法,具体代码分析我放到下一章节。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值