美文网首页
Volley源码分析

Volley源码分析

作者: 鹈鹕醍醐 | 来源:发表于2018-12-04 14:34 被阅读11次

    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的处理逻辑如下
    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源码分析

          本文链接:https://www.haomeiwen.com/subject/smywpftx.html