再回首 -- Volley源码解析

Posted 巨头之路

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了再回首 -- Volley源码解析相关的知识,希望对你有一定的参考价值。

前言

前面解析了Retrofit和OkHttp这两个框架的源码,提到网络框架,肯定少不了Volley这位老前辈,想曾经的项目用的可都是Volley,每个框架都有其适用的场景,只是随着时间的更迭,逐渐被替代。 那这次顺便也解析下Volley框架的源码,这里做个笔记

本篇的Volley版本基于 1.2.0-SNAPSHOT

Volley库地址

1.首先看下Volley的基本使用


	//第1步.创建RequestQueue(请求队列)
	RequestQueue mQueue = Volley.newRequestQueue(getApplicationContext());

    //第2步.创建所需的Request(请求),这里是创建StringRequest 
	StringRequest mStringRequest = 
				new StringRequest(Request.Method.GET, "http://www.baidu.com",
                new Response.Listener<String>() 
                    @Override
                    public void onResponse(String response) 
                        Log.i("wangshu", response);
                    
                , new Response.ErrorListener() 
		            @Override
		            public void onErrorResponse(VolleyError error) 
		                Log.e("wangshu", error.getMessage(), error);
		            
        		);

	//第3步.将请求添加进请求队列中
    mQueue.add(mStringRequest);

2.从Volley的基本使用API入手其源码:

a. 首先看下创建RequestQueue的代码,由Volley.newRequestQueue开始


	public class Volley 

		public static RequestQueue newRequestQueue(Context context) 
			//调用下面标注1的newRequestQueue
	        return newRequestQueue(context, (BaseHttpStack) null);
	    
	
		//标注1
		public static RequestQueue newRequestQueue(Context context, BaseHttpStack stack) 
	        BasicNetwork network;
	        if (stack == null) 
	            if (Build.VERSION.SDK_INT >= 9) 
	                //android版本大于2.3,则创建基于HttpUrlConnection的HurlStack
	                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, /* flags= */ 0);
	                    userAgent = packageName + "/" + info.versionCode;
	                 catch (NameNotFoundException e) 
	                
	                //Android版本小于2.3,则创建基于HttpClient的HttpClientStack
	                network =
	                        new BasicNetwork(
	                                new HttpClientStack(AndroidHttpClient.newInstance(userAgent)));
	            
	         else 
	            //stack != null的情况
	            network = new BasicNetwork(stack);
	        

			//调用下面标注2的newRequestQueue
	        return newRequestQueue(context, network);
	    


		//标注2
		private static RequestQueue newRequestQueue(Context context, Network network) 
	        final Context appContext = context.getApplicationContext();
	        // Use a lazy supplier for the cache directory so that newRequestQueue() can be called on
	        // main thread without causing strict mode violation.
	        DiskBasedCache.FileSupplier cacheSupplier =
	                new DiskBasedCache.FileSupplier() 
	                    private File cacheDir = null;
	
	                    @Override
	                    public File get() 
	                        if (cacheDir == null) 
	                            cacheDir = new File(appContext.getCacheDir(), DEFAULT_CACHE_DIR);
	                        
	                        return cacheDir;
	                    
	                ;
	        RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheSupplier), network);
	        queue.start();
	        return queue;
	    
	

结合代码和注释,Volley.newRequestQueue主要做了两件事

  • 判断Android的系统版本是否大于2.3; Android版本大于或等于2.3,则创建基于HttpUrlConnection的HurlStack。 Android版本小于2.3,则创建基于HttpClient的HttpClientStack
  • 创建请求队列RequestQueue,并调用其start函数,最后返回RequestQueue

接着进去RequestQueue的start 函数


	public class RequestQueue 

		public void start() 
	        //将当前正在执行的线程全部中断
	        stop(); 
	        //创建缓存线程并且启动它
	        mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
	        mCacheDispatcher.start();
	
	        
	        // mDispatchers数组默认动态初始化长度为4,所以网络执行线程默认创建4条
	        for (int i = 0; i < mDispatchers.length; i++) 
	            NetworkDispatcher networkDispatcher =
	                    new NetworkDispatcher(mNetworkQueue, mNetwork, mCache, mDelivery);
	            mDispatchers[i] = networkDispatcher;
	            networkDispatcher.start();
	        
	    
	

在RequestQueue的start 函数中,初始化一个缓存调度线程(CacheDispatcher)和4个( 默认情况下 ) 网络调度线程(NetworkDispatcher),所以Volley默认情况下会在后台开启5个线程。线程都初始化之后,分别调用其start方法开启线程

b. 接着看下第2步创建的StringRequest类,看下StringRequest两个核心的函数


	public class StringRequest extends Request<String> 

		//deliverResponse主要负责回调响应数据
	    @Override
	    protected void deliverResponse(String response) 
	
	        Response.Listener<String> listener;
	        synchronized (mLock) 
	            listener = mListener;
	        
	        if (listener != null) 
	            listener.onResponse(response);
	        
	    
	
	    //负责解析响应数据
	    @Override
	    @SuppressWarnings("DefaultCharset")
	    protected Response<String> parseNetworkResponse(NetworkResponse response) 
	        String parsed;
	        try 
	            parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
	         catch (UnsupportedEncodingException e) 
	            // Since minSdkVersion = 8, we can't call
	            // new String(response.data, Charset.defaultCharset())
	            // So suppress the warning instead.
	            parsed = new String(response.data);
	        
	        return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));
	    
	

可以看到StringRequest类的代码很简洁,deliverResponse函数负责将响应结果进行回调,parseNetworkResponse函数解析响应数据为Response实例,这里先记住deliverResponse和parseNetworkResponse这两个函数,后面会再碰到这个函数

c.将请求添加进请求队列中


	public class RequestQueue 

		public <T> Request<T> add(Request<T> request) 

	        request.setRequestQueue(this);
	        synchronized (mCurrentRequests) 
	            mCurrentRequests.add(request);
	        
	
	        // Process requests in the order they are added.
	        request.setSequence(getSequenceNumber());
	        request.addMarker("add-to-queue");
	        //发送request生命周期事件
	        sendRequestEvent(request, RequestEvent.REQUEST_QUEUED);
	
	        /**
	         * 如果这个request不支持缓存,那将直接添加到网络请求队列中,并返回这个request
	         * request.shouldCache() 默认为true,即开启缓存
	         */
	        if (!request.shouldCache()) 
	            mNetworkQueue.add(request);
	            return request;
	        
	        //request支持缓存,则添加到缓存队列中
	        mCacheQueue.add(request);
	        return request;
	    
	

在add函数中主要通过request.shouldCache()判断是否支持缓存,不支持缓存则将request添加进mNetworkQueue(网络请求队列),支持缓存的话则将request添加到mCacheQueue(缓存队列); request.shouldCache( )默认情况下为true,即开启缓存

在上面的代码中,可以了解到:

  • 默认情况下,是将请求添加到缓存队列中
  • Volley中主要维护了了1个缓存调度线程(CacheDispatcher),和4个(默认情况下) 网络调度线程(NetworkDispatcher)

请求添加到缓存队列,我们就从缓存调度线程(CacheDispatcher)入手

3.CacheDispatcher的工作流程

CacheDispatcher继承自Thread,那核心自然是run函数.


	public class CacheDispatcher extends Thread 

		@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 
	                processRequest();
	             catch (InterruptedException e) 
	                // We may have been interrupted because it was time to quit.
	                if (mQuit) 
	                    Thread.currentThread().interrupt();
	                    return;
	                
	                VolleyLog.e(
	                        "Ignoring spurious interrupt of CacheDispatcher thread; "
	                                + "use quit() to terminate it");
	            
	        
	    
		

在run函数中开启了死循环,不断执行processRequest函数


	private void processRequest() throws InterruptedException 
        
        //从缓存队列中取出请求
        final Request<?> request = mCacheQueue.take();
        processRequest(request);
    


	@VisibleForTesting
    void processRequest(final Request<?> request) throws InterruptedException 
        request.addMarker("cache-queue-take");
        request.sendEvent(RequestQueue.RequestEvent.REQUEST_CACHE_LOOKUP_STARTED);

        try 
            // If the request has been canceled, don't bother dispatching it.
            if (request.isCanceled()) 
                request.finish("cache-discard-canceled");
                return;
            

            // 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.
                //返回true则表示之前有执行过该请求,属于重复请求
                if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) 
                    mNetworkQueue.put(request);
                
                return;
            

            // If it is completely expired, just send it to the network.
            //true表示 缓存已到有效期
            if (entry.isExpired()) 
                request.addMarker("cache-hit-expired");
                request.setCacheEntry(entry);
                //返回true则表示之前有执行过该请求,属于重复请求
                if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) 
                    mNetworkQueue.put(request);
                
                return;
            

            // 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");
            /**
             * 判断缓存的数据是否解析成功
             * JsonObjectRequest的parseNetworkResponse函数中,解析失败的话会对Response类的error变量设置值
             * response.isSuccess()则是判断error变量是否为null
             */
            if (!response.isSuccess())  //缓存数据解析失败的情况
                request.addMarker("cache-parsing-failed");
                mCache.invalidate(request.getCacheKey(), true);
                request.setCacheEntry(null);
                //返回true则表示之前有执行过该请求,属于重复请求
                if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) 
                    mNetworkQueue.put(request);
                
                return;
            
            //缓存是否需要更新
            if (!entry.refreshNeeded()) //不需要更新缓存的情况
                // Completely unexpired cache hit. Just deliver the response.
                //mDelivery的默认实现类是ExecutorDelivery,在RequestQueue类的构造函数中赋值的
                //在ExecutorDelivery的postResponse函数中将子线程切换到主线程,并回调给Response.Listener的onResponse()方法
                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;
                //返回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.
                    //将数据回调给主线程 (Response.Listener的onResponse()方法 )
                    mDelivery.postResponse(request, response);
                
            
         finally 
            request.sendEvent(RequestQueue.RequestEvent.REQUEST_CACHE_LOOKUP_FINISHED);
        
    

processRequest函数中大致做了如下几个判断:

  1. processRequest函数从缓存队列中取出请求,然后判断该请求是否被取消?
    • 如果取消,则直接中止该函数;
  2. 如果没有取消,则判断该请求是否有对应的缓存?
    • 如果没有缓存,则判断该请求是否已执行过,执行过则直接中止该函数,未执行过则添加到网络请求队列并中止该函数
  3. 如果有缓存,则判断该缓存是否过期?
    • 如果已过期,则判断该请求是否已执行过,执行过则直接中止该函数,未执行过则添加到网络请求队列并中止该函数
  4. 如果未过期,则解析缓存的数据,接着判断缓存数据是否解析成功?
    • 如果解析失败,则设置缓存无效,并判断该请求是否已执行过,执行过则直接中止该函数,未执行过则添加到网络请求队列并中止该函数
  5. 如果解析成功,接着判断该缓存是否需要更新?
    • 如果不需要更新,则将第4步中缓存数据解析的结果回调给主线程
  6. 如果需要更新,则判断该请求是否已执行过?
    • 如果该请求已执行过,将第4步中缓存数据解析的结果回调给主线程
    • 如果该请求未执行过,将第4步中缓存数据解析的结果回调给主线程,并将请求添加到网络请求队列中

processRequest函数中调用request.parseNetworkResponse()解析缓存的数据, request是从缓存队列中取出的,所以这个request就是一开始API中调用mQueue.add() 添加到缓存队列的StringRequest,而StringRequest类的parseNetworkResponse函数在上面已经提到,是用于将响应数据解析为Response实例的

4.NetworkDispatcher的工作流程

NetworkDispatcher同样继承自Thread,run函数同样是开启了死循环,不断执行processRequest函数


	public class NetworkDispatcher extends Thread 

		private void processRequest() throws InterruptedException 
	        //从网络队列中取出请求
	        Request<?> request = mQueue.take();
	        processRequest(request);
	    


		@VisibleForTesting
	    void processRequest(Request<?> request) 
	        long startTimeMs = SystemClock.elapsedRealtime();
	        request.sendEvent(RequestQueue.RequestEvent.REQUEST_NETWORK_DISPATCH_STARTED);
	        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");
	                request.notifyListenerResponseNotUsable();
	                return;
	            
	
	            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.
	            //如果服务端响应码是304 && 该请求已经请求过,则取消该次请求
	            //在下面的代码中request.markDelivered()会将该请求设置为已请求状态
	            if (networkResponse.notModified && request.hasHadResponseDelivered()) 
	                request.finish("not-modified");
	                request.notifyListenerResponseNotUsable();
	                return;
	            
	
	            // 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.
	            requestVolley框架源码分析

Volley源码解析——从实现角度深入剖析volley

Volley源码解析

Android开发——Volley源码解析

Volley 源码解析(转)

Volley源码解析