Volley源码解析(三)网络请求流程

Volley的简单介绍http://blog.csdn.net/chengshuyuan_uestc/article/details/51755189
Volley源码解析(一)Volley中乱码问题及解决方案http://blog.csdn.net/chengshuyuan_uestc/article/details/51755191
Volley源码分析(二)-Volley中的Request类http://blog.csdn.net/chengshuyuan_uestc/article/details/51755197
Volley源码解析(三)网络请求流程http://blog.csdn.net/chengshuyuan_uestc/article/details/51755201
volley封装的很好,使用起来很方便,在一篇博客中,我们学些了volley进行网络请求的三个步骤,以及请求不同格式定义不同的Request,但是对Volley的源码我们还不是很清楚,就让我们一起来阅读以下Volley的源码,将Volley的工作流程和工作原理梳理以下。

首先我们看一下Volley官方文档给我们的Volley工作流程图

image

OK,我们就根据这张图,从我们使用Volley进行网络请求的步骤来阅读源代码。

在使用Volley进行网络请求时,我们首先要得到一个RequestQueue

RequestQueue requestQueue = Volley.newRequestQueue(this);

看下一newRequestQueue()函数的实现,

    //Volley类中
    public static RequestQueue newRequestQueue(Context context) {
        return newRequestQueue(context, null);
    }

这个方法调用了同在Volley类中的重载函数,继续看

    //Volley类中
    public static RequestQueue newRequestQueue(Context context, HttpStack stack)
    {
        return newRequestQueue(context, stack, -1);
    }

同样是调用了重载函数

//Volley类中
    public static RequestQueue newRequestQueue(Context context, HttpStack stack, int maxDiskCacheBytes) {
        File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);
        String userAgent = "volley/0";
        try {
            String packageName = context.getPackageName();
            PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);
            userAgent = packageName + "/" + info.versionCode;
        } catch (NameNotFoundException e) {
        }

        if (stack == null) {
            if (Build.VERSION.SDK_INT >= 9) {
                stack = new HurlStack();
            } else {
                // Prior to Gingerbread, HttpUrlConnection was unreliable.
                // See: http://android-developers.blogspot.com/2011/09/androids-http-clients.html
                stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
            }
        }

        Network network = new BasicNetwork(stack);
        RequestQueue queue;
        if (maxDiskCacheBytes <= -1)
        {
            // No maximum size specified
            queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
        }
        else
        {
            // Disk cache size specified
            queue = new RequestQueue(new DiskBasedCache(cacheDir, maxDiskCacheBytes), network);
        }
        queue.start();
        return queue;
    }

OK,在这个方法中,我们终于看到了函数的实现部分,首先得到userAgent,我们知道在http请求报文中,请求行的四个部分(HOST、Connection、user-agent、agent-language)就包括user-agent。

接下来,如果HttpStack的对象为空,则判断系统版本号,在9之前使用HttpClientStack实例,在后边的版本则创建爱你一个HurlStack实例。其实HttpClientStack中使用的是Apache提供的HttpClient来进行网络的请求,而HurlStack使用的是HttpUrlConnection来进行网络请求。这时因为在2.3之前,HttpUrlConnection存在一个bug,而在2.3之后,在修复bug之后,HttpUrlConnction因为支持压缩等原因,更被推荐使用。HttpStack接口只有一个方法,performRequest()。HurlStack类和HttpClientStack类都实现了这个接口。HttpUrlConnection进行网络请求比较简单一点,我们看一下HurlStack类中的performRequest()实现。

//HurlStack类中
    public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders)
            throws IOException, AuthFailureError {
        String url = request.getUrl();
        HashMap<String, String> map = new HashMap<String, String>();
        map.putAll(request.getHeaders());
        map.putAll(additionalHeaders);
        if (mUrlRewriter != null) {
            String rewritten = mUrlRewriter.rewriteUrl(url);
            if (rewritten == null) {
                throw new IOException("URL blocked by rewriter: " + url);
            }
            url = rewritten;
        }
        URL parsedUrl = new URL(url);
        HttpURLConnection connection = openConnection(parsedUrl, request); //根据url打开一个连接
        for (String headerName : map.keySet()) {
            connection.addRequestProperty(headerName, map.get(headerName));    //添加请求参数
        }
        setConnectionParametersForRequest(connection, request);
        // Initialize HttpResponse with data from the HttpURLConnection.
        ProtocolVersion protocolVersion = new ProtocolVersion("HTTP", 1, 1);  //设置http版本号 为http/1.1
        int responseCode = connection.getResponseCode();                     // 得到返回状态码
        if (responseCode == -1) {
            // -1 is returned by getResponseCode() if the response code could not be retrieved.
            // Signal to the caller that something was wrong with the connection.
            throw new IOException("Could not retrieve response code from HttpUrlConnection.");
        }
        StatusLine responseStatus = new BasicStatusLine(protocolVersion,
                connection.getResponseCode(), connection.getResponseMessage());
        BasicHttpResponse response = new BasicHttpResponse(responseStatus);  // 得到Response
        if (hasResponseBody(request.getMethod(), responseStatus.getStatusCode())) {
            response.setEntity(entityFromConnection(connection));
        }
        for (Entry<String, List<String>> header : connection.getHeaderFields().entrySet()) {
            if (header.getKey() != null) {
                Header h = new BasicHeader(header.getKey(), header.getValue().get(0));
                response.addHeader(h);
            }
        }
        return response;
    }

就是我们熟悉的使用HttpUrlConnection进行网络请求。我添加了部分注释。

接着返回Volley类的newRequestQueue()函数,接着得到一个Network实例,Network接口中,只有一个方法performRequest(), BasicNetwork实现了这个接口,看源码后我们发现BasicNetWork中的performRequest()方法调用了HttpStack类中的performRequest()方法。

接着我们得到ReuqestQueue实例,并执行了其start()方法,我们看一下RequestQueue类中start()方法执行了什么动作。

RequestQueue中维护了两个基于优先级的Request队列, 缓存队列和网络请求队列。放在缓存队列中的Request,将通过缓存获取数据,放在网络请求队列中的Request,将通过网络获取数据。

private final PriorityBlockingQueue<Request<?>> mCacheQueue = new PriorityBlockingQueue<Request<?>>();
private final PriorityBlockingQueue<Request<?>> mNetworkQueue = new PriorityBlockingQueue<Request<?>>();

RquestQueue中还和维护了一个正在进行,尚未完成的请求集合。

private final Set<Request<?>> mCurrentRequests = new HashSet<Request<?>>();

维护了一个等待请求的集合,如果一个请求正在被处理并且可以被缓存,后续相同的url请求将进入此队列。

private final Map<String, Queue<Request<?>>> mWaitingRequests = new HashMap<String, Queue<Request<?>>>();

那么在创建RequestQueue之后调用的start()方法呢

//RequestQueue类中
    public void start() {
        stop();  // Make sure any currently running dispatchers are stopped.
        // Create the cache dispatcher and start it.
        mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
        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的实例,然后调用了它的start()方法,接着在一个for循环里去创建NetworkDispatcher的实例,并分别调用它们的start()方法。这里的CacheDispatcher和NetworkDispatcher都是继承自Thread的,而默认情况下for循环会执行四次,也就是说当调用了Volley.newRequestQueue(context)之后,就会有五个线程一直在后台运行,不断等待网络请求的到来,其中CacheDispatcher是缓存线程,NetworkDispatcher是网络请求线程。

我们分别看一下CacheDispatcher类和NetworkDispatcher类的run()方法

    //CacheDispatcher类
    @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();

        Request<?> request;
        while (true) {
            // release previous request object to avoid leaking request object when mQueue is drained.
            request = null;
            try {
                // Take a request from the queue.
                request = mCacheQueue.take();
            } catch (InterruptedException e) {
                // We may have been interrupted because it was time to quit.
                if (mQuit) {
                    return;
                }
                continue;
            }
            try {
                request.addMarker("cache-queue-take");

                // If the request has been canceled, don't bother dispatching it.
                if (request.isCanceled()) {
                    request.finish("cache-discard-canceled");
                    continue;
                }

                // Attempt to retrieve this item from cache.
                Cache.Entry entry = mCache.get(request.getCacheKey());
                if (entry == null) {
                    request.addMarker("cache-miss");
                    // Cache miss; send off to the network dispatcher.
                    mNetworkQueue.put(request);
                    continue;
                }

                // If it is completely expired, just send it to the network.
                if (entry.isExpired()) {
                    request.addMarker("cache-hit-expired");
                    request.setCacheEntry(entry);
                    mNetworkQueue.put(request);
                    continue;
                }

                // We have a cache hit; parse its data for delivery back to the request.
                request.addMarker("cache-hit");
                Response<?> response = request.parseNetworkResponse(
                        new NetworkResponse(entry.data, entry.responseHeaders));
                request.addMarker("cache-hit-parsed");

                if (!entry.refreshNeeded()) {
                    // Completely unexpired cache hit. Just deliver the response.
                    mDelivery.postResponse(request, response);
                } else {
                    // Soft-expired cache hit. We can deliver the cached response,
                    // but we need to also send the request to the network for
                    // refreshing.
                    request.addMarker("cache-hit-refresh-needed");
                    request.setCacheEntry(entry);

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

                    // Post the intermediate response back to the user and have
                    // the delivery then forward the request along to the network.
                    final Request<?> finalRequest = request;
                    mDelivery.postResponse(request, response, new Runnable() {
                        @Override
                        public void run() {
                            try {
                                mNetworkQueue.put(finalRequest);
                            } catch (InterruptedException e) {
                                // Not much we can do about this.
                            }
                        }
                    });
                }
            } catch (Exception e) {
                VolleyLog.e(e, "Unhandled exception %s", e.toString());
            }
        }
    }

当没有cache或者cache过期时,需要将请求放到网络请求队列NetworkQueue中,如果存在cache且有效,则调用Request的parseNetworkResponse()方法,进行Response的解析工作。
run()方法执行的流程图如图所示:

image

NetworkDispatche类的run()方法代码如下:

    //NetworkDispatche类
    @Override
    public void run() {
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
        Request<?> request;
        while (true) {
            long startTimeMs = SystemClock.elapsedRealtime();
            // release previous request object to avoid leaking request object when mQueue is drained.
            request = null;
            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.
                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.
                if (networkResponse.notModified && request.hasHadResponseDelivered()) {
                    request.finish("not-modified");
                    continue;
                }

                // Parse the response here on the worker thread.
                Response<?> response = request.parseNetworkResponse(networkResponse);
                request.addMarker("network-parse-complete");

                // Write to cache if applicable.
                // TODO: Only update cache metadata instead of entire record for 304s.
                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()方法得到Response,而在刚才的分析中,我们知道BasicNetwork类中的perforrequest()方法调用的HttpStack类中的performRequest()方法,即使用HttpUrlConnection或者HttpClient来进行网络请求并得到Response。在得到Response后,如果需要加入缓存,则执行加入缓存的put操作。在得到Response之后,又执行了下面的语句:

//NetworkDispatche类
mDelivery.postResponse(request, response);

我们查看实现ResponseDelivery接口的ExecutorDelivery类的实现方法

    //ExecutorDelivery类中
    @Override
    public void postError(Request<?> request, VolleyError error) {
        request.addMarker("post-error");
        Response<?> response = Response.error(error);
        mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, null));
    }

在postResonse()函数中,需要实现ResponseDeliveryRunnable类,这个类的run()方法如下:

    //ExecutorDelivery类中
        @Override
        public void run() {
            // If this request has canceled, finish it and don't deliver.
            if (mRequest.isCanceled()) {
                mRequest.finish("canceled-at-delivery");
                return;
            }

            // Deliver a normal response or error, depending.
            if (mResponse.isSuccess()) {
                mRequest.deliverResponse(mResponse.result);
            } else {
                mRequest.deliverError(mResponse.error);
            }

            // If this is an intermediate response, add a marker, otherwise we're done
            // and the request can be finished.
            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();
            }
       }
    }

不用看太多,我们看到调用了我们熟悉的Request类的deliverResponse(mResponse.result)方法。
NetworkDispatche类run()方法的流程图如下图所示:

image

OK 到现在我们分析完了用Volley进行网络请求的原理、Request请求的发出(缓存或者从网络获取)、Response的处理等等过程。

需要注意的地方

另外分析网络请求时一个重要的点需要我们注意, 网络请求是一个耗时操作,肯定不会在UI主线程中进行网络请求的操作,Volley在请求网络或者Cache时都是在一个新的Thread类中,然而我们可以可以有如下操作。

    StringRequest stringRequest = new StringRequest("http://www.baidu.com",
            new Response.Listener<String>() {
                @Override
                public void onResponse(String response) {
                    tv_string.setText(response.toString());
                }
            },
            new Response.ErrorListener() {
                @Override
                 public void onErrorResponse(VolleyError error) {
                    tv_string.setText("error");
                }
             }
    );

看到我们直接更新了UI,这是怎么实现的呢?

其实在ExecutorDelivery类的构造函数中,我们传入了一个Handler对象:

    //ExecutorDelivery类中
    public ExecutorDelivery(final Handler handler) {
        // Make an Executor that just wraps the handler.
        mResponsePoster = new Executor() {
            @Override
            public void execute(Runnable command) {
                handler.post(command);
            }
        };
    }

在前面分析ExecutorDelivery类的过程中,我们说明了在ResponseDeliveryRunnable中调用了Request类的deliverResponse方法,而上边execute()函数的参数正是ResponseDeliveryRunnable类的对象。我们知道handler.post(Runnable),Runnable就和handler相关的线程是同一个线程。那么该handler是和哪个线程关联呢?

查看调用关系,在RequestQueue类的构造函数时有如下代码:

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

一切水落石出,handler是和主线程关联的,所以是可以直接进行UI更新的。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值