Android Volley 源码解析(一),ffmpeg 音视频开发实战 2019 下载
Volley 是 Google 在 2013 年的 I/O 大会上推出的 「Android 异步网络请求框架和图片加载框架」,它的设计目标就是去进行 数据量不大,但 通信频繁 的网络操作,而对于大数据量的网络操作,比如下载文件等,Volley 的表现就会非常糟糕。
Volley 的使用方法
在进行源码分析之前,先让我们来看下平时是怎样使用 Volley 的
RequestQueue requestQueue = Volley.newRequestQueue(context);
StringRequest stringRequest = new StringRequest(url
, new Response.Listener<String>() {
@Override
public void onResponse(String s) {
// TODO:
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
// TODO:
}
});
requestQueue.add(stringRequest);
1、通过 Volley.newRequestQueue(Context) 获取一个 RequestQueue
2、传入 URL 构建 Request,并实现相应的回调
3、将 Request 加入到 RequestQueue 中
Volley 中比较重要的类
在这先把 Volley 中比较重要的类说一下,到时候看源码能更加明白:
| 类名 | 作用 |
| --- | --- |
| Volley | 对外暴露的 API,主要作用是构建 RequestQueue |
| Request | 所有网络请求的抽象类,StringRequest、JsonRequest、ImageRequest 都是它的子类 |
| RequestQueue | 存放请求的队列,里面包括 CacheDispatcher、NetworkDispatcher 和 ResponseDelivery |
| Response | 封装一个解析后的结果以便分发 |
| CacheDispatcher | 用于执行缓存队列请求的线程 |
| NetworkDispatcher | 用户执行网络队列请求的线程 |
| Cache | 缓存请求结果,Volley 默认使用的是基于 sdcard 的 DiskBaseCache |
| HttpStack | 处理 Http 请求,并返回请求结果 |
| Network | 调用 HttpStack 处理请求,并将结果转换成可被 ResponseDelivery 处理的 NetworkResponse |
| ResponseDelivery | 返回结果的分发接口 |
二、请求的执行流程
我们从 Volley 的使用方法入手,一步一步探究底层的源码实现,我们的入手点就是
Volley.newRequestQueue(context)
public static RequestQueue newRequestQueue(Context context) {
return newRequestQueue(context, (BaseHttpStack) null);
}
这个方法只有一行代码,只是调用了 newRequestQueue() 的方法重载,并给第二个参数传入 null,那我们看下带有两个参数的 newRequestQueue 方法中的代码
public static RequestQueue newRequestQueue(Context context, BaseHttpSta
ck stack) {
BasicNetwork network;
if (stack == null) {
if (Build.VERSION.SDK_INT >= 9) {
network = new BasicNetwork(new HurlStack());
} else {
String userAgent = "volley/0";
try {
String packageName = context.getPackageName();
PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);
userAgent = packageName + "/" + info.versionCode;
} catch (NameNotFoundException e) {
}
network = new BasicNetwork(
new HttpClientStack(AndroidHttpClient.newInstance(userAgent)));
}
} else {
network = new BasicNetwork(stack);
}
return newRequestQueue(context, network);
}
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;
}
可以看到,这个方法中先判断 stack 是否为 null,如果是的话,这里会根据 Android 手机的系统版本号来进行相应的处理,当 SDK >= 9,则创建一个 HurlStack 实例,否则创建一个 HttpClientStack 实例,实际上 HurlStack 内部使用的是 HttpURLConnction 进行网络请求,而 HttpClientStack 则是使用 HttpClient 进行网络请求,这里之所以要这么处理,主要是因为在 Android 2.3(SDK = 9)之前,HttpURLConnection 存在一个很严重的问题,所以这时候用 HttpClient 来进行网络请求会比较合适。
不过由于现在的 Android 手机基本都是 4.0 以上的,而且 HttpClient 已经由于某些原因被弃用了,所以现在只要了解 HttpURLConnection 相关的知识就够了。思路拉回来,我们继续看代码,拿到 Stack 的实例之后将其构建成一个 Network 对象,它是用于根据传入的 Stack 对象来处理网络请求的,紧接着构建出一个 RequestQueue 对象,并调用 start() 方法。
我们接着看 start() 方法究竟做了什么:
public void start() {
stop();
mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
mCacheDispatcher.start();
for (int i = 0; i < mDispatchers.length; i++) {
NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,
mCache, mDelivery);
mDispatchers[i] = networkDispatcher;
networkDispatcher.start();
}
}
public void stop() {
if (mCacheDispatcher != null) {
mCacheDispatcher.quit();
}
for (final NetworkDispatcher mDispatcher : mDispatchers) {
if (mDispatcher != null) {
mDispatcher.quit();
}
}
}
先调用 stop() 方法将当前正在进行 Dispatcher 都停掉,然后创建了一个 CacheDispatcher 实例,并调用了它的 start() 方法,接着在一个循环里去创建 NetworkDispatcher 的实例,分别调用它们的 start() 方法,这里的 CacheDispatcher 和 NetworkDispatcher 都是继承自 Thread 的,默认情况下 for 循环会执行四次,也就是说当调用了 Volley.newRequestQueue(context) 之后,就会有五个线程在后台运行,等待网络请求的到来,其中 CacheDispatcher 是缓存线程,NetworkDispatcher 是网络请求线程。
得到 RequestQueue 之后,构建相应的 Request,然后调用 add() 方法将其加入到请求队列中
public <T> Request<T> add(Request<T> request) {
// 将 Request 标记为属于此队列,并将其放入 mCurrentRequests 中
request.setRequestQueue(this);
synchronized (mCurrentRequests) {
mCurrentRequests.add(request);
}
// 让 Request 按照他们被添加的顺序执行
request.setSequence(getSequenceNumber());
request.addMarker("add-to-queue");
//如果请求不需要被缓存,就跳过缓存,直接进行网络请求
if (!request.shouldCache()) {
mNetworkQueue.add(request);
return request;
}
mCacheQueue.add(request);
return request;
}
可以看到,传入 Request 之后,会先判断该 Request 是否需要进行缓存,如果不需要就直接将其加入到网络请求队列,需要缓存则加入缓存队列。默认情况下,每条请求都是应该缓存的,当然我们也可以调用 Request 的 setShouldCache() 方法来进行设置。
Request 被添加到缓存队列中后,在后台等待的缓存线程就要开始运行起来了,我们看下 CacheDispatcher 的 run() 方法究竟是怎么实现的。
@Override
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
// 初始化 Cache
mCache.initialize();
while (true) {
try {
processRequest();
} catch (InterruptedException e) {
if (mQuit) {
return;
}
}
}
}
private void processRequest() throws InterruptedException {
final Request<?> request = mCacheQueue.take();
request.addMarker("cache-queue-take");
// 如果请求已经取消了,我们直接结束该请求
if (request.isCanceled()) {
request.finish("cache-discard-canceled");
return;
}
// 从 Cache 中取出包含请求缓存数据的 Entry
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;
}
// 缓存的数据封装成 NetworkResponse
Response<?> response = request.parseNetworkResponse(
new NetworkResponse(entry.data, entry.responseHeaders));
if (!entry.refreshNeeded()) {
// 如果缓存没有过期就直接进行分发
mDelivery.postResponse(request, response);
} else {
// 重置该请求的 Entry
request.setCacheEntry(entry);
response.intermediate = true;
if (!mWaitingRequestManager.maybeAddToWaitingRequests(request)) {
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);
}
}
}
代码相对比较长,我在关键的地方已经打上注释了,在这里总结一下,可以看到在初始化了 Cache 之后,有一个 while(true) 循环,说明缓存线程是始终执行的,接着会在缓存中取出响应结果,如果为 null 的话,就将其加入到网络请求队列中,如果不为空的话,再判断该缓存是否已过期,已经过期则同样把这条请求加入到网络请求队列中,否则直接使用缓存中的数据。最后将数据进行解析,并进行分发。
看完 CacheDispathcer 的 run() 方法,我们接着看 NetworkDispatcher 的 run() 方法
@Override
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
while (true) {
try {
processRequest();
} catch (InterruptedException e) {
if (mQuit) {
return;
}
}
}
}
private void processRequest() throws InterruptedException {
Request<?> request = mQueue.take();
long startTimeMs = SystemClock.elapsedRealtime();
try {
request.addMarker("network-queue-take");
// 如果 Request 已经取消了,那就不执行网络请求
if (request.isCanceled()) {
request.finish("network-discard-cancelled");
request.notifyListenerResponseNotUsable();
return;
}
addTrafficStatsTag(request);
// 执行网络请求
NetworkResponse networkResponse = mNetwork.performRequest(request);
// 如果服务器返回 304,而且我们已经分发过该 Request 的结果,那就不用进行第二次分发了
//(这里补充一下,304 代表服务器上的结果跟上次访问的结果是一样的,也就是说数据没有变化)
if (networkResponse.notModified && request.hasHadResponseDelivered()) {
request.finish("not-modified");
request.notifyListenerResponseNotUsable();
return;
}
// 在子线程解析返回的结果
Response<?> response = request.parseNetworkResponse(networkResponse);
// 如果需要的话,就将返回结果写入缓存
if (request.shouldCache() && response.cacheEntry != null) {
mCache.put(request.getCacheKey(), response.cacheEntry); }
// 分发响应结果
request.markDelivered();
mDelivery.postResponse(request, response);
request.notifyListenerResponseReceived(response);
} catch (VolleyError volleyError) {
request.notifyListenerResponseNotUsable();
} catch (Exception e) {
request.notifyListenerResponseNotUsable();
}
}
评论