Volley为我们提供了五种基本的网络请求方式,分别是StringRequest、JsonRequest、JsonObjectRequest、JsonArrayRequest、ImageRequest。下面分别介绍这五种请求的用法。
一、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<String>() { @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<JSONObject>() { @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<JSONArray>() { @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<Bitmap>() { @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); <pre name="code" class="java"> // 为图片设置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<String> 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<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>();上面两个变量是保证线程同步的关键所在,在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<Request> mCacheQueue; /** The queue of requests going out to the network. */ private final BlockingQueue<Request> 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<Request> cacheQueue, BlockingQueue<Request> 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<Request> 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<Request> 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<String, String> responseHeaders = new HashMap<String, String>(); try { // Gather headers. Map<String, String> headers = new HashMap<String, String>(); // 获取该请求的缓存并将协议的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<String, String> 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<String, String> 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."); } }下面的方法在上面一个方法中被调用,用于根据不同的请求方式创建不同的请求对象。