Volley源码分析(一)

本文深入分析了Volley框架的实现原理,包括RequestQueue的工作流程、CacheDispatcher和NetworkDispatcher的运作机制,以及响应处理机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

关于android的http的请求,相信大家都清楚主要的调用方式有两种,一个是HttpURLConnection,另一个是HttpClient,这两个相信大家都用过,也可能和笔者有同样的感受,就是仍然需要我们自己实现一些功能,调动起来并不是很便捷,当然既然这样我们便会寻找一些框架,来简化我们的工作,而volley无疑是比较好的选择,关于volley的使用,大家可以自行百度,比较简单,这里就不再介绍,我们主要从源码层面上,分析下volley的实现原理。

Volley源码的下载地址:

首先我们看下volley调用的入口toolbox下的volley类,其逻辑比较简单,只一个newRequestQueue()的静态方法,如下:

public static RequestQueue newRequestQueue(Context context, HttpStack stack)
  {
    File cacheDir = new File(context.getCacheDir(), "volley");

    String userAgent = "volley/0";
    try {
      String packageName = context.getPackageName();

      PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);
      userAgent = packageName + "/" + info.versionCode;
    }
    catch (PackageManager.NameNotFoundException localNameNotFoundException) {
    }
    if (stack == null) {
      if (Build.VERSION.SDK_INT >= 9) {
        stack = new HurlStack();
      }
      else
      {
        stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
      }
    }

    Network network = new BasicNetwork(stack);

    RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
    queue.start();

    return queue;
  }

可以看到这个方法主要目的获取RequestQueue这个请求队列的对象,并调用start()方法开启队列的轮询。而在new RequestQueue()对象的时候,我们会传入两个参数,一个缓存Cache的具体实现类DiskBasedCache对象,另一个是对HttpStack进行二次封装的Network对象,具体这两个参数的作用,我们后续还会再次分析,接下来我们先看RequestQueue,这个类是我们在使用volley这个框架时主要的接触对象。
首先我们看下RequestQueue的构造方法RequestQueue():

  public RequestQueue(Cache cache, Network network, int threadPoolSize, ResponseDelivery delivery)
  {
    this.mCache = cache;
    this.mNetwork = network;
    this.mDispatchers = new NetworkDispatcher[threadPoolSize];
    this.mDelivery = delivery;
  }

这个方法很简单,主要是些赋值操作,我们在此列出来是想主要说下这些参数的含义,方便我们的后续代码阅读和理解:
cache 负责存储http 缓存的类。
network 负责具体执行http请求的类,对HttpStack进行了二次封装。
threadPoolSize 在新建网络请求处理对象mDispatchers时传递的线程数,默认为4。
delivery: 负责将经过newwork二次封装后的response的具体执行,将response进行error和正常情况的分流处理。
通过上面的参数我们可以看出,其实RequestQueue主要是将请求分为缓存请求和网络请求,然后内部进行调用,因而逻辑并不复杂。
接下来我们看下volley调用的RequestQueue的start()方法:

  public void start()
  {
    stop();

    this.mCacheDispatcher = new CacheDispatcher(this.mCacheQueue, this.mNetworkQueue, this.mCache, this.mDelivery);
    this.mCacheDispatcher.start();

    for (int i = 0; i < this.mDispatchers.length; i++) {
      NetworkDispatcher networkDispatcher = new NetworkDispatcher(this.mNetworkQueue, this.mNetwork, 
        this.mCache, this.mDelivery);
      this.mDispatchers[i] = networkDispatcher;
      networkDispatcher.start();
    }
  }

首先调用stop(),中断并清空之前的缓存和网络请求;再重新创建对象并调用start()方法开启线程,这里我们要说明下,CacheDispatcher和NetworkDispatcher作为缓存请求和网络请求的分发机制,其本质都是Thread,只是根据业务逻辑进行了二次封装。看到这里我们会很想探知CacheDispatcher和NetworkDispatcher的具体实现,先稍等下,我们还需要分析下RequestQueue的add()方法,这个会是我们平时接触最多的方法。

public Request add(Request request)
  {
    request.setRequestQueue(this);
    synchronized (this.mCurrentRequests) {
      this.mCurrentRequests.add(request);
    }

    request.setSequence(getSequenceNumber());
    request.addMarker("add-to-queue");

    if (!request.shouldCache()) {
      this.mNetworkQueue.add(request);
      return request;
    }

    synchronized (this.mWaitingRequests) {
      String cacheKey = request.getCacheKey();
      if (this.mWaitingRequests.containsKey(cacheKey))
      {
        Queue stagedRequests = (Queue)this.mWaitingRequests.get(cacheKey);
        if (stagedRequests == null) {
          stagedRequests = new LinkedList();
        }
        stagedRequests.add(request);
        this.mWaitingRequests.put(cacheKey, stagedRequests);
        if (VolleyLog.DEBUG) {
          VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold.", new Object[] { cacheKey });
        }
      }
      else
      {
        this.mWaitingRequests.put(cacheKey, null);
        this.mCacheQueue.add(request);
      }
      return request;
    }
  }

将resquest添加到mCurrentRequests集合,判断resquest是否需要缓存,如果不需要则将request添加到网络请求队列mNetworkQueue,并返回request。如果需要缓存,获取request对应的缓存key cacheKey,判断mWaitingRequests是已否含有这个key,如果没有,则将cacheKey添加到mWaitingRequests,并将将request添加到mCacheQueue;如果含有则request添加到cacheKey对应的mWaitingRequests中的queue中。
下面我们分别看CacheDispatcher和NetworkDispatcher。我们知道在volley中http的请求策略是如果需要缓存则会先从缓存当中去,如果缓存中没有则会再去网络中请求,当然如果不需要缓存,则会之后走网络请求。所以我们先分析CacheDispatcher。
我们依然先看构造函数:

public CacheDispatcher(BlockingQueue<Request> cacheQueue, BlockingQueue<Request> networkQueue, Cache cache, ResponseDelivery delivery)
  {
    this.mCacheQueue = cacheQueue;
    this.mNetworkQueue = networkQueue;
    this.mCache = cache;
    this.mDelivery = delivery;
  }

仍然都是赋值的操纵,可以看出CacheDispatcher主要是对缓存请求队列cacheQueue,网络请求队列networkQueue,response的执行者delivery,以及缓存策略实体cache进行操作。看到这里我们可以猜测下,这个类的主要策略思路,因为之前我们有分析过ImageLoader的缓存,同理我们猜测这里先是在缓存队列cacheQueue获得请求,并从cache获取请求对应的缓存resposne,将response通过delivery进行解包、分流;如果操作失败,则将请求转入到网络请求当中。
因为CacheDispatcher是继承的Thread,我们主要看run()方法,首先设定线程优先级,然后初始化cache,当mQuit不为true时进行while循环,我们来看while循环中的具体逻辑:

final Request request = (Request)this.mCacheQueue.take();
          request.addMarker("cache-queue-take");

          if (request.isCanceled()) {
            request.finish("cache-discard-canceled");
          }
          else
          {
            Cache.Entry entry = this.mCache.get(request.getCacheKey());
            if (entry == null) {
              request.addMarker("cache-miss");

              this.mNetworkQueue.put(request);
            }
            else if (entry.isExpired()) {
              request.addMarker("cache-hit-expired");
              request.setCacheEntry(entry);
              this.mNetworkQueue.put(request);
            }
            else
            {
              request.addMarker("cache-hit");
              Response response = request.parseNetworkResponse(
                new NetworkResponse(entry.data, entry.responseHeaders));
              request.addMarker("cache-hit-parsed");

              if (!entry.refreshNeeded())
              {
                this.mDelivery.postResponse(request, response);
              }
              else
              {
                request.addMarker("cache-hit-refresh-needed");
                request.setCacheEntry(entry);

                response.intermediate = true;

                this.mDelivery.postResponse(request, response, new Runnable()
                {
                  public void run() {
                    try {
                      CacheDispatcher.this.mNetworkQueue.put(request);
                    } catch (InterruptedException localInterruptedException) {
                    }
                  } } );
              }
            }
          }

这里的逻辑主要是在mCacheQueue中取出request,判断它是否结束,如果结束则直接返回;否则从mCache中获取request的对应缓存内容,如果内容为空则走网络请求,如果不为空判断是否失效,失效的话也走网络请求;如果没有失效,则将缓存中的内容封装为NetworkResponse并同过parseNetworkResponse()方法返回response。最后看缓存是否有刷新需求,没有的话直接调用mDelivery.postResponse()返回response,有的话返回response并通过Runnable启个延时线程,将request添加到网络请求队列。

接下来我们看网络请求分发线程NetworkDispatcher,关于构造方法我们就不再复述了,直接看run()函数:

 public void run()
  {
    Process.setThreadPriority(10);
    while (true)
    {
      Request request;
      try {
        request = (Request)this.mQueue.take();
      } catch (InterruptedException e) {
      }
      if (this.mQuit) {
        return;
        try
        {
          request.addMarker("network-queue-take");

          if (request.isCanceled()) {
            request.finish("network-discard-cancelled");
          }
          else
          {
            if (Build.VERSION.SDK_INT >= 14) {
              TrafficStats.setThreadStatsTag(request.getTrafficStatsTag());
            }

            NetworkResponse networkResponse = this.mNetwork.performRequest(request);
            request.addMarker("network-http-complete");

            if ((networkResponse.notModified) && (request.hasHadResponseDelivered())) {
              request.finish("not-modified");
            }
            else
            {
              Response response = request.parseNetworkResponse(networkResponse);
              request.addMarker("network-parse-complete");

              if ((request.shouldCache()) && (response.cacheEntry != null)) {
                this.mCache.put(request.getCacheKey(), response.cacheEntry);
                request.addMarker("network-cache-written");
              }

              request.markDelivered();
              this.mDelivery.postResponse(request, response);
            }
          } } catch (VolleyError volleyError) { parseAndDeliverNetworkError(request, volleyError); }
        catch (Exception e)
        {
          Request request;
          VolleyLog.e(e, "Unhandled exception %s", new Object[] { e.toString() });
          this.mDelivery.postError(request, new VolleyError(e));
        }
      }
    }
  }

首先设定线程的优先级,从网络请求队列中获取request,判断request是否结束,如果没结束则通过mNetwork调用performRequest()方法执行网络请求,并将得到reponse,并根据需求进行处理,如果需要缓存则将缓存添加到mCache,然后将reponse通过mDelivery进行分发。

接下来我们看下response的处理机制ExecutorDelivery,ExecutorDelivery是接口ResponseDelivery的具体实现类,关于ResponseDelivery比较简单,定义了异常返回和正常返回的处理方法,这里不再做说明,我们先看下他的构造函数:

  public ExecutorDelivery(final Handler handler)
  {
    this.mResponsePoster = new Executor()
    {
      public void execute(Runnable command) {
        handler.post(command);
      }
    };
  }

定义了一个Executor对象,通过主线程UI的Handler的post()方法来处理Runnable对象。

下面我们来看正常情况下的返回处理逻辑:

public void postResponse(Request<?> request, Response<?> response, Runnable runnable)
  {
    request.markDelivered();
    request.addMarker("post-response");
    this.mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));
  }

新建ResponseDeliveryRunnable对象,并通过execute进行处理,当然这时的该Runnable对象是交给构造方法中的Handler进行处理的,在默认的逻辑,这个Handler是UI主线程的。
关于异常的处理。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值