《Volley源码分析》Part2、Volley源码解析

来源:互联网 发布:成都网络推广公司排名 编辑:程序博客网 时间:2024/06/08 13:28

Volley是什么,怎么用,这里就不做介绍了,大家可以看我的关于Volley的使用以及介绍一文,上面是翻译官方介绍的,并且自己附加了实例代码程序。

1、整体流程整理

根据我们创建RequestQueue的过程,首先看Volley#newRequestQueue方法。

public static RequestQueue newRequestQueue(Context context, BaseHttpStack stack) {    BasicNetwork network;    if (stack == null) {        if (Build.VERSION.SDK_INT >= 9) { //在sdk版本小于9的时候使用httpClient,否则使用httpUrlConnection            network = new BasicNetwork(new HurlStack());        } else {            // Prior to Gingerbread, HttpUrlConnection was unreliable.            // See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html            // At some point in the future we'll move our minSdkVersion past Froyo and can            // delete this fallback (along with all Apache HTTP code).            String userAgent = "volley/0";            try {                String packageName = context.getPackageName();                PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);                userAgent = packageName + "/" + info.versionCode;            } catch (NameNotFoundException e) {            }            network = new BasicNetwork(                    new HttpClientStack(AndroidHttpClient.newInstance(userAgent)));        }    } else {        network = new BasicNetwork(stack);    }    return newRequestQueue(context, network);}

如果BaseHttpStack为空的话,会根据系统版本进行区分,sdk小于9的话,会使用httpClient,否则的话,会使用httpUrlConnection。BashHttpStack是默认的网络请求处理逻辑,Volley支持用户自定义网络处理逻辑。BasicNetwork是对网络请求的一个统一封装,它负责处理真实网络请求之后获取的结果的分发处理。

在上面代码中最后调用了newRequestQueue(context, network);,也就是下面这个方法。

private static RequestQueue newRequestQueue(Context context, Network network) {    File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);    RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);    queue.start();    return queue;}

newRequestQueue中,构造了一个RequestQueue对象,它的两个参数分别是CachenetWork,可以看到这里Cache的默认实现是使用的DiskBasedCache,而network的默认实现就是上面创建的BasicNetwork。之后调用了RequestQueue#start(),在看这个方法做了什么。

//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();    }}//RequestQueue#stop()public void stop() {    if (mCacheDispatcher != null) {        mCacheDispatcher.quit();    }    for (final NetworkDispatcher mDispatcher : mDispatchers) {        if (mDispatcher != null) {            mDispatcher.quit();        }    }}

可以看到这里是构造了CacheDispatcher对象和NetworkDispatcher对象,其实CacheDispatcher和NetworkDispatcher都是线程,继承自Thread。在start中,首先调用了stop方法,对线程进行了停止。之后创建了CacheDispatcher对象,并且传入了mCacheQueuemNetworkQueuemCachemDelivery,分别是缓存队列,网络请求队列,缓存对象和结果分发对象。其实缓存队列和网络请求队列都是使用的阻塞队列实现,而NetworkDispatcher的创建是根据mDispatchers数组的大小进行创建,而这个队列默认初始化是4,所以会创建四个用于网络请求的线程。并且在最后对缓存线程和网络请求线程都调用了start(),让线程进入运行状态,而由于这里使用了阻塞队列,因此这两个线程都是会在没有任务的时候一直阻塞,直到有任务到达或者调用了quit退出。

从看到代码我们知道在缓存线程(CacheDispatcher)和网络请求线程(NetworkDispatcher)中,都传入了一个mDispatchers,所以在研究请求到来的流程之前,先看看这个结果分发类做了什么。

public interface ResponseDelivery {    /**     * Parses a response from the network or cache and delivers it.     */    void postResponse(Request<?> request, Response<?> response);    /**     * Parses a response from the network or cache and delivers it. The provided     * Runnable will be executed after delivery.     */    void postResponse(Request<?> request, Response<?> response, Runnable runnable);    /**     * Posts an error for the given request.     */    void postError(Request<?> request, VolleyError error);}

可以看到ResponseDelivery是一个接口,内部实现了对请求结果的分发和错误分发。(在Volley中,大量的使用到了接口变成,让代码的耦合清晰明了,简直经典)而ResponseDelivery的真实实现类是ExecutorDelivery,可以在

public RequestQueue(Cache cache, Network network, int threadPoolSize) {    this(cache, network, threadPoolSize,            new ExecutorDelivery(new Handler(Looper.getMainLooper())));}

中找到。并且可以看到传入了一个Handler,并且这个Handler是使用MainLooper创建的,也就是这个Handler发送消息会传到主线程哦。可以猜测,最后的从任务线程切换到主线程就是这个Handler处理的。

而在ExecutorDelivery中的postResponse和postError中,结果和错误实际上交给了ExecutorDelivery#ResponseDeliveryRunnable类处理,这个类是作为一个handler的消息处理的,最后根据请求是否有错误进行了分发。会传到了request的deliverResponse和deliverError,最后在Request中设置的监听会调用到最后用户处理。

if (mResponse.isSuccess()) {    mRequest.deliverResponse(mResponse.result);} else {    mRequest.deliverError(mResponse.error);}

到目前我们知道了RequestQueue做了什么操作,这里简单总结一下:

RequestQueue中创建了1个缓存线程和4个网络请求线程,以及一个结果回传分发任务,并且对缓存线程和网络请求线程都进行了运行,让他们一直处于阻塞等待状态。当有任务加入到mCacheQueue或者mNetworkQueue中的时候,线程就会开始运行处理逻辑了。而ResponseDelivery作为结果分发类,它的对象被传入了缓存线程和网络请求任务线程中,在ResponseDelivery中分别有正确结果分发和错误分发操作。

你现在可能很急于知道当有一个request加入到RequestQueue的时候,具体做了什么操作?下面马上就说这部分内容。

Volley内建提供了StringRequest、JsonObjectRequest、JsonArrayRequest,好像最近也加入了GsonRequest(因为看isuue中有提到)。他们其实都是Request<T>的子类。

当有一个Request发起请求时,只需要调用RequestQueue的public <T> Request<T> add(Request<T> request)方法即可。下面看下这个方法。

public <T> Request<T> add(Request<T> request) {        // Tag the request as belonging to this queue and add it to the set of current requests.    request.setRequestQueue(this);    synchronized (mCurrentRequests) {        mCurrentRequests.add(request);    }    // Process requests in the order they are added.    request.setSequence(getSequenceNumber());    request.addMarker("add-to-queue");    // If the request is uncacheable, skip the cache queue and go straight to the network.    if (!request.shouldCache()) {        mNetworkQueue.add(request);        return request;    }    mCacheQueue.add(request);    return request; }

从代码可以看到,首先会给request绑定一个requestQueue,之后加入到mCurrentRequests队列中,这个mCurrentRequests队列在这里添加之后,会在finish和cancel的时候移除,实际上感觉是维系了一个请求队列的整体描述队列吧,方便在RequestQueue中找到要处理的request。之后判断是否需要做缓存处理,Volley默认shouldCache是true。因此会先添加到mCacheQueue中。那么我们就来看看CacheDispatcher的run方法中做了什么处理吧。

这里代码还是比较多的,我把Volley自己添加日志打点的代码去掉了,并对格式缩行了。可以先看一下代码,代码加了注释。

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();  //这里是对cache的初始化,存贮路径不存在的话,会创建缓存文件存贮路径,如果存在的的话则会扫描所有文件,映射到一个map中。    while (true) {  //这里构造了一个死循环,退出条件是线程出现中断。(报InterruptedException)        try {            // Get a request from the cache triage queue, blocking until            // at least one is available.            //这里是从缓存队列中取出来request,因为mCacheQueue是一个阻塞队列,因此这里如果队列为空的话, take会一直阻塞,直到有Request来。            final Request<?> request = mCacheQueue.take();              // If the request has been canceled, don't bother dispatching it.            if (request.isCanceled()) {  //如果取消了的话,则调用request.finish将自己从RequestQueue中移除。并跳过此次任务。                request.finish("cache-discard-canceled");                continue;            }            // Attempt to retrieve this item from cache.            Cache.Entry entry = mCache.get(request.getCacheKey());            //从缓存中取出来缓存对象,如果不存在,判断这个请求是否已经有相同缓存key值得请求在执行中(in flight),没有的话,则添加到mNetworkQueue中,执行网络请求。(对于这块理解可能不准确,欢迎讨论)            if (entry == null) {                request.addMarker("cache-miss");                // Cache miss; send off to the network dispatcher.                if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) {                    mNetworkQueue.put(request);                }                continue;            }            // If it is completely expired, just send it to the network.            if (entry.isExpired()) {                request.setCacheEntry(entry);                if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) {                    mNetworkQueue.put(request);                }                continue;            }            // We have a cache hit; parse its data for delivery back to the request.            //从缓存中解析出来Response,之后调用mDelivery进行分发。            Response<?> response = request.parseNetworkResponse(                    new NetworkResponse(entry.data, entry.responseHeaders));            if (!entry.refreshNeeded()) {                // Completely unexpired cache hit. Just deliver the response.                mDelivery.postResponse(request, response);            } else {                // Soft-expired cache hit. We can deliver the cached response,                // but we need to also send the request to the network for refreshing.                request.setCacheEntry(entry);                // Mark the response as intermediate.                response.intermediate = true;                if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) {                    // Post the intermediate response back to the user and have                    // the delivery then forward the request along to the network.                    mDelivery.postResponse(request, response, new Runnable() {                        @Override                        public void run() {                            try {                                mNetworkQueue.put(request);                            } catch (InterruptedException e) {                                // Restore the interrupted status                                Thread.currentThread().interrupt();                            }                        }                    });                } else {                    // request has been added to list of waiting requests                    // to receive the network response from the first request once it returns.                    mDelivery.postResponse(request, response);                }            }        } catch (InterruptedException e) {            // We may have been interrupted because it was time to quit.            if (mQuit) {                return;            }        }    }}

代码还是很清晰的,简单总结一下:

拿到一个request之后,首先判断这个请求是否存在缓存,是否过期,如果过期的话,并且没有正在运行的相同key的任务,就添加到网络请求中,进行请求。如果有缓存的话,则调用parseNetworkResponse解析出来一个Response对象,并调用mDelivery进行结果的分发。

再看一下NetworkDispatcher的流程吧。

这里列出来核心代码

NetworkResponse networkResponse = mNetwork.performRequest(request);  //实际上是调用了networkStact的performRequest,....Response<?> response = request.parseNetworkResponse(networkResponse);  //解析请求到的结果

它的代码相比简单很多,因为真实的请求是BaseHttpStack以及请求封装类BasicNetworky。

自己整理的Volley请求流程图,看着可能比较乱,可以先简单看一下,阅读完文字分析之后再看一下,应该会比较容易能理解整体流程。

image

原创粉丝点击