Volley源码学习(处理请求)

本文详细介绍了Volley网络请求库的工作原理与流程,包括请求队列管理、缓存策略、网络请求处理及响应结果分发等核心环节。

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

概述

今天复习了下Volley,刚好这几天一直在学习源码,然后参考了网上的文章,以在最后回调onResponse和onErrorResponse为目的去学习它。关于它的用法可以看这一篇。

它的流程大致是这样的:创建请求>存放到当前请求队列>判断是否有相同的请求,有则存放到暂存队列,无则到网络请求队列;在暂存队列中进入下一步是放到缓存队列,判断状态>是否需要重新到网络队列最后是响应请求。

开始

这是一个最基础的用法。

//1.创建一个请求队列
        final RequestQueue request = Volley.newRequestQueue(MainActivity.this);
        //2.创建一个请求
        StringRequest stringRequest = new StringRequest(url, new Response.Listener<String>() {
            @Override
            public void onResponse(String response) {
                tv_result.setText(response);
            }
        }, new Response.ErrorListener() {//发生异常
            @Override
            public void onErrorResponse(VolleyError error) {
                tv_result.setText("加载错误");
            }
        });

        //3.将创建的请求添加到请求队列中去
        request.add(stringRequest);
        //取消请求
        request.cancelAll("take");

进入newRequestQueue(MainActivity.this):创建RequestQueue

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

        if (stack == null) {
            if (Build.VERSION.SDK_INT >= 9) {
            //使用HttpUrlConnection
                stack = new HurlStack();
            } else {
               //使用HttpClient
                stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
            }
        }

        Network network = new BasicNetwork(stack);
        //在此创建RequestQueue
        RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
        queue.start();

        return queue;
    }

HurlStack和HttpClientStack,他们分别是对应HttpUrlConnection和HttpClient。看其他文章才知道这是由于在api9之前,HttpUrlConnection的BUG多,但在API9以后,由于HttpUrlConnection的BUG修复了很多,加上他是一个轻量型的框架,所以在API9之后使用HttpUrlConnection。

RequestQueue的构造方法
public RequestQueue(Cache cache, Network network, int threadPoolSize,
            ResponseDelivery delivery) {
        mCache = cache;//缓存策略
        mNetwork = network;//网络
        mDispatchers = new NetworkDispatcher[threadPoolSize];//调度器,默认的threadPoolSize为4
        mDelivery = delivery;//分发器
    }
RequestQueue的start方法
public void start() {
        stop();  

        mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
          //开启缓存调度,它是一个Thread
        mCacheDispatcher.start();

        // Create network dispatchers (and corresponding threads) up to the pool size.
        for (int i = 0; i < mDispatchers.length; i++) {
            NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,
                    mCache, mDelivery);
            mDispatchers[i] = networkDispatcher;
            //开始网络调度
            networkDispatcher.start();
        }
    }

以下是缓存调度和网络调度

CacheDispatcher的run方法

 @Override
    public void run() {
        if (DEBUG) VolleyLog.v("start new dispatcher");
        //设置优先级(后台)
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);

        // Make a blocking call to initialize the cache.
        mCache.initialize();//初始化缓存

        while (true) {//进入循环
            try {
                //从队列中获取请求
                final Request<?> request = mCacheQueue.take();//阻塞操作
                request.addMarker("cache-queue-take");//标记请求为缓存suo

                //如果请求取消了,就不进行分发或者发送请求
                if (request.isCanceled()) {
                    request.finish("cache-discard-canceled");
                    continue;
                }

                //根据缓存的key去找磁盘上的缓存,它的key是url
                Cache.Entry entry = mCache.get(request.getCacheKey());
                if (entry == null) {
                //说明不存在,标记为miss,然后加入网络队列,进行新的循环
                    request.addMarker("cache-miss");
                    mNetworkQueue.put(request);
                    continue;
                }
                //检查是否过期
                if (entry.isExpired()) {
                //说明已经过期了,标记为expired,加入网络队列,进行新的循环
                    request.addMarker("cache-hit-expired");
                    request.setCacheEntry(entry);
                    mNetworkQueue.put(request);
                    continue;
                }

                //到这里说明缓存可用
                request.addMarker("cache-hit");
                //解析响应
                Response<?> response = request.parseNetworkResponse(
                        new NetworkResponse(entry.data, entry.responseHeaders));
                request.addMarker("cache-hit-parsed");

                if (!entry.refreshNeeded()) {
                    //不需要刷新,直接分发响应
                    mDelivery.postResponse(request, response);
                } else {

                    //需要刷新,因为是一个软过期请求,所以进行分发然后也要进行发送请求。
                    request.addMarker("cache-hit-refresh-needed");
                    request.setCacheEntry(entry);

                    // Mark the response as intermediate.
                    response.intermediate = true;

                    //先分发,然后在加入网络队列中去刷新
                    mDelivery.postResponse(request, response, new Runnable() {
                        @Override
                        public void run() {
                            try {
                                mNetworkQueue.put(request);
                            } catch (InterruptedException e) {
                                // Not much we can do about this.
                            }
                        }
                    });
                }

            } catch (InterruptedException e) {
                // We may have been interrupted because it was time to quit.
                if (mQuit) {//退出
                    return;
                }
                continue;
            }
        }
    }

小结:

  • 开启缓存调度,进入一个循环,从缓存队列中拿取请求;此队列是一个阻塞队列。
  • 判断请求的状态,如果是根据请求的key(url)获取对应的实体,从实体中获取状态。
    • 如果为null,那么直接放入网络请求中去。
    • 如果是过期,那么放入到请求中的cacheEntry,然后放入到网络请求中去
    • 如果不为null也不过期,那么解析获取响应,然后进一步判断是否需要刷新,需要的话则进行分发响应并且发送网络请求,不需要刷新的话则直接分发响应。

NetworkDispatcher的run方法

  @Override
    public void run() {
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
        while (true) {
            long startTimeMs = SystemClock.elapsedRealtime();//开始时间,在后面如果出现异常可以用到
            Request<?> request;
            try {
                // Take a request from the queue.
                request = mQueue.take();//一样是阻塞操作
            } catch (InterruptedException e) {
                // We may have been interrupted because it was time to quit.
                if (mQuit) {
                    return;
                }
                continue;
            }

            try {//标记为网络队列操作
                request.addMarker("network-queue-take");

                // If the request was cancelled already, do not perform the
                // network request.
                //如果请求被取消了,就finish掉,进行新的循环
                if (request.isCanceled()) {
                    request.finish("network-discard-cancelled");
                    continue;
                }

                addTrafficStatsTag(request);

                // Perform the network request.
                //响应请求
                NetworkResponse networkResponse = mNetwork.performRequest(request);
                request.addMarker("network-http-complete");//标记为完成响应

                // If the server returned 304 AND we delivered a response already,
                // we're done -- don't deliver a second identical response.
                //根据英文注释,如果该此响应已经存在就不再分发了,将此请求finish掉,重新循环
                if (networkResponse.notModified && request.hasHadResponseDelivered()) {
                    request.finish("not-modified");
                    continue;
                }

                //解析响应
                Response<?> response = request.parseNetworkResponse(networkResponse);
                request.addMarker("network-parse-complete");

                //判断是否需要缓存
                if (request.shouldCache() && response.cacheEntry != null) {
                //缓存请求
                    mCache.put(request.getCacheKey(), response.cacheEntry);
                    request.addMarker("network-cache-written");
                }

                // Post the response back.
                request.markDelivered();
                //分发响应
                mDelivery.postResponse(request, response);
            } catch (VolleyError volleyError) {
                volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
                parseAndDeliverNetworkError(request, volleyError);
            } catch (Exception e) {
                VolleyLog.e(e, "Unhandled exception %s", e.toString());
                VolleyError volleyError = new VolleyError(e);
                volleyError.setNetworkTimeMs(SystemClock.elapsedRealtime() - startTimeMs);
                //分发错误响应
                mDelivery.postError(request, volleyError);
            }
        }
    }

Network的performRequest(request),Network是BasicNetwork

  @Override
    public NetworkResponse performRequest(Request<?> request) throws VolleyError {
        long requestStart = SystemClock.elapsedRealtime();
        while (true) {
            HttpResponse httpResponse = null;
            byte[] responseContents = null;
            Map<String, String> responseHeaders = Collections.emptyMap();
            try {
            //以下过程是将状态、响应码、实体、响应头等封装到NetworkResponse中并返回
                // Gather headers.
                Map<String, String> headers = new HashMap<String, String>();
                addCacheHeaders(headers, request.getCacheEntry());
                //真正发起网络请求的地方
                httpResponse = mHttpStack.performRequest(request, headers);
                StatusLine statusLine = httpResponse.getStatusLine();
                int statusCode = statusLine.getStatusCode();

                responseHeaders = convertHeaders(httpResponse.getAllHeaders());
                // Handle cache validation.
                if (statusCode == HttpStatus.SC_NOT_MODIFIED) {

                    Entry entry = request.getCacheEntry();
                    if (entry == null) {
                        return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, null,
                                responseHeaders, true,
                                SystemClock.elapsedRealtime() - requestStart);
                    }
                    //添加响应头信息
  entry.responseHeaders.putAll(responseHeaders);
                    return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, entry.data,
                            entry.responseHeaders, true,
                            SystemClock.elapsedRealtime() - requestStart);
                }

                // Some responses such as 204s do not have content.  We must check.
                if (httpResponse.getEntity() != null) {
                  responseContents = entityToBytes(httpResponse.getEntity());
                } else {
                  // Add 0 byte response as a way of honestly representing a
                  // no-content request.
                  responseContents = new byte[0];
                }

                // if the request is slow, log it.
                long requestLifetime = SystemClock.elapsedRealtime() - requestStart;
                logSlowRequests(requestLifetime, request, responseContents, statusLine);

                if (statusCode < 200 || statusCode > 299) {
                    throw new IOException();
                }
                return new NetworkResponse(statusCode, responseContents, responseHeaders, false,
                        SystemClock.elapsedRealtime() - requestStart);
            } 
            //删减代码,主要是做一些异常处理
                   .......
            }
        }
    }

从以上可知最终发起网络请求的是mHttpStack,也就是最开始由版本判断的,大于9里面使用HttpUrlConnection,小于9使用HttpClient,然后将获取到的HttpResponse包含的信息封装到NetworkResponse中并返回。

小结:

  • NetworkDispatcher同样是进入循环,然后从网络队列中拿出请求,这次只判断请求是否取消了。
  • 然后通过Network,它是BasicNetwork对象,通过mHttpStack获取响应;
  • 然后判断是否已经分发了该响应,如果没有,解析响应后判断此请求是否需要缓存,如果需要便缓存起来。
  • 最后进行分发响应结果。

RequestQueue的add方法

 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.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.
        //判断该请求是否需要缓存
        if (!request.shouldCache()) {
        //添加到网络队列
            mNetworkQueue.add(request);
            return request;
        }

        // Insert request into stage if there's already a request with the same cache key in flight.
        synchronized (mWaitingRequests) {
        //获取该请求对象对应的缓存的key
            String cacheKey = request.getCacheKey();
            if (mWaitingRequests.containsKey(cacheKey)) {
            //说明该key对应的请求队列存在(有相同的请求正在处理)
                // There is already a request in flight. Queue up.
                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 {
                // 插入一个null队列,表示当前有请求正在执行
                mWaitingRequests.put(cacheKey, null);
                mCacheQueue.add(request);
            }
            return request;
        }
    }

小结 RequestQueue里面有两个优先级阻塞队列,也就是PriorityBlockingQueue,一个是放网络的,另一个是放缓存的;还有一个Map集合mWaitingRequests,用来管理请求,即如果有同类请求正在处理或等待,则将该请求放到等待队列中,否则标记为没有同类请求。

走了这么多,还是看不到在哪里回调请求的结果。那么回头看看RequestQueue的构造方法吧

RequestQueue中的一个构造方法

public RequestQueue(Cache cache, Network network, int threadPoolSize) {
        this(cache, network, threadPoolSize,
                new ExecutorDelivery(new Handler(Looper.getMainLooper())));
    }

可知其中的ExecutorDelivery对象的构造参数是一个Handler,并且是在主线程中的,该构造方法还可以限制线程数。

ExecutorDelivery

public ExecutorDelivery(final Handler handler) {
        // 创建一个里面用于处理任务的执行器
        mResponsePoster = new Executor() {
            @Override
            public void execute(Runnable command) {
            //这个Handler就是传进来的主线程Handler;将任务发送至主线程中的Handler
                handler.post(command);
            }
        };
    }

分发响应结果的最终方法

@Override
    public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {
        request.markDelivered();
        request.addMarker("post-response");
        //mResponsePoster在构造方法中已经初始化了,也就是将响应任务传给主线程的Handler,让Handler发送
        mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));
    }

进入ResponseDeliveryRunnable

它是一个实现Runnable的类,以下为它的run方法

  @Override
        public void run() {
            // 如果请求被取消了,则不进行分发
            if (mRequest.isCanceled()) {
                mRequest.finish("canceled-at-delivery");
                return;
            }
//根据响应结果分发内容或者是响应的失败
            if (mResponse.isSuccess()) {
            //响应成功
                mRequest.deliverResponse(mResponse.result);
            } else {
            //响应失败
                mRequest.deliverError(mResponse.error);
            }

            // 做标记是否为中间响应或者完成
            if (mResponse.intermediate) {
                mRequest.addMarker("intermediate-response");
            } else {
                mRequest.finish("done");
            }

            // If we have been provided a post-delivery runnable, run it.
            if (mRunnable != null) {
                mRunnable.run();
            }
       }
    }
到这里,就快接近能够看到成功和失败的回调了,也就是这两个方法
 if (mResponse.isSuccess()) {
            //响应成功
                mRequest.deliverResponse(mResponse.result);
            } else {
            //响应失败
                mRequest.deliverError(mResponse.error);
            }

由于这里mRequest是抽象的,进到继承它的StringRequest类中,看deliverResponse()方法:

@Override
    protected void deliverResponse(String response) {
        mListener.onResponse(response);
    }

然后在Request中看deliverError()方法:

public void deliverError(VolleyError error) {
        if (mErrorListener != null) {
            mErrorListener.onErrorResponse(error);
        }
    }

然后看看其他的方法

Request的finish()方法
 void finish(final String tag) {
        if (mRequestQueue != null) {
        //调用请求队列的finish
            mRequestQueue.finish(this);
        }
        if (MarkerLog.ENABLED) {
            final long threadId = Thread.currentThread().getId();
            //向主线程发送消息
            if (Looper.myLooper() != Looper.getMainLooper()) {
                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());
        }
    }

RequestQueue中的finish方法

<T> void finish(Request<T> request) {
        // Remove from the set of requests currently being processed.
        synchronized (mCurrentRequests) {
            mCurrentRequests.remove(request);
        }
        synchronized (mFinishedListeners) {
          for (RequestFinishedListener<T> listener : mFinishedListeners) {
          //移除请求时的回调
            listener.onRequestFinished(request);
          }
        }

        if (request.shouldCache()) {
            synchronized (mWaitingRequests) {
                String cacheKey = request.getCacheKey();
                Queue<Request<?>> waitingRequests = mWaitingRequests.remove(cacheKey);
                ......
                    mCacheQueue.addAll(waitingRequests);
                }
            }
        }
    }

RequestQueue中的finish方法首先将完成的请求从当前请求队列中移除,然后遍历RequestFinishedListener回调onRequestFinished(request)方法;最后被移除的暂存请求队列如需要缓存的话则添加到缓存队列中。

最后是RequestQueue的cancelAll();

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;
            }
        });
    }

public void cancelAll(RequestFilter filter) {
        synchronized (mCurrentRequests) {
            for (Request<?> request : mCurrentRequests) {
                if (filter.apply(request)) {
                    request.cancel();
                }
            }
        }
    }

# Request类
 public void cancel() {
        mCanceled = true;
    }

其中通过过滤来进行取消。

到此,一个流程基本就结束了(除了具体的发起网络请求和具体的获取响应)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值