Android Volley 源码解析(一),网络请求的执行流程

本文详细介绍了Volley框架的设计目标、使用方法、重要类及其职责,以及请求的执行流程,包括RequestQueue的创建、网络请求的处理和缓存策略。
摘要由CSDN通过智能技术生成

一、Volley 简介



Volley 是 Google 在 2013 年的 I/O 大会上推出的 「Android 异步网络请求框架和图片加载框架」,它的设计目标就是去进行 数据量不大,但 通信频繁 的网络操作,而对于大数据量的网络操作,比如下载文件等,Volley 的表现就会非常糟糕。

Volley 的使用方法

在进行源码分析之前,先让我们来看下平时是怎样使用 Volley 的

RequestQueue requestQueue = Volley.newRequestQueue(context);

StringRequest stringRequest = new StringRequest(url

, new Response.Listener() {

@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, BaseHttpStack 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 Request add(Request 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); }

写在最后

今天关于面试的分享就到这里,还是那句话,有些东西你不仅要懂,而且要能够很好地表达出来,能够让面试官认可你的理解,例如Handler机制,这个是面试必问之题。有些晦涩的点,或许它只活在面试当中,实际工作当中你压根不会用到它,但是你要知道它是什么东西。

最后在这里小编分享一份自己收录整理上述技术体系图相关的几十套腾讯、头条、阿里、美团等公司的面试题,把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分。

还有 高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料 帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。

【Android核心高级技术PDF文档,BAT大厂面试真题解析】

【算法合集】

【延伸Android必备知识点】

《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!
官认可你的理解,例如Handler机制,这个是面试必问之题。有些晦涩的点,或许它只活在面试当中,实际工作当中你压根不会用到它,但是你要知道它是什么东西。

最后在这里小编分享一份自己收录整理上述技术体系图相关的几十套腾讯、头条、阿里、美团等公司的面试题,把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分。

还有 高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料 帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。

【Android核心高级技术PDF文档,BAT大厂面试真题解析】

[外链图片转存中…(img-no08aucX-1714639152484)]

【算法合集】

[外链图片转存中…(img-1Mv3bWkq-1714639152485)]

【延伸Android必备知识点】

[外链图片转存中…(img-7WNf8zsf-1714639152486)]

《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!

  • 24
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值