Volley使用与解析

     最近项目中部分轻量级网络请求业务使用到了Volley框架,也趁此机会系统的看一下其用法和源码设计思路,我会先讲解其最基本的几种用法,然后从创建消息队列开始入手分析一下源码,不多说,走起~

    Volley为我们提供了五种基本的网络请求方式,分别是StringRequestJsonRequestJsonObjectRequestJsonArrayRequestImageRequest。下面分别介绍这五种请求的用法。

一、Volley框架用法

   首先当然是创建RequestQueue对象,然后使用该对象把请求对象添加进去。先说下,在用完Request和RequestQueue后一定要在onDestroy()中调用stop(),因为从你创建RequestQueue对象的那一刻起,Volley就默认的给你开启了五个子线程,如果你没有添加Request到队列,那么线程就一直等待,等待,等待。。。即使用户按下返回键,Activity也是不会被finish掉的,因为子线程们还是在等待,这时会造成严重的内存泄露(如果用户多次退出和进入那个Activity,会创建多个Activity实例)。务必注意。

    下面介绍Volley的五种基本用法:


   1、StringRequest用法:

/**
	 * StringRequest请求
	 * 
	 * @return 返回StringRequest对象
	 */
	public StringRequest startStringRequest() {
		StringRequest jora = new StringRequest(mUrl,
				new Response.Listener() {

					@Override
					public void onResponse(String arg0) {
						// 注意,在这个方法中处理请求返回来的正确结果,根据自己的业务需求处理,下同
						if (mResponceCallback != null) {
							mResponceCallback.onStringParse(arg0);
						}
					}

				}, new Response.ErrorListener() {

					@Override
					public void onErrorResponse(VolleyError error) {
						// 这里处理请求返回的错误,下同
						VolleyLog.e("Error: ", error.getMessage());

					}

				});
		if (mRequestQueue != null) {
			mRequestQueue.add(jora);
		}
		return jora;
	}

   2、JsonObjectRequest用法

/** JsonObject请求 */
	public JsonObjectRequest startJsonObjectRequest() {
		JsonObjectRequest jora = new JsonObjectRequest(mUrl, null,
				new Response.Listener() {
					@Override
					public void onResponse(JSONObject arg0) {

						if (mResponceCallback != null) {
							mResponceCallback.onJsonObjectPrase(arg0);
						} else {

						}
					}
				}, new Response.ErrorListener() {

					@Override
					public void onErrorResponse(VolleyError arg0) {

						Log.i("TAG", arg0.getMessage());
					}
				});
		if (mRequestQueue != null) {
			mRequestQueue.add(jora);
		}
		return jora;
	}

   3、JsonArrayRequest用法

/**
	 * JsonArrayRequest请求
	 * 
	 * @return 返回JsonArrayRequest对象
	 */
	public JsonArrayRequest startJsonArrayRequest() {
		JsonArrayRequest jsona = new JsonArrayRequest(mUrl,
				new Response.Listener() {
					@Override
					public void onResponse(JSONArray response) {
						if (mResponceCallback != null) {
							mResponceCallback.onJsonArrayParse(response);
						}
					}
				}, new Response.ErrorListener() {

					@Override
					public void onErrorResponse(VolleyError arg0) {
						Log.i("TAG", arg0.getMessage());
					}
				});
		if (mRequestQueue != null) {
			mRequestQueue.add(jsona);
		}
		return jsona;
	}

   4、ImageRequest用法

/**
	 * 图片请求方法
	 * 
	 * @param imageUrl
	 *            图片地址
	 * @param width
	 *            宽度
	 * @param height
	 *            高度
	 * @param config
	 *            图片质量配置,取Bitmap.Config类的四个值
	 * @param errorListener
	 *            错误监听
	 * @return
	 */
	public ImageRequest startImageRequest(String imageUrl, int width,
			int height, Bitmap.Config config,
			Response.ErrorListener errorListener) {
		ImageRequest imageRequest;
		imageRequest = new ImageRequest(imageUrl,
				new Response.Listener() {
					@Override
					public void onResponse(Bitmap response) {
						if (mResponceCallback != null) {
							mResponceCallback.onImageSet(response);
						}
					}

				}, width, height, config, errorListener);
		if (mRequestQueue != null) {
			mRequestQueue.add(imageRequest);
		}
		return imageRequest;
	}
      当然了,对于图片的请求,Volley还提供了一种处理方式,如下:

      

// 获取NetworkImageView实例
		mNetImag = (NetworkImageView) findViewById(R.id.net_image);
		// 创建ImageLoader对象
		mImageLoader = new ImageLoader(mRequestQueue, new ImageCache() {

			@Override
			public void putBitmap(String url, Bitmap bitmap) {
				// 在这里将请求到的图片做缓存处理
				Log.d(TAG, "url--->" + url + "-->bitmap:" + bitmap);
			}

			@Override
			public Bitmap getBitmap(String url) {
				//
				return null;
			}
		});
		
		// 设置请求错误时的图片
		mNetImag.setErrorImageResId(R.drawable.ic_launcher);
		// 设置默认的图片
		mNetImag.setDefaultImageResId(R.drawable.ic_launcher);
                // 为图片设置url,ok,收工
		mNetImag.setImageUrl(mImageUrl, mImageLoader);


         除了基本用法以外,Volley还提供了高度定制、灵活的用法,比如在请求还未完成时你可以随时取消任务,也可以间接的修改连接时长等,下面就介绍一些使用Volley的小技巧:

-------------------------------------------------------------------------华丽分隔线--------------------------------------------------------------------------

      1、自定义连接时长

       在Request类中有一个setRetryPolicy()方法用来设置RetryPolicy,通常我们创建一个请求对象时可能并没有用到,实际上Volley会默认的给我们设置这个属性,不信我们看下StringRequest的构造方法:

        

/**
     * Creates a new request with the given method.
     *
     * @param method the request {@link Method} to use
     * @param url URL to fetch the string at
     * @param listener Listener to receive the String response
     * @param errorListener Error listener, or null to ignore errors
     */
    public StringRequest(int method, String url, Listener listener,
            ErrorListener errorListener) {
        super(method, url, errorListener);
        mListener = listener;
    }
       不过貌似没有看到设置那个方法啊,但是他有一个super()调用了其父类也就是Request()的构造方法,我们去Request()中去看看:
/**
     * Creates a new request with the given method (one of the values from {@link Method}),
     * URL, and error listener.  Note that the normal response listener is not provided here as
     * delivery of responses is provided by subclasses, who have a better idea of how to deliver
     * an already-parsed response.
     */
    public Request(int method, String url, Response.ErrorListener listener) {
        mMethod = method;
        mUrl = url;
        mErrorListener = listener;
		// 给了一个默认的RetryPolicy
        setRetryPolicy(new DefaultRetryPolicy());

        mDefaultTrafficStatsTag = TextUtils.isEmpty(url) ? 0: Uri.parse(url).getHost().hashCode();
    }
       正如上所示,我们在创建相应的请求时,Volley为我们创建了默认的RetryPolicy,RetryPolicy只是一个接口,就这个名字翻译过来是“政策重定义”,也就是我们队Request的一些行为重新规范。RetryPolicy接口有一个实现类DefaultRetryPolicy,那我们再去那个类看看:

       

/**
 * Default retry policy for requests.
 */
public class DefaultRetryPolicy implements RetryPolicy {
    /** The current timeout in milliseconds. */
    private int mCurrentTimeoutMs;

    /** The current retry count. */
    private int mCurrentRetryCount;

    /** The maximum number of attempts. */
    private final int mMaxNumRetries;

    /** The backoff multiplier for for the policy. */
    private final float mBackoffMultiplier;

    /** The default socket timeout in milliseconds */
	/** 默认连接超时时长为2.5s */
    public static final int DEFAULT_TIMEOUT_MS = 2500;

    /** The default number of retries */
    public static final int DEFAULT_MAX_RETRIES = 1;

    /** The default backoff multiplier */
    public static final float DEFAULT_BACKOFF_MULT = 1f;

    /**
     * Constructs a new retry policy using the default timeouts.
     */
    public DefaultRetryPolicy() {
        this(DEFAULT_TIMEOUT_MS, DEFAULT_MAX_RETRIES, DEFAULT_BACKOFF_MULT);
    }

    /**
     * Constructs a new retry policy.
     * @param initialTimeoutMs The initial timeout for the policy.
     * @param maxNumRetries The maximum number of retries.
     * @param backoffMultiplier Backoff multiplier for the policy.
     */
    public DefaultRetryPolicy(int initialTimeoutMs, int maxNumRetries, float backoffMultiplier) {
		mCurrentTimeoutMs = initialTimeoutMs;
        mMaxNumRetries = maxNumRetries;
        mBackoffMultiplier = backoffMultiplier;
    }

    /**
     * Returns the current timeout.
     */
    @Override
    public int getCurrentTimeout() {
        return mCurrentTimeoutMs;
    }

    /**
     * Returns the current retry count.
     */
    @Override
    public int getCurrentRetryCount() {
        return mCurrentRetryCount;
    }

    /**
     * Prepares for the next retry by applying a backoff to the timeout.
     * @param error The error code of the last attempt.
     */
    @Override
    public void retry(VolleyError error) throws VolleyError {
        mCurrentRetryCount++;
        mCurrentTimeoutMs += (mCurrentTimeoutMs * mBackoffMultiplier);
        if (!hasAttemptRemaining()) {
            throw error;
        }
    }

    /**
     * Returns true if this policy has attempts remaining, false otherwise.
     */
    protected boolean hasAttemptRemaining() {
        return mCurrentRetryCount <= mMaxNumRetries;
    }
}


     

 
  

二、Volley框架源码分析      首先来了解下Volley的原理:我们在使用时首先创建一个RequestQueue对象,该对象表示一个请求队列,然后我们创建相应的请求,并加入到该请求队列中。然后调用start()方法即可开始网络请求。

      我们已经知道Volley的基本用法了,那么现在已经迫不及待的想去一窥源码吧!源码并不多,算上相关的Log、Error、缓存等类也就只有四五十个类,核心类也就十几个,下面先把主要的核心类介绍一下:

    (1)Volley:这个类很简单,对于开发者来说就是获取RequestQueue对象,而实际上,在获取对象时也帮我们的应用程序创建好了缓存目录、根据系统版本使用不同的网络请求方式等。

    (2)RequestQueue:Volley框架的“首脑”,它会负责将用户的请求添加到子线程去执行,是它将用户的Request、Network、CacheDispatcher和NetworkDispatcher衔接起来,配合完成任务的。

    (3)Request:是一个抽象类,它的直接实现子类有ClearCacheRequest、ImageRequest、JsonRequest、StringRequest,同时JsonRequest也是一个抽象类,它有两个实现子类分别是JsonObjectRequest和JsonArrayRequest。顾名思义Request就是用户的请求,通常开发人员根据自己的需求使用不同的请求。

    (4)HttpStack:一个接口,他有两个实现类,分别是HttpClientStack和HurlStack,前者实现了HttpClient请求方式,后者实现了HttpURLConnect请求方式,我们都知道,HttpClient是Apache提供的开源的网络请求方式,而HttpURLConnection是JDK提供的标准的网络请求方式,实质上都是HTTP请求,实际上,Android的Framework在HttpClient的基础上自己实现了一个AndroidHttpClient类,在Volley框架中就使用到了它。Volley框架会根据系统版本选择不同的请求方式,2.2版本以下会使用HttpClient请求,在2.2版本以上会使用HttpUrlConnection请求。

    (5)Network:是一个接口,它有一个BasicNetwork实现类,它负责将用户的Request使用HttpStack封装好的请求在子线程中去执行。实际上,我们在使用Volley类创建RequestQueue对象时,框架就已经帮我们创建好了HttpStack了,并且将HttpStack对象传递给Network,然后将Network对象和缓存目录传递给RequestQueue,从而创建一个RequestQueue对象,而如果你不想使用Volley类提供的方式来获取RequestQueue,那你要做的事情就多了,RequestQueue开放了三个构造函数,都包含了一个Network参数,因此,你需要实现一个Network类,当然你也可以直接使用BasicNetwork,但是BasicNetwork构造函数又需要提供一个HttpStack参数或者直接使用它的两个实现类,因此,你如果想要自己new一个RequestQueue对象,你就需要自己去做Volley类所做的事情。

    (6)CacheDispatcher:这是一个继承Thread的线程,它负责根据用户的Request判断缓存是否有这个请求,如果有这个缓存还要判断它是否过期。如果不存在缓存或者过期那就直接让NetworkDispatcher去网络请求获取。

    (7)NetworkDispatcher:这也是一个继承Thread的线程,它负责将用户的Request进行网络请求,当然了,在NetworkDispatcher中是由Network来执行的。

    (8)Cache:一个接口,它有两个实现类,分别是DiskBasedCache和NoCache,主要是缓存相关。

    (9)ResponseDelivery:它负责传递网络请求的响应,比如错误、结果信息等。

   (10)ImageRequest、ImageLoader、NetImageView:这三者是Volley提供对图片的支持,使用Volley来获取网络图片有两种方式,一种是使用ImageRequest请求,和普通的StringRequest请求一样,不过在构造对象时需要传递几个参数,还有一种方式就是使用ImageLoader和NetImageView,这都是Volley提供好了的,后面会详细介绍的。

   上面就是Volley框架的一些核心类,下面我们先提出一些问题,然后跟着问题来分析源码:

  

    1、Volley框架的请求、响应机制是怎么设计的?

     要回答这个问题,我们从如何创建RequestQueue说起,这里使用最简单的方法来快速获取一个RequestQueue,就是使用Volley.newRequestQueue(mContext)newRequestQueue(Context context, HttpStack stack)创建RequestQueue对象使用获取的,实际上前者是直接调用了后者来获取一个对象的,来看看Volley类便知,代码不多,我原封不动的贴出来:

public class Volley {

    /** Default on-disk cache directory. */
    private static final String DEFAULT_CACHE_DIR = "volley";

    /**
     * Creates a default instance of the worker pool and calls {@link RequestQueue#start()} on it.
     *
     * @param context A {@link Context} to use for creating the cache dir.
     * @param stack An {@link HttpStack} to use for the network, or null for default.
     * @return A started {@link RequestQueue} instance.
     */
    public static RequestQueue newRequestQueue(Context context, HttpStack stack) {
       //这里创建一个缓存目录,路径就是/Android/data/包名/...
       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) {
            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));
            }
        }

        Network network = new BasicNetwork(stack);

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

        return queue;
    }
    
    /**
     * context
     * dir sdcard's root dire
     * null
     * */
    public static RequestQueue newRequestQueueInDisk(Context context, String dir, HttpStack stack) {
        File cacheDir = new File(dir, 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) {
        }
// 如果用户没有自定义HttpStack就使用默认的HurlStack(HttpUrlConnection)和HttpClientStack(HttpClient)请求
//在2.2版本下使用HttpClient,因为2.2版本下不太支持HttpURLConnect,在2.2之上,官方建议使用HttpURLConnect
      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创建Network
        Network network = new BasicNetwork(stack);
       //根据缓存目录和Network来创建RequestQueue对象
        RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
       //开启队列,
       queue.start();

        return queue;
    }

    /**
     * Creates a default instance of the worker pool and calls {@link RequestQueue#start()} on it.
     *
     * @param context A {@link Context} to use for creating the cache dir.  Context参数用来创建缓存目录
     * @return A started {@link RequestQueue} instance.
     *这个方法是我门使用时直接调用的,然后Volley又直接调用了重载的newRequest(context,null)
     */
    public static RequestQueue newRequestQueue(Context context) {
        return newRequestQueue(context, null);
    }

        可以看到,我们在创建RequestQueue对象时,它已经将队列开启了,此时他的线程都处于等待状态,一旦有新的Request加入进来就去执行。上面 RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);代码最终会调用到其另外一个构造方法:

    

 public RequestQueue(Cache cache, Network network, int threadPoolSize) {
        this(cache, network, threadPoolSize,
                new ExecutorDelivery(new Handler(Looper.getMainLooper())));
    }
     上面的参数分别是缓存对象、Network对象、线程池的大小、ExectorDelovery对象,第三个参数默认是4,也就是为网络请求准备了4个子线程,这里重点关注最后一个参数,可以看到在创建ExecutorDelivery对象时获取主线程的Looper来创建了一个Handler对象,这个对象是用于子线程获取结果后和主线程通信时用到的。

     下面我们看些RequestQueue的start()方法:

     

/**
     * 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();
        }
		Log.d(TAG, "-->start()");
    }
        由上面的代码可以看出来,当我们创建RequestQueue对象时就默认调用了start()方法,start()方法会先stop()掉所有已经开启的线程,并重新开启一个缓存读取的线程(CacheDispatcher),以及四个网络请求线程(NetworkDispatcher),那现在我们先去看看CacheDispatch是如何工作的,对了,这里有一个重要的对象没介绍,那就是start()方法中创建CacheDispatcher对象时传递的第一个mCacheQueue和第二个mNetworkQueue参数,看下他们在RequestQueue中的定义:

        

 /** The cache triage queue. */
    /** 缓存请求队列*/
    private final PriorityBlockingQueue mCacheQueue =
        new PriorityBlockingQueue();

    /** The queue of requests that are actually going out to the network. */
    private final PriorityBlockingQueue mNetworkQueue =
        new PriorityBlockingQueue();
      上面两个变量是保证线程同步的关键所在,在start()方法中会将这两个参数传递给CacheDispatcher和NetworkDispatcher,在这两个线程中取出Reques并执行,接下来先看下CacheDispatcher源码,源码中只有两个方法,一个quit()和一个run(),以下是CacheDIspatcher类的全部源码:

     

/**
 * Provides a thread for performing cache triage on a queue of requests.
 *
 * Requests added to the specified cache queue are resolved from cache.
 * Any deliverable response is posted back to the caller via a
 * {@link ResponseDelivery}.  Cache misses and responses that require
 * refresh are enqueued on the specified network queue for processing
 * by a {@link NetworkDispatcher}.
 */
@SuppressWarnings("rawtypes")
public class CacheDispatcher extends Thread {
	private final String TAG = CacheDispatcher.class.getName();
    private static final boolean DEBUG = VolleyLog.DEBUG;

    /** The queue of requests coming in for triage. */
    private final BlockingQueue mCacheQueue;

    /** The queue of requests going out to the network. */
    private final BlockingQueue mNetworkQueue;

    /** The cache to read from. */
    private final Cache mCache;

    /** For posting responses. */
	/** 消息传递 */
    private final ResponseDelivery mDelivery;

    /** Used for telling us to die. */
    private volatile boolean mQuit = false;

    /**
     * Creates a new cache triage dispatcher thread.  You must call {@link #start()}
     * in order to begin processing.
     *
     * @param cacheQueue Queue of incoming requests for triage
     * @param networkQueue Queue to post requests that require network to
     * @param cache Cache interface to use for resolution
     * @param delivery Delivery interface to use for posting responses
     */
    public CacheDispatcher(
            BlockingQueue cacheQueue, BlockingQueue networkQueue,
            Cache cache, ResponseDelivery delivery) {
        mCacheQueue = cacheQueue;
        mNetworkQueue = networkQueue;
        mCache = cache;
        mDelivery = delivery;
    }

    /**
     * Forces this dispatcher to quit immediately.  If any requests are still in
     * the queue, they are not guaranteed to be processed.
     */
    public void quit() {
        mQuit = true;
        interrupt();
    }

    @Override
    public void run() {
        if (DEBUG) VolleyLog.v("start new dispatcher");
		// 设置线程优先级,Process.THREAD_PRIORITY_BACKGROUND参数表示该线程比普通线程的优先级要稍微低一点,这样不至于对用户界面交互产生较大影响
        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.
				// 从缓存BlockingQueue中读取Request,如果为空就会阻塞当前线程,当前线程就会一直处于等待状态,知道mCacheQueue中有新的Request
                final Request request = mCacheQueue.take();
                request.addMarker("cache-queue-take");
				Log.d(TAG, "--->run()" + request);
                // 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.
				// 根据缓存key值来获取缓存,这个缓存key值实际上就是该请求的url
                Cache.Entry entry = mCache.get(request.getCacheKey());
				// 如果Entry是null说明没有缓存,就添加到mNetworkQueue队列,去从网络获取
                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.
				// 如果有Entry,判断是否过期,如果过期也从网络获取
                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.
					// 使用ResponseDelivery对象调用postResponse();
					// 这个方法是ExecutorDelivery重写的方法,最终会调用到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;

                    // 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.
				// 当调用quit()方法时,线程就结束等待,子线程结束
                if (mQuit) {
					Log.d(TAG, "缓存线程被吵醒了!退出!");
                    return;
                }
                continue;
            }
        }
    }
}
     CacheDispatcher类的关键代码都在源码中注释了,这个类就是负责判断该Request是否需要进行网络请求,如果不需要就直接调用 mDelivery.postResponse(request, response);返回给用户。否则就让NetworkDispatcher来执行网络请求。下面来看下NetworkDispatcher的源码:

     

/**
 * Provides a thread for performing network dispatch from a queue of requests.
 *
 * Requests added to the specified queue are processed from the network via a
 * specified {@link Network} interface. Responses are committed to cache, if
 * eligible, using a specified {@link Cache} interface. Valid responses and
 * errors are posted back to the caller via a {@link ResponseDelivery}.
 */
@SuppressWarnings("rawtypes")
public class NetworkDispatcher extends Thread {
    /** The queue of requests to service. */
	Handler handler;
	private final String TAG = NetworkDispatcher.class.getName();
    private final BlockingQueue mQueue;
    /** The network interface for processing requests. */
    private final Network mNetwork;
    /** The cache to write to. */
    private final Cache mCache;
    /** For posting responses and errors. */
    private final ResponseDelivery mDelivery;
    /** Used for telling us to die. */
	// 使用volatile修饰,线程每次读取时都会读取最后一次修改的值
    private volatile boolean mQuit = false;

    /**
     * Creates a new network dispatcher thread.  You must call {@link #start()}
     * in order to begin processing.
     *
     * @param queue Queue of incoming requests for triage
     * @param network Network interface to use for performing requests
     * @param cache Cache interface to use for writing responses to cache
     * @param delivery Delivery interface to use for posting responses
     */
    public NetworkDispatcher(BlockingQueue queue,
            Network network, Cache cache,
            ResponseDelivery delivery) {
        mQueue = queue;
        mNetwork = network;
        mCache = cache;
        mDelivery = delivery;
    }

    /**
     * Forces this dispatcher to quit immediately.  If any requests are still in
     * the queue, they are not guaranteed to be processed.
     */
    public void quit() {
        mQuit = true;
        interrupt();
    }

    @Override
    public void run() {
		// 设置线程执行优先级,Process.THREAD_PRIORITY_BACKGROUND参数设置表示这个线程的优先级比普通的线程要稍微低一些
		// 所以,这个线程的执行对用户的界面交互的影响比较低
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
        Request request;
        while (true) {
            try {
                // Take a request from the queue.
				// 同理,如果mQueue中没有请求就处于阻塞、等待状态。
                request = mQueue.take();
				Log.d(TAG, "-->run()--" + request);
            } catch (InterruptedException e) {
                // We may have been interrupted because it was time to quit.
                if (mQuit) {
					Log.d(TAG, "网络线程被吵醒了,将要退出?!");
                    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;
                }

                // Tag the request (if API >= 14)
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
                    TrafficStats.setThreadStatsTag(request.getTrafficStatsTag());
                }

                // Perform the network request.
				// Network执行,这个方法是BasicNetwork重写的
                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) {
                parseAndDeliverNetworkError(request, volleyError);
            } catch (Exception e) {
                VolleyLog.e(e, "Unhandled exception %s", e.toString());
                mDelivery.postError(request, new VolleyError(e));
            }
        }
    }

    private void parseAndDeliverNetworkError(Request request, VolleyError error) {
        error = request.parseNetworkError(error);
        mDelivery.postError(request, error);
    }
        以上就是NetworkDispatcher类的源码了,其解析并传递请求后的Response和CacheDispatcher是一样的,就不罗嗦,主要是它进行网络请求的方法是 NetworkResponse networkResponse = mNetwork.performRequest(request);来执行的,这个方法是BasicNetwork实现的,我们看看这个类的源码,代码略多,我们只看其perfirmRequest()方法吧:

      

@Override
    public NetworkResponse performRequest(Request request) throws VolleyError {
        long requestStart = SystemClock.elapsedRealtime();
        while (true) {
            HttpResponse httpResponse = null;
            byte[] responseContents = null;
			// 响应消息头信息
            Map responseHeaders = new HashMap();
            try {
                // Gather headers.
                Map headers = new HashMap();
				// 获取该请求的缓存并将协议的Head加入到缓存
                addCacheHeaders(headers, request.getCacheEntry());
				// 执行Http请求
                httpResponse = mHttpStack.performRequest(request, headers);
				// 获取响应状态
                StatusLine statusLine = httpResponse.getStatusLine();
				// 响应码
                int statusCode = statusLine.getStatusCode();

                responseHeaders = convertHeaders(httpResponse.getAllHeaders());
                // Handle cache validation.
                if (statusCode == HttpStatus.SC_NOT_MODIFIED) {
                    return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED,
                            request.getCacheEntry().data, responseHeaders, true);
                }

                // Some responses such as 204s do not have content.  We must check.
                if (httpResponse.getEntity() != null) {
                  responseContents = entityToBytes(httpResponse.getEntity());
                } else {
                  // Add 0 byte response as a way of honestly representing a
                  // no-content request.
                  responseContents = new byte[0];
                }

                // if the request is slow, log it.
                long requestLifetime = SystemClock.elapsedRealtime() - requestStart;
                logSlowRequests(requestLifetime, request, responseContents, statusLine);

                if (statusCode < 200 || statusCode > 299) {
                    throw new IOException();
                }
                return new NetworkResponse(statusCode, responseContents, responseHeaders, false);
            } catch (SocketTimeoutException e) {
                attemptRetryOnException("socket", request, new TimeoutError());
            } catch (ConnectTimeoutException e) {
                attemptRetryOnException("connection", request, new TimeoutError());
            } catch (MalformedURLException e) {
                throw new RuntimeException("Bad URL " + request.getUrl(), e);
            } catch (IOException e) {
                int statusCode = 0;
                NetworkResponse networkResponse = null;
                if (httpResponse != null) {
                    statusCode = httpResponse.getStatusLine().getStatusCode();
                } else {
                    throw new NoConnectionError(e);
                }
                VolleyLog.e("Unexpected response code %d for %s", statusCode, request.getUrl());
                if (responseContents != null) {
                    networkResponse = new NetworkResponse(statusCode, responseContents,
                            responseHeaders, false);
                    if (statusCode == HttpStatus.SC_UNAUTHORIZED ||
                            statusCode == HttpStatus.SC_FORBIDDEN) {
                        attemptRetryOnException("auth",
                                request, new AuthFailureError(networkResponse));
                    } else {
                        // TODO: Only throw ServerError for 5xx status codes.
                        throw new ServerError(networkResponse);
                    }
                } else {
                    throw new NetworkError(networkResponse);
                }
            }
        }
    }
        上面代码呢实际上也是对HttpStack的一种包装了,真正执行请求的还是上面的httpResponse = mHttpStack.performRequest(request, headers);代码,也就是HttpStack的方法,也就是他的两个实现类的方法,我们先来看HttpClientStack的源码,代码略多,就看两个主要的:

       

    @Override
    public HttpResponse performRequest(Request request, Map additionalHeaders)
            throws IOException, AuthFailureError {
		// 创建一个Http请求
        HttpUriRequest httpRequest = createHttpRequest(request, additionalHeaders);
        addHeaders(httpRequest, additionalHeaders);
        addHeaders(httpRequest, request.getHeaders());
        onPrepareRequest(httpRequest);
        HttpParams httpParams = httpRequest.getParams();
        int timeoutMs = request.getTimeoutMs();
        // TODO: Reevaluate this connection timeout based on more wide-scale
        // data collection and possibly different for wifi vs. 3G.
		// 连接时长
        HttpConnectionParams.setConnectionTimeout(httpParams, 5000);
        HttpConnectionParams.setSoTimeout(httpParams, timeoutMs);
		// 执行请求
        return mClient.execute(httpRequest);
    }

    /**
     * Creates the appropriate subclass of HttpUriRequest for passed in request.
     */
    @SuppressWarnings("deprecation")
    /* protected */ static HttpUriRequest createHttpRequest(Request request,
            Map additionalHeaders) throws AuthFailureError {
        switch (request.getMethod()) {
            case Method.DEPRECATED_GET_OR_POST: {
                // This is the deprecated way that needs to be handled for backwards compatibility.
                // If the request's post body is null, then the assumption is that the request is
                // GET.  Otherwise, it is assumed that the request is a POST.
                byte[] postBody = request.getPostBody();
                if (postBody != null) {
                    HttpPost postRequest = new HttpPost(request.getUrl());
                    postRequest.addHeader(HEADER_CONTENT_TYPE, request.getPostBodyContentType());
                    HttpEntity entity;
                    entity = new ByteArrayEntity(postBody);
                    postRequest.setEntity(entity);
                    return postRequest;
                } else {
                    return new HttpGet(request.getUrl());
                }
            }
            case Method.GET:
                return new HttpGet(request.getUrl());
            case Method.DELETE:
                return new HttpDelete(request.getUrl());
            case Method.POST: {
                HttpPost postRequest = new HttpPost(request.getUrl());
                postRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType());
                setEntityIfNonEmptyBody(postRequest, request);
                return postRequest;
            }
            case Method.PUT: {
                HttpPut putRequest = new HttpPut(request.getUrl());
                putRequest.addHeader(HEADER_CONTENT_TYPE, request.getBodyContentType());
                setEntityIfNonEmptyBody(putRequest, request);
                return putRequest;
            }
            default:
                throw new IllegalStateException("Unknown request method.");
        }
    }
     下面的方法在上面一个方法中被调用,用于根据不同的请求方式创建不同的请求对象。
     2、Volley是如何处理多线程、并发的?

你可能感兴趣的:(Android开源项目)