volley
是2013年谷歌IO大会推出的,是google在OKHttp
之前的官方推荐网络框架。适用于通讯频率高,传输数量级小的使用场景,其api 简单易用,麻雀虽小却又五脏俱全
Request request = new StringRequest(url, new Response.Listener<String>() {
@Override
public void onResponse(String response) {
response 在UI线程回调
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
error 在UI线程回调
}
}).setShouldCache(false).setTag(UserActivity.this);
Volley.newRequestQueue(this).add(request);
示例代码相当简单,实例化一个RequestQueen
,然后实例化一个Request
并add到queue中结束。如下是具体过程解析
Request构建
基于面向对象的设计思路,每个请求封装为Request
对象,需要关注的重要属性如下:
-
mMethod
包含对象的请求方式, -
mErrorListener/mListener
处理监听 -
retryPolicy
重试策略 -
mUrl
请求地址 -
mShouldCache
是否缓存(默认为true) -
mCacheEntry
缓存数据 -
mTag
,请求的标记 -
mCanceled\mResponseDelivered
当前请求的处理状态
RequestQueue构建
- 1.
Volley.newRequestQueue(this)
追溯源码可看到多个重构方法,最终都会调用
示例代码,不要关注语法错误
public static RequestQueue newRequestQueue(Context context, @Nullable HttpStack stack, int maxDiskCacheBytes) {
定义缓存目录
File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);
定义请求agent
String userAgent = appPackageName + "/" + appVersionCode;
HttpClientStack 拼装网络请求成HttpUriRequest并交给HttpClient执行以获取response
HttpClientStack stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
BasicNetwork中转层,获取到原始response后根据StatusLine信息拼装为volley的NetworkResponse
Network network = new BasicNetwork(stack);
RequestQueue传送带/任务分配者,默认手下有4个等待干活的员工(NetworkDispatcher),一个同步传送带BlockingQueue输出任务
RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir, maxDiskCacheBytes), network);
queue.start();
return queue;
}
在方法体的最后创建了一个RequestQueue
对象并调用了start()
方法
public class RequestQueue {
private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4;
public RequestQueue(Cache cache, Network network, int threadPoolSize,ResponseDelivery delivery) {
mCache = cache; 缓存对象
mNetwork = network; 网络请求中转层
mDispatchers = new NetworkDispatcher[threadPoolSize]; 4个全天无休不要加班费的机器员工
mDelivery = delivery; 流水线最后的成品分发
}
public void start() {
stop(); // Make sure any currently running dispatchers are stopped.
缓存调度器
mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
mCacheDispatcher.start();
依据threadPoolSize创建指定数量的网络调度器
for (int i = 0; i < mDispatchers.length; i++) {
NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,mCache, mDelivery);
mDispatchers[i] = networkDispatcher;
networkDispatcher.start();
}
}
}
- 2.具体的联网请求操作:
RequestQueue.add(Request)
public <T> Request<T> add(Request<T> request) {
request.setRequestQueue(this);
synchronized (mCurrentRequests) {
mCurrentRequests.add(request);
}
......
不需要缓存的请求则直接添加到mNetworkQueue并返回
if (!request.shouldCache()) {
mNetworkQueue.add(request);
return request;
}
将请求添加到缓存队列中
synchronized (mWaitingRequests) {
String cacheKey = request.getCacheKey();
Queue<Request<?>> stagedRequests = mWaitingRequests.get(cacheKey);
stagedRequests.add(request);
mWaitingRequests.put(cacheKey, stagedRequests);
return request;
}
}
接下来梳理下volley的工作流程
- 1:通过
RequestQueue newRequestQueue()
创建RequestQueue或者通过RequestQueue的构造方法定制自己。默认创建4个NetworkDispatcher
和 在主线程处理回调的ExecutorDelivery
- 2:
request
被添加至mNetworkQueue
后。NetworkDispatcher
对象通过BlockingQueue#take获取到。并在子线程中获取原始response
以下为NetworkDispatcher
中的关键代码:
public class NetworkDispatcher extends Thread {
@Override
public void run() {
PriorityBlockingQueue在take()时会上锁,确保线程安全,每个Dispatcher不会取到重复Request
Request<?> request = BlockingQueue.take();
if (request.isCanceled()) {
request.finish("network-discard-cancelled");
continue;
}
NetworkResponse networkResponse = BasicNetwork.performRequest(request);
Response<?> response = request.parseNetworkResponse(networkResponse)
request.markDelivered();
mDelivery.postResponse(request, response);
(catch VolleyError | Exception error){
mDelivery.postError(request, error)
}
}
}
public class BasicNetwork{
中介,24小时开业揽活,然后把活分给HttpStack
public NetworkResponse performRequest(Request<?> request){
while (true) {
httpResponse = mHttpStack.performRequest(request, headers);
StatusLine statusLine = httpResponse.getStatusLine();
int statusCode = statusLine.getStatusCode();
if (statusCode == HttpStatus.SC_NOT_MODIFIED) {
Entry entry = request.getCacheEntry();
return new NetworkResponse(······);
}else {······}
}
}
}
public class HttpClientStack implements HttpStack{
Volley 通过newRequestQueue构造的 AndroidHttpClient
protected final HttpClient mClient;
@Override
public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders){
将volley的Request转换为HttpClient使用的HttpUriRequest
HttpUriRequest httpRequest = createHttpRequest(request, additionalHeaders);
addHeaders(httpRequest, additionalHeaders);
addHeaders(httpRequest, request.getHeaders());
onPrepareRequest(httpRequest);
HttpParams httpParams = httpRequest.getParams();
int timeoutMs = request.getTimeoutMs();
HttpConnectionParams.setConnectionTimeout(httpParams, 5000);
HttpConnectionParams.setSoTimeout(httpParams, timeoutMs);
最终活分配给了HttpClient去执行
return mClient.execute(httpRequest);
}
}
- 3:最终的响应分发器:ExecutorDelivery
- 1 ExecutorDelivery 在构造函数中创建了一个Executor
public ExecutorDelivery(final Handler handler) { mResponsePoster = (Executor)(commond)→{handler.post(command);} }
- 2 postError/postResponse都调用了
mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, null));
- 3 ResponseDeliveryRunnable的处理逻辑如下
- 1 ExecutorDelivery 在构造函数中创建了一个Executor
class ResponseDeliveryRunnable implements Runnable{
@Override
public void run() {
判断请求是否取消,避免网络延迟时deliverResponse导致context泄漏
if (mRequest.isCanceled()) {
mRequest.finish("canceled-at-delivery");
return;
}
分发请求的响应,已StringRequest为例,无操作直接扔给mListener.onResponse(response);
if (mResponse.isSuccess()) {
mRequest.deliverResponse(mResponse.result);
} else {
mRequest.deliverError(mResponse.error);
}
}
Volley使用时的细节与注意事项
-
1:如果使用静态构造
newRequestQueue()
则不要调用start()
方法,虽然start内部会先做stop逻辑停止可能存在的调度器任务,但是重复的Thread
创建(NetworkDispatcher)
仍然是内存浪费。而且如果Thread # interrupt()
没有成功,则GC会无法回收废弃的NetworkDispatcher -
2:request的缓存问题,当服务器返回304状态码时,volley会直接使用cache数据。但是查找cache的CacheKey默认是
mMethod + ":" + mUrl
,如果使用的是post请求,那么cacheKey就必须自定义。可能需要overWrite或者的方法有:-
Request#setShouldCache(boolean)
是否缓存,默认true
,对于表单提交或者刷新操作可设置此参数 -
Request#getCacheKey()
设置缓存key -
Request#getParams()
设置请求参数 -
Request#getHeaders()
设置请求header
-
-
3:Request管理。如果发起网络请求后没有关注activity或者fragment的生命周期,极有可能导致内存泄漏甚至崩溃。比如弱网时,用户放弃等待离开当前页面,但是请求还在执行,分发response至已经finish的activity或者detached的fragment,导致内存泄漏。需要关注的方法有:
-
Request#setTag(Object tag)
给单个request加上tag标记,如果mListener
或者mErrorListener
使用的是activity/fragment
的内部类或者mvp结构,建议必加此tag标记 -
RequestQueue#cancelAll(Object tag)
批量取消拥有指定tag的request,一般请求可在Activity和fragment的销毁时添加此逻辑,避免内存泄漏。
-
网友评论