Volley 源码分析(一)

Volley.java

Volley.java包含两个重载方法,newRequestQueue(Context context) 内部调用的也是 newRequestQueue(Context context,HttpStack stack)

newRequestQueue(Context c ontext,HttpStack stack)这个静态方法主要做了:

  1. 创建了本次磁盘的缓存目录;
  2. 根据不同android sdk的版本选择不同的网络连接实现;
  3. 创建RequestQueue,调用RequestQueue的start()方法,返回RequestQueue对象。
  public static RequestQueue newRequestQueue(Context context, HttpStack stack) {        //创建磁盘的缓存目录
        File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);

        String userAgent = "volley/0";
        try {
            String packageName = context.getPackageName();
            PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);
            userAgent = packageName + "/" + info.versionCode;
        } catch (NameNotFoundException e) {
        }
        //如果sdk版本大于等于9(android2.3)使用HurlStack,HurlStack内部使用的是HttpURLConnection做的网络连接
        //如果sdk版本小于9使用HttpClientStack ,HttpClientStack内部用的是 HttpClient
        if (stack == null) {
            if (Build.VERSION.SDK_INT >= 9) {
                stack = new HurlStack();
            } else {
                // Prior to Gingerbread, HttpUrlConnection was unreliable.
                // See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html
                stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
            }
        }
        //创建的stack被传递到BasicNetwork内部
        Network network = new BasicNetwork(stack);
        //创建RequestQueue,调用start方法,返回RequestQueue
        RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
        queue.start();

        return queue;
    }
RequestQueue.java

先看下RequestQueue的start方法,start方法:

  1. 首先调用stop方法确保所有的网络或则缓存的分发终止;
  2. 创建一个缓存分发CacheDispather线程,并调用start处理缓存;
  3. 创建与线程池设置的数量相同的网络分发线程,并调用start方法开始.
  /** The cache triage queue. */
    private final PriorityBlockingQueue<Request<?>> mCacheQueue =
        new PriorityBlockingQueue<Request<?>>();

    /** The queue of requests that are actually going out to the network. */
    private final PriorityBlockingQueue<Request<?>> mNetworkQueue =
        new PriorityBlockingQueue<Request<?>>();

    /** Number of network request dispatcher threads to start. */
    private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4;

    /** Cache interface for retrieving and storing responses. */
    private final Cache mCache;

    /** Network interface for performing requests. */
    private final Network mNetwork;

    /** Response delivery mechanism. */
    private final ResponseDelivery mDelivery;

    /** The network dispatchers. */
    private NetworkDispatcher[] mDispatchers;

    /** The cache dispatcher. */
    private CacheDispatcher mCacheDispatcher;
/**
     * Starts the dispatchers in this queue.
     */
    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();
        }
    }

    /**
     * Stops the cache and network dispatchers.
     */
    public void stop() {
        if (mCacheDispatcher != null) {
            mCacheDispatcher.quit();
        }
        for (int i = 0; i < mDispatchers.length; i++) {
            if (mDispatchers[i] != null) {
                mDispatchers[i].quit();
            }
        }
    }

可以看到RequestQueue内部使用PriorityBlockingQueue管理线程的并发问题的,PriorityBlockingQueue是一个线程安全的阻塞队列,后续将会分析下PriorityBlockingQueue的实现原理。

在使用Volley时,首先会调用Volley.java的newRequestQueue方法创建RequestQueue, newRequestQueue内部调用了RequestQueue的start方法。然后创建Request,调用RequestQueue的add方法将创建的Request加入RequestQueue。那么RequestQueue的add方法做了哪些事呢?

add方法:

  1. 设置当前RequestQueue为Request的RequestQueue,并将request加入到当前request的集合中;
  2. 给request设置编号;
  3. 如果加入的request不需要缓存,直接加入网络队列;
  4. 如果加入的request已经加入过,将其作为mWaittingRequests的元素;
  5. 没有加入过,将其加入等待队列和缓存队列中;

源码如下:

 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;
        }

        // Insert request into stage if there's already a request with the same cache key in flight.
        synchronized (mWaitingRequests) {
            String cacheKey = request.getCacheKey();
            if (mWaitingRequests.containsKey(cacheKey)) {
                // There is already a request in flight. Queue up.
                Queue<Request<?>> stagedRequests = mWaitingRequests.get(cacheKey);
                if (stagedRequests == null) {
                    stagedRequests = new LinkedList<Request<?>>();
                }
                stagedRequests.add(request);
                mWaitingRequests.put(cacheKey, stagedRequests);
                if (VolleyLog.DEBUG) {
                    VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold.", cacheKey);
                }
            } else {
                // Insert 'null' queue for this cacheKey, indicating there is now a request in
                // flight.
                mWaitingRequests.put(cacheKey, null);
                mCacheQueue.add(request);
            }
            return request;
        }
    }
NetworkDispatcher.java

NetworkDispather继承Thread,在其run方法中做了:

  1. 从网络队列中获取一个request,如果请求被取消了,调用continue继续while循环;
  2. 请求未被取消,调用BasicNetWork的performRequest()方法连接网络,返回NetWorkResponse;
  3. 对304状态进行处理。如果返回的networkResponse内容没有更改,并且已经被分发掉,继续下一次请求;
  4. 调用request的parseNetworkResponse,返回Response;
  5. 如果需要缓冲,调用DiskBasedCache的put方法向磁盘缓存;
  6. 调用ResponseDelivery的postResponse分发response;

NetworkDispather的run()方法源码如下:

  @Override
    public void run() {
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
        while (true) {
            long startTimeMs = SystemClock.elapsedRealtime();
            Request<?> request;
            try {
                // Take a request from the queue.
                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.
                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);
            }
        }
    }
CacheDispatcher.java

该类继承Thread,在其run()方法中主要做了:

  1. 首先去缓存队列中获取Request
  2. 如果Requestb被取消了,不做处理continue
  3. 从磁盘缓存中获取缓存对象,缓存丢失,将request放入到网络队列中,不再处理continue
  4. 检验缓存是否过期,若缓存过期,将缓存设置给request,然后将request加入到网络队列中,调用continue继续;
  5. 如果request都不满足,说明得到了缓存,并且缓存没有过期,调用Response<?> response = request.parseNetworkResponse( new NetworkResponse(entry.data, entry.responseHeaders)),利用缓存构建Response交给request处理;
  6. 如果缓存不需要刷新,将response 分发掉( Completely unexpired cache hit. Just deliver the response.);
  7. 如果需要刷新,设置request的缓存,标记response,分发response之后,将request加入到网络队列中。

如下是CacheDispather中run方法的源码:

 @Override
    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 {
                // Get a request from the cache triage queue, blocking until
                // at least one is available.
                final Request<?> request = mCacheQueue.take();
                request.addMarker("cache-queue-take");

                // If the request has been canceled, don't bother dispatching it.
                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 it is completely expired, just send it to the network.
                if (entry.isExpired()) {
                    request.addMarker("cache-hit-expired");
                    request.setCacheEntry(entry);
                    mNetworkQueue.put(request);
                    continue;
                }

                // We have a cache hit; parse its data for delivery back to the request.
                request.addMarker("cache-hit");
                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 {
                    // Soft-expired cache hit. We can deliver the cached response,
                    // but we need to also send the request to the network for
                    // refreshing.
                    request.addMarker("cache-hit-refresh-needed");
                    request.setCacheEntry(entry);

                    // Mark the response as intermediate.
                    response.intermediate = true;

                    // 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) {
                                // Not much we can do about this.
                            }
                        }
                    });
                }

            } catch (InterruptedException e) {
                // We may have been interrupted because it was time to quit.
                if (mQuit) {
                    return;
                }
                continue;
            }
        }
    }

有了对网络和缓存的处理,来看下获取的response是怎么分发的呢?

ExecutorDelivery.java

先看下构成方法的实现,通过一个Executor对Handler进行了封装,内容的实现是通过mResonsePoster的execute执行runnable内部的方法的,如下:

““java
/**
* 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);
}
};
}

@Override
public void postResponse(Request

 /**
     * A Runnable used for delivering network responses to a listener on the
     * main thread.
     */
    @SuppressWarnings("rawtypes")
    private class ResponseDeliveryRunnable implements Runnable {
        private final Request mRequest;
        private final Response mResponse;
        private final Runnable mRunnable;

        public ResponseDeliveryRunnable(Request request, Response response, Runnable runnable) {
            mRequest = request;
            mResponse = response;
            mRunnable = runnable;
        }

        @SuppressWarnings("unchecked")
        @Override
        public void run() {
            // If this request has canceled, finish it and don't deliver.
            //如果请求被取消了,就不再处理,直接return
            if (mRequest.isCanceled()) {
                mRequest.finish("canceled-at-delivery");
                return;
            }

            // Deliver a normal response or error, depending.
            //如果响应成功,调用request的deliverResponse分发响应
            //如果相应不成功,调用request的deliverError分发响应的错误
            //一般都是设置listener监听回调
            if (mResponse.isSuccess()) {
                mRequest.deliverResponse(mResponse.result);
            } else {
                mRequest.deliverError(mResponse.error);
            }

            // If this is an intermediate response, add a marker, otherwise we're done
            // and the request can be finished.
            if (mResponse.intermediate) {
                mRequest.addMarker("intermediate-response");
            } else {
                mRequest.finish("done");
            }
            //调用传进来runnable的run方法,这样runnable的中的逻辑就会在分发后被执行
            // If we have been provided a post-delivery runnable, run it.
            if (mRunnable != null) {
                mRunnable.run();
            }
       }
    }

源码注释的很详细,我加上了些自己的理解。

通过上面的分析,大致理清了Volley的网络请求分发的过程。看Volley网络实现源码,自己对网络的处理理解的更深入了些,学习了大牛们的编码风格和设计思想。后续还会对Volley中网络的重试机制和ImageLoader进行分析,相信自己会有更多收获!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值