Volley的Request的添加、移除和取消

来源:互联网 发布:淘宝三无产品如何投诉 编辑:程序博客网 时间:2024/05/19 04:52

本文系转载,转载于Volley源码分析 1,原文作者写的不错,很赞!

我们先来看一下Volley的架构图:
这里写图片描述

可以看出,Volley至少工作在3个线程当中,其中

蓝色部分为主线程:主要的工作是将请求按照优先级的顺序添加到cache的队列当中,当发出去的请求的得到相应的时候,在主线程将结果进行分发。
绿色部分为cache线程:如果cache hit,那么直接将cache中的数据进行解析,并传递给主线程,如果miss,那么则交给NetworkDispatcher进行处理。
黄色部分则为网络线程:与cache线程不同,cache只有一个线程在工作,而网络线程则可以有多个同时工作,进行网络请求,解析结果,写入cache,最终也是响应结果交给主线程。
我们以一段代码为例,跟踪代码的流程,对比上面的框架图,看看Volley到底是如何工作的。

    RequestQueue mQueue = Volley.newRequestQueue(MainActivity.this);    JsonObjectRequest jsonObjectRequest = new JsonObjectRequest("http://m.weather.com.cn/data/101010100.html", null, new Response.Listener<JSONObject>() {                                  @Override                                  public void onResponse(JSONObject response) {                                    // TODO Auto-generated method stub                                    Log.i(TAG, response.toString());                                  }},new Response.ErrorListener() {                                    @Override                                    public void onErrorResponse(VolleyError error) {                                      // TODO Auto-generated method stub                                      Log.i(TAG, error.getMessage());                                    }});    mQueue.add(jsonObjectRequest);

首先,我们新建了一个RequestQueue的实例,当然在实际使用中,我们应该使用ApplicationContext作为VolleyContext

然后,我们新建了一个JsonObjectRequest,即一个Json的请求,在其中的回调函数onResponse中添加我们收到结果要做的事情,当然我们知道这部分代码是运行在主线程当中。

最后,将JsonObjectReques的实例添加到RequestQueue当中。

这就是Volley使用的全部的代码,总体看来,我们所能看到的所有的请求都是进入RequestQueue,然后就等待处理,得到onResponse的响应,因此,我们可以跟随代码,从mQueue.add(jsonObjectRequest)来分析。

  • 任务的添加:mQueue.add(jsonObjectRequest)
   public <T> Request<T> add(Request<T> request) {        // Tag the request as belonging to this queue and add it to the set of current requests.        //将request与该任务队列相关联。        request.setRequestQueue(this);        //将该请求放在任务队列的待做任务当中。        synchronized (mCurrentRequests) {            mCurrentRequests.add(request);        }        // Process requests in the order they are added.        // 设定序列号        request.setSequence(getSequenceNumber());        // 添加日志        request.addMarker("add-to-queue");        // If the request is uncacheable, skip the cache queue and go straight to the network.        // 如果该日志不需要Cache的话,那么跳过cache的队列,直接进行网络请求        if (!request.shouldCache()) {            mNetworkQueue.add(request);            return request;        }        // 如果程序运行到这里,说明需要缓存cache,那么进行的操作是,先检查当前的任务有没有在cache的运行当中,        // 如果正在进行,或者说cache对应的cacheKey有Reqeust正在执行,那么则直接加入到cacheKey对应的队列当中即可。        // 如果需要cache,而且没有正在这行,则添加到等待队列和cache队列当中。        // Insert request into stage if there's already a request with the same cache key in flight.        // 同步任务队列,根据该请求是否添加到RequestQueue的不同情况,分别处理        synchronized (mWaitingRequests) {            //判断等待队列是否包含当前添加的任务            String cacheKey = request.getCacheKey();            if (mWaitingRequests.containsKey(cacheKey)) {                // There is already a request in flight. Queue up.                // 该cacheKey对应的任务之前添加过,并且还没有处理完成。则取出cacheKey对应的任务队列,将该任务添加进去。                Queue<Request<?>> stagedRequests = mWaitingRequests.get(cacheKey);                if (stagedRequests == null) {                    stagedRequests = new LinkedList<Request<?>>();                }                stagedRequests.add(request);                mWaitingRequests.put(cacheKey, stagedRequests);                if (VolleyLog.DEBUG) {                    VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold.", cacheKey);                }            } else {                // Insert 'null' queue for this cacheKey, indicating there is now a request in                // flight.                // 与上面不同的是,当前的任务没有处理过,所以将任务添加到等待队列中,然后添加到cache的队列中。                mWaitingRequests.put(cacheKey, null);                mCacheQueue.add(request);            }            return request;        }    }

上面的代码就是RequestQueueadd()方法。我们可以画一下它的流程图

这里写图片描述

从中,可以看出,RequestQueue中add(Request request)所做的工作为:

  • 绑定request到此RequestQueue.this
  • request添加到mCuurentRequest的链表中
  • request设置序列号,并打印Log
  • 根据request是否需要cache,如果不需要cache,则直接将其放入mNetworkueue当中。
  • 如果request需要cache,则检查该request对应的cacheKey(一般实际上使用URL)是否已经在mWaitingRequests列表中存在,如果已经存在,那么则更新cacheKey所对应的列表,如果不存在,则将其放进mCacheQueue中,再放入mNetworkQueue中等待执行。
  • add(Request request)方法执行完毕,返回。

此时我们也可以明白在RequestQueue中的几个集合类的作用:

//该map的作用是用来缓存正在执行的需要Cache的Requestprivate final Map<String, Queue<Request<?>>> mWaitingRequests = new HashMap<String, Queue<Request<?>>>();//所有的需要处理的Reqeust都会在这个集合当中private final Set<Request<?>> mCurrentRequests = new HashSet<Request<?>>();//mCacheQueue保存的是需要从Cache中获取的Requestprivate final PriorityBlockingQueue<Request<?>> mCacheQueue = new PriorityBlockingQueue<Request<?>>();//mNetworkQueue保存的是需要网络操作的Requestprivate final PriorityBlockingQueue<Request<?>> mNetworkQueue = new PriorityBlockingQueue<Request<?>>();

对应的RequestQueueadd方法,我们再来看一下对应的finish(Request<?> request)方法。

  • 任务的结束:finish(Request<?> request)

这里写图片描述

可以看出,RequestQueuefinish(Request request)主要做了以下的事情:

  • reqeust从当前的任务列表mCurrentRequests中删除
  • 判断该request是否是需要cache的请求,如果不是,则直接退出即可。
  • 如果该request需要cache,那么则删除所对应的cacheKey,然后将等待的请求全部加入到mCacheQueuecache的任务队列当中。其实finish()调用的并不是说任务要取消,而是说任务已经完成了,所以对应的也很容易理解了,首先该任务完成了,就应该从RequestQueue中消失,如果不需要cache,那么直接退出就可以啦,如果需要cache,那么就可以删除该cacheKey(URL)对应的请求列表,因为这个时候网络任务应该已经完成,所以将剩下的任务加入到mCacheQueue当中,让他们从cache中获取就可以。)。
    还有一点需要注意的是,该方法是的访问限定符是默认的,即包访问权限,并且在Volley的源代码中,仅仅是Request.finish(String tag)调用了该方法。

接下来我们进一步分析一下Request中的finish(String tag)方法,同时我们注意到,对于Reqeust主要是一些标记变量和关于请求的内容,在逻辑上其他的相关的代码并不多。

 /**     * Notifies the request queue that this request has finished (successfully or with error).     *     * 通知请求队列,该请求已经完成,或者成功,或者存在错误。     *      * <p>Also dumps all events from this request's event log; for debugging.</p>     * <p>导出event log的所有的时间,用来调试。</p>     *      * 该方法可能来自CacheDisptacher,ResponseDelivery,NetworkDispatcher的调用     */    void finish(final String tag) {        // 通知RequestQueue停止该任务        if (mRequestQueue != null) {            mRequestQueue.finish(this);        }        //如果需要记录日志,则记录之。        if (MarkerLog.ENABLED) {            //获取线程Id            final long threadId = Thread.currentThread().getId();            //判断当前的Looper的线程是否为主线程,也就是判断当前的代码是否在主线程上运行。            if (Looper.myLooper() != Looper.getMainLooper()) {                // If we finish marking off of the main thread, we need to                // actually do it on the main thread to ensure correct ordering.                // 如果我们是在其他的线程(非main线程)上取消任务,那么我们需要在主线程上来完成以保证正确的顺序。                // 这边的顺序没有特别看到,为什么要这样设计。                //获取主线程的handler                Handler mainThread = new Handler(Looper.getMainLooper());                //分发取消请求的任务。                mainThread.post(new Runnable() {                    @Override                    public void run() {                        mEventLog.add(tag, threadId);                        mEventLog.finish(this.toString());                    }                });                return;            }            //这里就是主线程            mEventLog.add(tag, threadId);            mEventLog.finish(this.toString());        } else {            long requestTime = SystemClock.elapsedRealtime() - mRequestBirthTime;            if (requestTime >= SLOW_REQUEST_THRESHOLD_MS) {                VolleyLog.d("%d ms: %s", requestTime, this.toString());            }        }    }

此处代码比较简单,Requestfinish方法主要是调用了RequestQueuefinish()方法来标记完成任务。进而打印相关的Log信息,此处有一点不太明白的是,为什么一定要在主线程上打印Log?

任务的取消:cancelAll(final Object tag)

  /**     * Cancels all requests in this queue for which the given filter applies.     * @param filter The filtering function to use     */    public void cancelAll(RequestFilter filter) {        synchronized (mCurrentRequests) {            for (Request<?> request : mCurrentRequests) {                if (filter.apply(request)) {                    request.cancel();                }            }        }    }    /**     * Cancels all requests in this queue with the given tag. Tag must be non-null     * and equality is by identity.     */    public void cancelAll(final Object tag) {        if (tag == null) {            throw new IllegalArgumentException("Cannot cancelAll with a null tag");        }        cancelAll(new RequestFilter() {            @Override            public boolean apply(Request<?> request) {                return request.getTag() == tag;            }        });    }

这里面才是真正的取消任务,其主要的工作如下:

同步mCurrentRequests,因为要遍历任务,所以要同步该集合。
依次判断Request是否符合tag或者RequestFilter的要去,如果符合,则取消相应的任务。
Requestcancel方法则更加简单,如下:

    public void cancel() {        mCanceled = true;    }

现在看来,如果要取消某个RequestVolley不会主动的要求Network或者Cache的工作线程停止当前的正在执行的Request,而是在执行过程中,如果发现ReqeustmCanceled标记被设置,那么就不再进行下一步操作。

原创粉丝点击