Volley源码分析

volley是2013年谷歌IO大会推出的,是google在OKHttp之前的官方推荐网络框架。适用于通讯频率高,传输数量级小的使用场景,其api 简单易用,麻雀虽小却又五脏俱全

Request request = new StringRequest(url, new Response.Listener() {
    @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  Request add(Request 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> 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 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的处理逻辑如下
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的销毁时添加此逻辑,避免内存泄漏。

你可能感兴趣的:(Volley源码分析)