抽时间看了下Volley的源码,感觉这个框架还是很不错的,这里对其源码进行分析。
GitHub链接
主要从以下几个方面分析:
a. 简要介绍 Volley 的使用
b. Volley 访问网络的整体流程
c. 对源码中一些重要的类的总结
简要介绍 Volley 的使用
// 创建请求队列
RequestQueue mQueue = Volley.newRequestQueue(context); // 创建请求
StringRequest stringRequest = new StringRequest("http://www.baidu.com",
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
Log.d("TAG", response);
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.e("TAG", error.getMessage(), error);
}
});
// 添加请求到队列中
mQueue.add(stringRequest);
Volley 访问网络的整体流程
首先通过 Volley#newRequestQueue() 创建请求队列。
在Volley类中,一共就提供了两个静态的重载方法 newRequestQueue()
主要看下 newRequestQueue(Context, HttpStack)
public static RequestQueue newRequestQueue(Context context) {
return newRequestQueue(context, null);
}
public static RequestQueue newRequestQueue(Context context, HttpStack stack){
return newRequestQueue(context, stack, -1);
}
//最终调用这个方法
//context 用于获取disk缓存地址
//HttpStack 用于network , null for default
//maxDiskCacheBytes 最大disk缓存大小 。-1 for default size 是5M
public static RequestQueue newRequestQueue(Context context, HttpStack stack, int maxDiskCacheBytes) {
//默认内部缓存路径 data/data/packagename/cache/ volley/文件夹
File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);//volley
String userAgent = "volley/0";
try {
String packageName = context.getPackageName();
PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);
//请求默认userAgent
userAgent = packageName + "/" + info.versionCode;
} catch (NameNotFoundException e) {
}
if (stack == null) {
if (Build.VERSION.SDK_INT >= 9) {
//使用默认HttpStack,这个类的作用是是用httpUrlConnection请求网络;
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));
}
}
//使用httpstack构造 Network对象 。封装网络请求的一些操作
Network network = new BasicNetwork(stack);
RequestQueue queue;
if (maxDiskCacheBytes <= -1)
{
// No maximum size specified
//使用Network对象和缓存路径,构造 RequestQueue 请求队列对象。 创建请求队列并启动
queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
}
else
{
// Disk cache size specified
queue = new RequestQueue(new DiskBasedCache(cacheDir, maxDiskCacheBytes), network);
}
//请求队列运行
queue.stcart();
return queue;
}
可以看到,其中主要创建了Network,HttpStack,RequestQueue,并启动了RequestQueue
最终网络访问就封装在HttpStack中,这个后面再看
先看一下请求队列类 RequestQueue
/**
* A request dispatch queue with a thread pool of dispatchers.
*
* Calling {@link #add(Request)} will enqueue the given Request for dispatch,
* resolving from either cache or network on a worker thread, and then delivering
* a parsed response on the main thread.
*/
//带有调度线程池 的 请求调度队列 。
//通过add 方法添加请求到队列中等待调度,在一个工作线程中要么缓存要么请求网络,然后发送已经解析的响应给主线程
public class RequestQueue {
/** 请求完成回调接口 */
public static interface RequestFinishedListener<T> {
/** 请求执完了回调接口. */
public void onRequestFinished(Request<T> request);
}
/**
* Staging area for requests that already have a duplicate request in flight.
*/
//HashMap形式的缓存队列,对于已经没有执行完/正在执行的相同的请求,被放进到等待请求队列中。
private final Map<String, Queue<Request<?>>> mWaitingRequests =
new HashMap<String, Queue<Request<?>>>();
/**
* The set of all requests currently being processed by this RequestQueue. A Request
* will be in this set if it is waiting in any queue or currently being processed by
* any dispatcher.
*/
//当前请求队列正在执行的请求集合 CurrentRequest
private final Set<Request<?>> mCurrentRequests = new HashSet<Request<?>>();
/** The cache triage queue. */
//请求缓存队列 使用的是阻塞队列的形式,用来缓存请求的,下面那个Cache是缓存响应的
private final PriorityBlockingQueue<Request<?>> mCacheQueue =
new PriorityBlockingQueue<Request<?>>();
/** The queue of requests that are actually going out to the network. */
真正网络请求的阻塞队列
private final PriorityBlockingQueue<Request<?>> mNetworkQueue =
new PriorityBlockingQueue<Request<?>>();
/** Number of network request dispatcher threads to start. */
//默认网络调度线程的数量是4个
private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4;
/** Cache interface for retrieving and storing responses. */
//文件缓存对象,获取并存储响应用的文件缓存对象
private final Cache mCache;
/** Network interface for performing requests. */
//真正访问网络的 Network对象
private final Network mNetwork;
/** Response delivery mechanism. */
//响应发送机制
private final ResponseDelivery mDelivery;
/** The network dispatchers. */
//网络调度线程 默认是4个
private NetworkDispatcher[] mDispatchers;
/** The cache dispatcher. */
//缓存调度线程
private CacheDispatcher mCacheDispatcher;
//请求执行完成回调接口集合
private List<RequestFinishedListener> mFinishedListeners =
new ArrayList<RequestFinishedListener>();
//请求队列构造函数 其他几个构造函数就不说了
public RequestQueue(Cache cache, Network network, int threadPoolSize,
ResponseDelivery delivery) {
mCache = cache;
mNetwork = network;
mDispatchers = new NetworkDispatcher[threadPoolSize];
mDelivery = delivery;
}
/**
* Starts the dispatchers in this queue.
*/
//请求队列启动这个队列 这个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();
// 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();
}
}
/**
* Gets a sequence number.
*/
//获取序列号
public int getSequenceNumber() {
return mSequenceGenerator.incrementAndGet();
}
/**
* Gets the {@link Cache} instance being used.
*/
public Cache getCache() {
return mCache;
}
/**
* Adds a Request to the dispatch queue.
* @param request The request to service
* @return The passed-in request
*/
在构建了请求队列后,发起请求只要把请求添加到请求队列中就好了,接下来看下RequestQueue#add()代
码 这里挑一些主要的代码看
public <T> Request<T> add(Request<T> request) {
// Tag the request as belonging to this queue and add it to the set of current requests.
//给这个请求打上属于这个队列的ta,并且把它添加到当前队列中
request.setRequestQueue(this);
synchronized (mCurrentRequests) {
1,添加在当前正在执行的集合中去
mCurrentRequests.add(request);
}
// Process requests in the order they are added.
2, 设置这个请求的序列号,即给每个请求一个唯一号
request.setSequence(getSequenceNumber());
request.addMarker("add-to-queue");
// If the request is uncacheable, skip the cache queue and go straight to the network.
3, 判断这个请求是否可缓存,如果不可缓存直接添加到Network队列
if (!request.shouldCache()) {
mNetworkQueue.add(request);
return request;
}
// Insert request into stage if there's already a request with the same cache key in flight.
如果有相同的请求正在执行但是没有返回结果,就把当前的这个请求加入到等待请求的HashMap中
synchronized (mWaitingRequests) {
请求中得到这个缓存的key
String cacheKey = request.getCacheKey();
看看mWaitingRequests的map中有没有同一个key的请求队列
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<?>>();
}
如果有同一个cachekey的队列,就加进去
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.
如果没有同一个cachekey的的request队列,就插入null,添加到 “缓存请求阻塞队列”中,说明有个新的请求,被缓存了
mWaitingRequests.put(cacheKey, null);
mCacheQueue.add(request);
}
return request;
}
}
从上面的RequestQueue类中可以看出这个类干了什么活。
在成员变量上
1, final Map<String, Queue<Request<?>>> mWaitingRequests = new HashMap<String, Queue<Request<?>>>();
请求等待Map,当同一个CacheKey的request正在执行/没有返回结果,就把同一个key的Request放进这个请求等待Map。
2,final PriorityBlockingQueue<Request<?>> mCacheQueue = new PriorityBlockingQueue<Request<?>>()
缓存请求的阻塞队列 。
3,final PriorityBlockingQueue<Request<?>> mNetworkQueue = new PriorityBlockingQueue<Request<?>>()
真正发送出去的网络请求队列
4 , final Set<Request<?>> mCurrentRequests = new HashSet<Request<?>>()
当前正在执行的Request的集合。当add请求时,直接将Request加入到这个集合中,在判断这个Request是否可缓存
5,final Cache mCache
响应的硬盘缓存
6,final Network mNetwork
正真访问网络的接口,其实现类封装了HttpUrlConnection的访问
7,final ResponseDelivery mDelivery
发送响应到主线程的句柄
8,NetworkDispatcher[] mDispatchers
网络调度线程
9,CacheDispatcher mCacheDispatcher
缓存调度线程
缓存调度线程和网络调度线程在start函数中传递构造对象是 传递是4个参数是mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
,缓存请求阻塞队列mCacheQueue,网络请求阻塞队列mNetworkQueue,响应硬盘缓存mCache,发送响应的句柄mDelivery,都是final修饰的,是说明这两个线程中,必定对这几个变量做着某种操作,目前猜测。
在方法上,主要是start()方法和add()方法
1, start方法,实例化缓存调度线程,CacheDispatcher,网络调度线程NetworkDispatcher
2, add方法,先将Request直接加入 “当前正在执行的Request的集合中mCurrentRequest”中,再为此Request加入唯一的序列号,在判断此Request是否可以缓存,不可缓存直接加入"网络请求阻塞队列mNetworkQueue"中,可以缓存,如果waitingRequest中存在相同Key的Request正在被执行,就加入到waitingRequest中,如果不存在就加入“缓存请求阻塞队列" cacheQueue中,请求需要缓存的话,会加入到缓存队列中,交给缓存派发器处理,否则交给网络请求派发器处理
到目前为止,Volley.newRequestQueue()方法完成了,即我们的网络请求第一步,建立请求队列完成。
先小结一下:建立请求队列所做的工作是,创建文件缓存(默认),实例化BasicNetwork,实例化Delivery用于发送线程请求,创建一条缓存线程和四条网络请求线程(默认)并运行。
网络请求Request的实现原理
在创建完请求队列后,接着就是建立一个请求,请求的方式可以是StringRequest、JsonArrayRequest或者ImageRequest等,那么这些请求的背后原理是什么呢?我们拿最简单的StringRequest来说,它继承自Request,而Request则是所有请求的父类,所以说如果你要自定义一个网络请求,就应该继承自Request。接下来我们看看StringRequest的源码,因为不管Request的子类是什么,大体的实现思路都是一致的,所以我们弄懂了StringRequest,那么对于其他的请求类的理解是相通的
public class StringRequest extends Request<String> {
private Listener<String> mListener;
public StringRequest(int method, String url, Listener<String> listener,
ErrorListener errorListener) {
super(method, url, errorListener);
mListener = listener;
}
/**
* Creates a new GET request.
*
* @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(String url, Listener<String> listener, ErrorListener errorListener) {
this(Method.GET, url, listener, errorListener);
}
@Override
protected void onFinish() {
super.onFinish();
mListener = null;
}
发送最终的响应到回调接口
@Override
protected void deliverResponse(String response) {
if (mListener != null) {
mListener.onResponse(response);
}
}
解析网络响应的方法
@Override
protected Response<String> parseNetworkResponse(NetworkResponse response) {
String parsed;
try {
parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
} catch (UnsupportedEncodingException e) {
parsed = new String(response.data);
}
return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));
}
}
我们主要关注的是deliverResponse方法和parseNetworkResponse。可以看出,这两个方法都是重写的,我们翻看父类Request的对应方法,发现是抽象方法,说明这两个方法在每一个自定义的Request中都必须重写。这里简单说说这两个方法的作用。
先看deliverResponse方法:它内部调用了mListener.onResponse(response)方法,而这个方法正是我们在写一个请求的时候,添加的listener所重写的onResponse方法,也就是说,响应成功后在这里调用了onResponse()方法。
接着看pareNetworkResponse方法:可以看出这里主要是对response响应做出一些处理。可以对比一下不同请求类的这个方法,都会不同的,所以说,这个方法是针对不同的请求类型而对响应做出不同的处理。比如说,如果是StringRequest,则将响应包装成String类型;如果是JsonObjectRequest,则将响应包装成JsonObject。那么现在应该清楚了:对于想要得到某一种特殊类型的请求,我们可以自定义一个Request,重写这两个方法即可。
小结一下:Request类做的工作主要是初始化一些参数,比如说请求类型、请求的url、错误的回调方法;而它的任一子类重写deliverResponse方法来实现成功的回调,重写parseNetworkResponse()方法来处理响应数据;至此,一个完整的Request请求搭建完毕。
添加请求
前面已经完成了请求队列的创建,Request请求的创建,那么接下来就是把请求添加进队列了。我们看RequestQueue#add()源码
/**
* Adds a Request to the dispatch queue.
* @param request The request to service
* @return The passed-in request
*/
public <T> Request<T> add(Request<T> request) {
// Tag the request as belonging to this queue and add it to the set of current requests.
标记当前请求,表示这个请求由当前RequestQueue处理
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.
如果有相同请求正在处理,那么把这个请求放进mWaitingRequest中,等待。
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中,同时也放进“缓存请求队列 ”mCacheQueue中
这代表这个请求已经开始在 缓存线程 中运行了
mWaitingRequests.put(cacheKey, null);
mCacheQueue.add(request);
}
return request;
}
}
得出如下结论:在这个add方法中,主要判断一个Request请求是否可以缓存(默认是可以缓存的),如果不可以则直接添加到网络请求队列mNetworkQueue,开始网络通信;如果可以,则进一步判断当前是否有相同的请求正在进行,如果有相同的请求,则让这个请求放进mWaitingRequest中,排队等待,如果没有相同的请求,则直接放进 缓存请求队列 mCacheQueue中,就交给缓存调度线程处理了
可以看一下add流程图,来自网络:
缓存调度线程 和 网络调度线程
通过上面的解释,目前请求队列RequestQueue和Request已经构建好了,接下类就该看看具体的网络请求是怎么发出的,怎么访问的,以及请求的响应是怎么回调到主线程的
缓存调度线程
在RequestQueue的start方法中实例化了缓存线程并开始运行,一直处于等待状态,而上面把请求添加进了缓存线程,此时缓存线程就开始真正的工作了。我们来看缓存线程的源码,主要看它的run()方法,CacheDispatcher#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();
Request<?> request;
死循环
while (true) {
// release previous request object to avoid leaking request object when mQueue is drained.
request = null;
try {
// Take a request from the queue.
从缓存队列中取出请求
request = mCacheQueue.take();
} ...
try {
request.addMarker("cache-queue-take");
// If the request has been canceled, don't bother dispatching it.
if (request.isCanceled()) {
request.finish("cache-discard-canceled");
continue;
}
// Attempt to retrieve this item from cache.
从文件缓存中取出这个请求的响应结果
Cache.Entry entry = mCache.get(request.getCacheKey());
if (entry == null) {
request.addMarker("cache-miss");
// Cache miss; send off to the network dispatcher.
响应结果是丢失,就计入网络请求队列,交给网络请求调度线程NetworkDispacter处理
mNetworkQueue.put(request);
continue;
}
// If it is completely expired, just send it to the network.
判断缓存是否过期,如果过期,,在加入网络请求队列,交给网络调度线程NetworkDispacter处理
if (entry.isExpired()) {
request.addMarker("cache-hit-expired");
request.setCacheEntry(entry);
重新加入网络请求对列,交给网络请求调度线程NetworkDispacter处理
mNetworkQueue.put(request);
continue;
}
// We have a cache hit; parse its data for delivery back to the request.
如果文件缓存的响应,没有过期,封装成NetworkResponse,返回Request的解析网络响应的方法parseNetworkResponse
request.addMarker("cache-hit");
先将响应的结果包装成NetworkResponse,然后调用Request子类的
parseNetworkResponse方法解析数据
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.
调用ExecutorDelivey#postResponse方法
异步把响应response 发送到request中
mDelivery.postResponse(request, response);
} else {
....
}
} catch (Exception e) {
VolleyLog.e(e, "Unhandled exception %s", e.toString());
}
}
}
while(true)循环,表示它一直在等待缓存队列的新请求的出现。
接着,先判断这个请求是否有对应的缓存结果,如果没有则直接添加到网络请求队列;
接着,再判断这个缓存结果是否过期了,如果过期则同样地添加到网络请求队列;
接下来便是对缓存结果的处理了,我们可以看到,先是把缓存结果包装成NetworkResponse类,然后调用了Request的parseNetworkResponse,这个方法我们在part2说过,子类需要重写这个方法来处理响应数据。最后,把处理好的数据post到主线程,这里用到了ExecutorDelivery#postResponse()方法,下面会分析到。
小结:CacheDispatcher线程主要对请求的响应进行,判断是否已经有缓存响应,是否已经过期,根据需要放进网络请求队列。同时对相应结果进行包装、处理,然后交由ExecutorDelivery处理。
这里以一张流程图显示它的完整工作流程:
网络请求线程
上面提到,请求不能缓存、缓存结果不存在、缓存过期的时候会把请求添加进请求队列,此时一直等待的网络请求线程由于获取到请求,终于要开始工作了,我们来看NetworkDispatcher#run()方法:
@Override
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
Request<?> request;
while (true) {
long startTimeMs = SystemClock.elapsedRealtime();
// release previous request object to avoid leaking request object when mQueue is drained.
request = null;
try {
// Take a request from the queue.
从队列中取Request
request = mQueue.take();
} ...
try {
...
// Perform the network request.
调用BasicNetwork实现类进行网络请求,并获得响应①
NetworkResponse networkResponse = mNetwork.performRequest(request);
...
// Parse the response here on the worker thread.
对响应进行解析
Response<?> response = request.parseNetworkResponse(networkResponse);
request.addMarker("network-parse-complete");
...
request.markDelivered();
发送响应到主线程
mDelivery.postResponse(request, response);
// Write to cache if applicable.
将响应结果写进缓存
if (request.shouldCache() && response.cacheEntry != null) {
mCache.put(request.getCacheKey(), response.cacheEntry);
request.addMarker("network-cache-written");
}
}
}
}
源码做了适当的删减,大体上和CacheDispatcher的逻辑相同,这里关注①号代码,这里调用了BasicNetwork#perfromRequest()方法,把请求传递进去,可以猜测,这个方法内部实现了网络请求的相关操作,那么我们进去看看,
@Override
public NetworkResponse performRequest(Request<?> request) throws VolleyError {
long requestStart = SystemClock.elapsedRealtime();
while (true) {
HttpResponse httpResponse = null;
byte[] responseContents = null;
Map<String, String> responseHeaders = Collections.emptyMap();
try {
// Gather headers.
Map<String, String> headers = new HashMap<String, String>();
addCacheHeaders(headers, request.getCacheEntry());
mHttpStack执行request
httpResponse = mHttpStack.performRequest(request, headers); // 1
StatusLine statusLine = httpResponse.getStatusLine();
int statusCode = statusLine.getStatusCode();
responseHeaders = convertHeaders(httpResponse.getAllHeaders());
// Handle cache validation.
if (statusCode == HttpStatus.SC_NOT_MODIFIED) {
Entry entry = request.getCacheEntry();
if (entry == null) {
return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, null,
responseHeaders, true,
SystemClock.elapsedRealtime() - requestStart); // 2
}
// A HTTP 304 response does not have all header fields. We
// have to use the header fields from the cache entry plus
// the new ones from the response.
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.5
entry.responseHeaders.putAll(responseHeaders);
return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, entry.data,
entry.responseHeaders, true,
SystemClock.elapsedRealtime() - requestStart);
}
...
}
}
主要看①号代码,mHttpStack.performRequest();这里调用了mHttpStack的performRequest方法,那么mHttpStack是什么呢?我们可以翻上去看看RequestQueue实例化BasicNetwork的时候传递的stack值,该值就是根据不同的系统版本号而实例化的HttpStack对象(版本号大于9的是HurlStack,小于9的是HttpClientStack),由此可知,这里实际调用的是HurlStack.performRequest()方法,
方法的内部基本是关于HttpUrlConnection的逻辑代码,这里就不展开说了。
可以这么说:HurlStack封装好了HttpUrlConnection,而HttpClientStack封装了HttpClient。该方法返回了httpResponse,接着把这个响应交由②处处理,封装成NetworkResponse对象并返回。在NetworkDispatcher#run()方法获取返回的NetworkResponse对象后,对响应解析,通过ExecutorDelivery#postResponse()方法回调解析出来的数据,这个过程和CacheDispatcher相同。
ExecutorDelivery 通知主线程
在CacheDispatcher和NetworkDispatcher中最后都有调用到ExecutorDelivery#postResponse()方法,那么这个方法到底是做什么呢?由于缓存线程和网络请求线程都不是主线程,所以主线程需要有“人”通知它网络请求已经完成了,而这个“人”正是由ExecutorDelivery充当。在完成请求后,通过ExecutorDelivery#postResponse()方法,最终会回调到主线程中重写的onResponse()方法。我们看看这个方法的源码ExecutorDelivery#postResponse():
@Override
public void postResponse(Request<?> request, Response<?> response) {
postResponse(request, response, null);
}
@Override
public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {
request.markDelivered();
request.addMarker("post-response");
mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));
}
在方法内部调用了mResponsePoster#execute()方法,那么,这个mResponsePoster是在哪里来的呢?其实这个成员变量是在ExecutorDelivery实例化的时候同时实例化的,而ExecutorDelivery则是在RequestQueue实例化的时候同时实例化的,读者可以自行查看相应的构造方法,其实这些工作在par 1建立请求队列的时候已经全部做好了。接着我们可以看以下代码,ExecutorDelivery#ExecutorDelivery():
public ExecutorDelivery(final Handler handler) {
// Make an Executor that just wraps the handler.
实例化Executor,并且重写execute方法
mResponsePoster = new Executor() {
@Override
public void execute(Runnable command) {
这里获取的handler是主线程的handler
handler.post(command);
}
};
}
execute()方法接收一个Runnable对象,那么我们回到上面的
mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));
可以看出这里实例化了一个ResponseDeliveryRunnable对象作为Runnable对象。而这里的ResponseDeliveryRunnable则是当前类Executor的一个内部类,实现了Runnable接口,我们来看看:
/**
* A Runnable used for delivering network responses to a listener on the
* main thread.
*/
被用来发送网络到主线程的runnable
@SuppressWarnings("rawtypes")
private class ResponseDeliveryRunnable implements Runnable {
private final Request mRequest;
private final Response mResponse;
private final Runnable mRunnable;
//构造方法
...
@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
if (mResponse.isSuccess()) {
调用request的deliverResponse方法
mRequest.deliverResponse(mResponse.result); // 1
} else {
mRequest.deliverError(mResponse.error);
}
...
}
}
}
总结
用google官方的一幅图来说明以上所说各个部分的关系(图片来自网络):
Volley的优缺点
优点
自动的调度网络请求
多并发的网络请求
可以缓存http请求
可以缓存响应
支持取消请求的API,可以取消单个请求,可以设置取消请求的范围域。
缺点
使用的是httpclient、HttpURLConnection
6.0不支持httpclient了,如果想支持得添加org.apache.http.legacy.jar
非常不适合大的文件流操作,例如上传和下载。因为Volley会把所有的服务器端返回的数据在解析期间缓存进内存。
只支持http请求
图片加载性能一般
参考链接:
https://www.jianshu.com/p/15e6209d2e6f
http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2017/0605/8040.html