Volley源码解析

尊重原创,转载请注明出处,谢谢


  很早就想写篇Volley源码解析的文章,但是写文章,特别是写源码解析的文章其实是需要决心的,也是需要时间的,因为你需要把你老早就看过的代码重新理一遍,然后加上注释,然后解析,继上一次写了个Volley的引子后,过去也有一段时间了,终于可以抽出一个周末下午的时间来写一下了,希望对需要的朋友有用的,另外,Volley的源码写的十分出色,可拓展性方面非常强,功能模块十分清晰,所以也是对自我的一次洗礼吧,学习他的设计以及编码也是具有很大的价值的,废话不多说了。


     我觉得解析的最好方式就是对照源码给出注释,然后得出结果,所以本文很大一部分解析都写在了代码的注释方面,从代码中来,到代码中去嘛!


先来看一下Volley的使用方式:

private RequestQueue mRequestQueue;
mRequestQueue =  Volley.newRequestQueue(this); 

JsonObjectRequest request = new JsonObjectRequest(Request.Method.GET,url,null,new Response.Listener<JSONObject>() { 
            @Override 
            public void onResponse(JSONObject response) { 

                 ......... 
            } 
        },new Response.ErrorListener() { 
            @Override 
            public void onErrorResponse(VolleyError error) { 
               ............
            } 
        }); 

mRequestQueue.add(request);

 
接下来对Volley的源码进行解析:

1 .从Volley类入手,这里面就是提供了一个创建RequestQueue的方法:newRequestQueue(),有两个重载方法,一个是使用默认的HttpStack,一个是可以自定义HttpStack作为参数传进去。
下面是newRequestQueue()代码及解析:

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) {
    }

    if (stack ==  null) { // 使用 Volley 默认的 HttpStack
        if (Build.VERSION.SDK_INT >=  9) { //API 版本在 9 以上则使用 HurlStack, 里面的 performRequest() 是通过 HttpUrlConnection 去实现网络请求的
            stack =  new HurlStack() ;

       else { //API 版本在 9 以上则使用 HttpClientStack, 里面的 performRequest() 是通过 HttpClient 去实现网络请求的
            // Prior to Gingerbread, HttpUrlConnection was unreliable.
            // See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html
            stack =  new HttpClientStack(AndroidHttpClient.newInstance(userAgent)) ;
        }
    }

    //new  一个 BasicNetWork, 这里面主要就是一个 perfromRequest ()方法区发起请求,是通过调 stack 里面的 perfromRequest() 来实现的
    Network network =  new BasicNetwork(stack) ;

    // 创建一个 RequestQueue, 关注传进去的两个参数
    RequestQueue queue =  new RequestQueue( new DiskBasedCache(cacheDir) network) ;

    queue.start() ;//这个start之后直接开启了整个队请求队列的处理过程,可以看下面对这个方法的解析

    return queue ;
}

所以可以知道真正发送请求的就是BasicNetWork里的performRequest(),它里面是通过HttpStack的perfromRequest()去发送请求的,而这里面就是利用HttpUrlConnection或HttpClient。所以对Volley最基础的发送请求部分有兴趣的话可以看BasicNetWork.performRequest()。

接下来解析一下RequestQueue:

先来看看RequestQueue类里的几个变量,也是整个框架最重要的几个组成部分:
1.CacheDispatcher :继承自Thread,是一个线程,负责处理走缓存缓存的请求,当调用start()启动之后,会不断的从缓存请求队列中取出请求进行处理,请求处理结束则把结果传递给ResponseDelivery去执行之后的处理,如果结果为缓存过,或者缓存失效或需要重新刷新的情况下,该请求都会重新进入NetWorkDispathcher.

2.NetworkDispatcher:也是一个线程,专门处理走网络的请求,start()后也是不断的从请求队列里取请求,处理请求,获取到结果后把结果传递到ResponseDelivery去执行后续处理,然后在判断是否需要缓存结果。

3.ResponseDelivery:返回结果分发接口

4.Network:就是执行网络请求的,里面的performRequest()就是发送请求并接收结果, 并将结果转换为可被ResponseDelivery处理的NetworkResponse。

5.Cache:缓存请求结果的,默认是DiskBaseCache

上面的这几个类各司其职,比如调度处理请求的、实现发送网络请求的、做缓存的等的,完成各自的工作,还有一个特点就是他们大都都是接口,而不是具体实现类,Volley有大量的面向接口的编程,这极大的加大了框架的灵活性和可拓展性,用户甚至可以自定义实现某个部分的功能。

RequestQueue类内部还有一些变量:

1.mCacheQueue:是一个存放结果已被缓存过的请求的队列,CacheDispather就是每次去这里面取请求进行处理
2.mNetworkQueue:存放进行网络请求的队列,NetworkDispather就是每次去这里面取请求进行处理的

上面两个队列的类型都是BlockingQueue:是一个支持两个附加操作的队列。这两个附加的操作是:在队列为空时,获取元素的线程会等待队列变为非空。当队列满时,存储元素的线程会等待队列可用,十分适合这种场景。

3.NetworkDispatcher[] mDispatchers:就是一个放Networkdispather的数组,默认定义容量是4

上面就是RequestQueue类里面的一些重要变量了,接下来看看它里面的方法,主要就是两个,一个start(),可以看到上面Volley.newRequestQueue()里面最后就调了start();还有一个是add(),就是把请求加入Queue中。

1.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() ; // 开启 CacheDispather 线程,开始处理 mCacheQueue 中的请求

    // Create network dispatchers (and corresponding threads) up to the pool size.
    // 循环开启数组里的 NetworkDispatcher 线程,开始处理 mNetworkQueue 中的请求
    for ( int i =  0 i < mDispatchers.length i++) {
        NetworkDispatcher networkDispatcher =  new NetworkDispatcher(mNetworkQueue mNetwork ,
                mCache mDelivery) ;
        mDispatchers[i] = networkDispatcher ;
        networkDispatcher.start() ;
    }
}

下面具体看一下CacheDispather start之后,里面的run是如果处理mCacheQueue里的请求的,也就是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 根据 key 取出相应的结果 entry
            Cache.Entry entry = mCache.get(request.getCacheKey()) ;

            // 如果结果为 null, 则打上缓存消失的标志,然后把请求加入 NetworkQueue ,重新进行网络请求
            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.
            // 如果缓存的请求结果已过期,则同样打个标志,然后放进 NetwoekQueue 重新请求
            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") ;
            // 把结果转化为 NetworkResponse 类型
            Response<?> response = request.parseNetworkResponse(
                    new NetworkResponse(entry.data entry.responseHeaders)) ;
            request.addMarker( "cache-hit-parsed") ;

            // 如果结果是不需要刷新的,则直接发送给 ResponseDelivery 进行分发
            if (!entry.refreshNeeded()) {
                // Completely unexpired cache hit. Just deliver the response.
                mDelivery.postResponse(request response) ;
            }
            // 如果结果是需要刷新的,则也是先把结果发送给 ResponseDelivery 进行分发,然后再放进 NetworkQueue 中进行网络请求
            // 这样可以先展示缓存的结果,然后又去请求网络回来就可以刷新结果了
            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;
        }
    }
}

上面的代码已经十分清晰的的解析了CacheDispatcher线程取请求处理请求的过程,可以看到取结果是到Cache里面去取的,那下面就解析一下这个Cache:

Cache只是一个接口,里面定义了对结果的存、取、删除等方法,而DiskBasedCache才是真正的实现类,先来看一下Cache里面缓存结果的一个实体Entry:
public static class Entry {
    /** The data returned from cache. */
    public byte[]  data ; // 结果数据

    /** ETag for cache coherency. */
    public String  etag ; // 标签,如消失、过期等

    /** Date of this response as reported by the server. */
    public long  serverDate ; // 网络请求回来的时间

    /** The last modified date for the requested object. */
    public long  lastModified ; // 上一次刷新的时间

    /** TTL for this record. */
    public long  ttl ;

    /** Soft TTL for this record. */
    public long  softTtl ;

    /** Immutable response headers as received from server; must be non-null. */
    public Map<String String>  responseHeaders = Collections.emptyMap() ;

    /** True if the entry is expired. */
    // 是否过期
    public boolean  isExpired() {
        return this. ttl < System. currentTimeMillis() ;
    }

    /** True if a refresh is needed from the original data source. */
    // 是否需要刷新
    public boolean  refreshNeeded() {
        return this. softTtl < System. currentTimeMillis() ;
    }
}


下面接着看一下NetworkDispatcher里面是如何处理mNetworkQueue里的请求的,同样看他的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.
            // 调用 NetWork performRequest ()来发送网络请求,并获取到结果 NetworkResponse
            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<?> 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.
            // 判断结果是否需要被缓存,如果需要,则缓存进 Cache
            if (request.shouldCache() && response.cacheEntry !=  null) {
                mCache.put(request.getCacheKey() response.cacheEntry) ;
                request.addMarker( "network-cache-written") ;
            }

            // Post the response back.
            request.markDelivered() ;
            // 发送结果给 ResponseDelivery 分发出去
            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) ;
        }
    }
}

上面我们已经知道了Volley是如何取请求,处理请求的,接下来看一下ResponseDelivery这个类,上面我们发现得到结果后都会调ResponseDelivery.postResponse()来分发结果:
同样的ResponseDelivery是一个接口,里面主要有一下三个方法:

1.postResponse(Request<?> request, Response<?> response) :分发Response
2.postResponse(Request<?> request, Response<?> response, Runnable runnable):同上
3.postError(Request<?> request, VolleyError error):分发error

而它的具体实现类是:ExecutorDelivery
所以来看看ExecutorDelivery中的PostResponse()方法:
public void  postResponse(Request<?> request Response<?> response Runnable runnable) {
    request.markDelivered() ;
    request.addMarker( "post-response") ;
    mResponsePoster.execute( new ResponseDeliveryRunnable(request response runnable)) ;
}

可以看到最后是执行了一个Runable:ResponseDeliveryRunable,所以我们看看这个Runable里面的run():

@Override
public void  run() {
    // If this request has canceled, finish it and don't deliver.
    if (mRequest.isCanceled()) {
        mRequest.finish( "canceled-at-delivery") ;
        return;
    }

    // Deliver a normal response or error, depending.
    // 调用 Request 里面的 deliverResponse( )方法
    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") ;
    }

    // If we have been provided a post-delivery runnable, run it.
    if (mRunnable !=  null) {
        mRunnable.run() ;
    }
}

可以看到是调用了Request里面的deliverResponse(),而Request是一个抽象类,具体的分发工作要由继承了Request的具体类来实现这个deliverResponse(),比如Volley封装的StringRequest等等,那么我们可以继承Request来实现这个deliverResponse()实现对结果的分发。

首先是定义一个listener接口:
public interface IResponseListener< extends IResponse> {

    void  onSuccess(BaseRequest< T> request response) ;

    void  onFail(BaseRequest< T> request response ErrorCodeExtend error) ;
}

然后实现一个类继承自Request,实现deliverResponse():
@Override
protected void  deliverResponse( response) {
    if ( responseListener !=  null) {
        responseListener.onSuccess( this, response) ;
    }
}

这样就通过回调实现了对返回结果的分发,只要我们创建一个Request的时候,这个responseListener就是参数之一,这个时候想起Volley的用法:
Request request=new Request(new IResponseListener(){
             onSuccess(){
              ..........
             }
})

也就知道了。


上面基本以及解析了Volley是如何从队列中取请求,处理请求,缓存,以及分发返回结果的过程,那么还有一个过程是把请求添加进队列里,这个过程我们使用Volley的时候是通过调用RequestQueue.add()方法来实现的,那么最后就来解析一下这个方法:
public < T> Request< Tadd(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.
    // 如果此请求是不需要缓存的,则直接加进 mNetworkQueue ,结束
    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
            mCacheQueue.add(request) ;
        }
        return request ;
    }
}

这就是把Request加进队列的过程。
至此,对Volley源码的解析也差不多结束了。


总结:
通过对Volley源码的解析,可以看出来这是一个写的十分优秀的开源项目,各模块分工十分明确,基本上都是面对接口编程,框架的可拓展性非常强,你可以自己实现某一模块的功能,然后像插件一样插进去,当然你也可以使用Volley帮你实现的默认的模块,拔插十分方便,这就是一个优秀的框架该有的样子吧,对用户十分友好,我想Volley应该是很多想自己写框架的朋友一个良好的借鉴。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值