Volley使用步骤一般来首就是三步:
1.创建一个Request对象。如下所示,创建一个StringRequest对象
StringRequest request = new StringRequest(
url,
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
//主线程中执行
}
},
new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
}
});
2.创建一个队列 。可以直接使用Volley这个工具类直接创建。如下所示:
RequestQueue queue = Volley.newRequestQueue(context);
3.将请求加入到队列中去。
queue.add(request);
好了,上述几步就完成了。
1.首先看一下ReqeustQueue是如何创建的。
显然,上面是通过Volley这个工具类创建的,看看源码得知,无论是调用了那个方法创建RequestQueue, 都会走Volley的这个方法:
private static RequestQueue newRequestQueue(Context context, Network network) {
File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);
RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
queue.start();
return queue;
}
看到了,RequestQueue中有两个参数。
第一个参数DiskBasedCache,实际上就是一个磁盘缓存。
第二个参数network,是一个接口
public interface Network {
/**
* 该方法用来执行网络请求
*/
NetworkResponse performRequest(Request<?> request) throws VolleyError;
}
看到了,内部只有一个方法,就是用来执行网络请求的。实现该接口的只有一个类,即BasicNetwork。
至于 BasicNetwork 内部使用的是 HttpURLConnection 还是 HttpClient , 这个真的不需要关系,只知道这是用来执行网络请求的就可以了。
两个参数的意义都明白了,接下来就看看RequestQueue了。首先我们得明白RequestQueue中这几个重要变量是干嘛的.
//需要从缓存中取数据的Request队列.也就是说,在该缓存中的Request,可能不需要从网络中获取数据了,直接从磁盘缓存中读取就可以。
private final PriorityBlockingQueue<Request<?>> mCacheQueue = new PriorityBlockingQueue<>();
// 需要执行网络请求的Request队列。该队列中的Request是必须要联网,然后执行网络请求的。
private final PriorityBlockingQueue<Request<?>> mNetworkQueue = new PriorityBlockingQueue<>();
//执行网络请求的方式。不用管,只需要记住调用该类的performRequest(request)就开始从网络上获取数据即可。
private final Network mNetwork;
//在主线程中,该类包含一个Handler.就是用来切换线程的。
private final ResponseDelivery mDelivery;
//线程池。该线程池是用来执行网络请求的
private final NetworkDispatcher[] mDispatchers;
//单个线程。用来从缓存中取出数据,显然,从缓存中取数据需要在子线程中执行。
private CacheDispatcher mCacheDispatcher;
下面来一一解析这些变量对应的类。
首先是两个阻塞队列,也就是mCacheQueue 与 mNetworkQueue ,对应的类是java中的,具体代码pass, 不用管。
接下来是mNetwork ,对应的接口是Network ,这接口已经在上面说过了,具体的实现类以及实现类的具体代码,不用管啦。
接下来是mDelivery,对应的接口是 ResponseDelivery ,实际上这也是个接口,唯一的实现类是 ExecutorDelivery ,在RequestQueue中也可以看到他是这样被实例化的
//RequestQueue的构造函数
public RequestQueue(Cache cache, Network network, int threadPoolSize) {
this(
cache,
network,
threadPoolSize,
new ExecutorDelivery(new Handler(Looper.getMainLooper())));
}
看到了吧,Looper.getMainLooper(),说明是在主线程中咯。在看看ExecutorDelivery 里面几个重要的方法。
/**
* 构造函数,可以看到会把Runnable放到主线程执行(因为参数handler就是主线程的)。
*/
public ExecutorDelivery(final Handler handler) {
// Make an Executor that just wraps the handler.
mResponsePoster =
new Executor() {
@Override
public void execute(Runnable command) {
handler.post(command);
}
};
}
/**
* 该方法由外部调用。记住了,这个方法很重要。
*/
@Override
public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {
request.markDelivered();
request.addMarker("post-response");
mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));
}
上面吧request,response,runnable为参数构造了一个ResponseDeliveryRunnable对象,实际上ResponseDeliveryRunnable就是runnable的一个实现类。看看run方法:
public void run() {
if (mRequest.isCanceled()) {
mRequest.finish("canceled-at-delivery");
return;
}
if (mResponse.isSuccess()) {
//啰嗦了一大堆,就这句有用。
mRequest.deliverResponse(mResponse.result);
} else {
mRequest.deliverError(mResponse.error);
}
if (mResponse.intermediate) {
mRequest.addMarker("intermediate-response");
} else {
mRequest.finish("done");
}
if (mRunnable != null) {
mRunnable.run();
}
}
最重要的一句,mRequest.deliverResponse(mResponse.result),接着可以去看看Request接口的deliverResponse方法了。接口的方法有啥好看的,直接看实现类,随便找一个,最简单的,开头提到的StringRequest:
/**
* 把结果传递到listener的onResponse方法中
*/
@Override
protected void deliverResponse(String response) {
Response.Listener<String> listener;
synchronized (mLock) {
listener = mListener;
}
if (listener != null) {
listener.onResponse(response);
}
}
看到没有,listener这货,就是开头创建StringRequest传入的参数。所以,到就可以明白了Response是如何传入到主线程中了吧,实际上就是Handler机制。
好吧,跑偏了。回到RequestQueue,看看里面有啥方法吧.
入手是从start方法开始,为啥呢?因为在 Volley#newRequestQueue 方法里面可以看到,创建玩RequestQueue后,就直接调用了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.
//初始化线程池里面的每个线程,并开启线程。默认4个线程
for (int i = 0; i < mDispatchers.length; i++) {
NetworkDispatcher networkDispatcher =
new NetworkDispatcher(mNetworkQueue, mNetwork, mCache, mDelivery);
mDispatchers[i] = networkDispatcher;
networkDispatcher.start();
}
}
start方法作用就是开启线程。
那么我们就看看两个线程吧。
首先是CacheDispatcher,继承了Thread.
@Override
public void run() {
if (DEBUG) VolleyLog.v("start new dispatcher");
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
mCache.initialize();
//直接来个无限循环。这里的意思就是不断的查看队列里是否有数据。看不懂这里不要紧,向下看就知道了。
while (true) {
try {
//执行请求,就看这个方法可以了,也就这有用
processRequest();
} catch (InterruptedException e) {
if (mQuit) {
Thread.currentThread().interrupt();
return;
}
......
}
}
}
private void processRequest() throws InterruptedException {
//从缓存队列中取出Request
final Request<?> request = mCacheQueue.take();
processRequest(request);
}
@VisibleForTesting
void processRequest(final Request<?> request) throws InterruptedException {
request.addMarker("cache-queue-take");
//请求取消
if (request.isCanceled()) {
request.finish("cache-discard-canceled");
return;
}
//如果缓存中不存在,就把该请求加入到mNetworkQueue中(也就是要从网络中获取)
Cache.Entry entry = mCache.get(request.getCacheKey());
if (entry == null) {
request.addMarker("cache-miss");
if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) {
mNetworkQueue.put(request);
}
return;
}
//缓存过期,同上
if (entry.isExpired()) {
request.addMarker("cache-hit-expired");
request.setCacheEntry(entry);
if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) {
mNetworkQueue.put(request);
}
return;
}
//缓存有效,包装为一个Response返回
request.addMarker("cache-hit");
Response<?> response =
request.parseNetworkResponse(
new NetworkResponse(entry.data, entry.responseHeaders));
request.addMarker("cache-hit-parsed");
if (!entry.refreshNeeded()) {
mDelivery.postResponse(request, response);
} else {
request.addMarker("cache-hit-refresh-needed");
request.setCacheEntry(entry);
response.intermediate = true;
if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) {
//看到了没有,ExecutorDelivery#postResponse方法被调用了,说明这里直接把结果返回给主线程了
mDelivery.postResponse(
request,
response,
new Runnable() {
@Override
public void run() {
try {
mNetworkQueue.put(request);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
});
} else {
mDelivery.postResponse(request, response);
}
}
}
再来看看NetworkDispatcher类,实际上跟CacheDispatcher里面的方法都一样,只是执行的策略不一样。所以我这里不明白为啥不抽象出一个借口,而是这两个方法为啥都是直接继承Thread的。不明白啊,不明白,不明白……
@VisibleForTesting
void processRequest(Request<?> request) {
long startTimeMs = SystemClock.elapsedRealtime();
try {
request.addMarker("network-queue-take");
if (request.isCanceled()) {
request.finish("network-discard-cancelled");
request.notifyListenerResponseNotUsable();
return;
}
addTrafficStatsTag(request);
// performRequest方法就是用来完成网络请求的,这里就这句最重要
NetworkResponse networkResponse = mNetwork.performRequest(request);
request.addMarker("network-http-complete");
if (networkResponse.notModified && request.hasHadResponseDelivered()) {
request.finish("not-modified");
request.notifyListenerResponseNotUsable();
return;
}
Response<?> response = request.parseNetworkResponse(networkResponse);
request.addMarker("network-parse-complete");
if (request.shouldCache() && response.cacheEntry != null) {
mCache.put(request.getCacheKey(), response.cacheEntry);
request.addMarker("network-cache-written");
}
request.markDelivered();
mDelivery.postResponse(request, response);
request.notifyListenerResponseReceived(response);
} catch (VolleyError volleyError) {
......
} catch (Exception e) {
......
}
}
好了,RequestQueue就说道这里了。其实基本上大概内容都差不多了。
ok, 接下来终结一下 ,步骤如下:
- Volley创建一个RequestQueue,并调用 RequestQueue#Start 方法.
- RequestQueue#Start 方法中,默认会初始化5个线程,一个 CacheDispatcher 和四个 NetworkDispatcher ,并使用 start 方法开启线程.
- CacheDispatcher #start 执行后,对于线程,直接看 run 方法,可以看到会有一个死循环去调用 processRequest 方法,在 processRequest 方法中会不断的查找缓存队列mCacheQueue是否有Request,如果没有,就阻塞。如果有,就直接根据该Request 查看缓存中是否有缓存。如果有缓存且可用,就直接调用 ExecutorDelivery#postResponse 返回结果,如果没有就添加到另一个缓存队列 mNetworkQueue 中;NetworkDispatcher 类中的方法与 CacheDispatcher 方法相同,只是在processRequest 中会直接根据 Request 调用 BasicNetwork#performRequest 方法去从网络上取数据。
- ExecutorDelivery 是在RequestQueue 中实例化的,并且在该类中,传入了由主线程的 Looper 构造成的 Handler 对象。postResponse 方法会把 response, request 封装成一个 Runnable 实现类 ResponseDeliveryRunnable的一个实例,然后调用 Executor#execute方法执行这个 Runable 对象。而在ExecutorDelivery 的构造函数中会重写 Executor#execute 方法,直接把Runable对象使用Handler.post方法抛给主线程执行.
- ResponseDeliveryRunnable 中,run 方法会调用 Request#deliverResponse 方法。查看Request的实现类,大致都是把 response 交给 listener#onResponse处理。所以onResponse 方法是在主线程中执行的。
2.创建Request对象。这个不说了,并不重要,我管他怎么创建的呢。
3. 将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.
//标记当前请求。其实就是把当前的RequestQueue传入到Request中
request.setRequestQueue(this);
//表示当前Request可以被立即处理啦
synchronized (mCurrentRequests) {
mCurrentRequests.add(request);
}
// Process requests in the order they are added.
request.setSequence(getSequenceNumber());
request.addMarker("add-to-queue");
//不需要缓存,就可以直接加入mNetworkQueue
if (!request.shouldCache()) {
mNetworkQueue.add(request);
return request;
}
mCacheQueue.add(request);
return request;
}
这里为啥加入了就不要管理呢?为啥request会自动执行呢?其实就是那两个Thread的实现类的功劳了。CacheDispatcher和NetworkDispatcher。因为他们的run方法中会无限循环,一直查看队列中是否有元素。一旦有元素,就直接被拿出去执行了。是不是有点像 Handler 机制里面 Looper 那样,一直在监听队列中是否有数据。
我的大概了解就是这么多,很多细节方面并没有去具体的了解,水平不够,以后再看。