Volley源码解读(上)
2017-02-20 12:28
148 查看
Volley框架的使用
Volley网络框架的使用方式绝大部分人都已经很熟悉了。最简单的就是通过Volley提供的静态方法newRequestQueue(Context context)来返回一个消息队列MessageQueue,然后在需要使用时将构造的网络请求消息添加到队列中去,这样就可以完成网络请求
//定义全局的请求队列 requestQueue=Volley.newRequestQueue(getApplicationContext()); //实例化一个请求,并添加到请求队列中去: String url = ""; //实例化一个新的StringRequest,参数依次为:请求方式,请求的URL,请求成功的回调接口,请求失败的回调接口。 StringRequest request = new StringRequest(Method.GET, url, new Listener<String>() { @Override public void onResponse(String arg0) { Toast.makeText(MainActivity.this, arg0, Toast.LENGTH_SHORT).show();; Log.e("successful", arg0); } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError arg0) { // TODO Auto-generated method stub Toast.makeText(MainActivity.this, arg0.toString(), Toast.LENGTH_SHORT).show(); Log.e("failed", arg0.toString()); } }); //为每一个请求设置Tag标记,便于后期对request的管理 request.setTag("testGet"); //添加到请求队列中去 MyApplication.getHttpQueues().add(request)
Volley的具体使用方式可以看这篇博文《Volley框架的使用》
Volley的源码(基于Vollley1.0.11)
在上边可以看到我们通过调用Volley提供的静态方法newRequestQueue来获取到一个消息队列,其源码其实是调用了另一个newRequestQueue方法,源码中有多个不同的newRequestQueue函数//所有的newRequestQueue函数都是调用的该函数,传入参数依次为上下文对象,http请求栈,缓存的最大值 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) { } //传入的stack为空,也就是没有自定义stack if (stack == null) { //根据不同的SDK版本使用不同的Stack进行存储 if (Build.VERSION.SDK_INT >= 9) { stack = new HurlStack(); } else { 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; } //---下边的函数都是调用的第一个函数--- //传入两个参数 public static RequestQueue newRequestQueue(Context context, int maxDiskCacheBytes) { return newRequestQueue(context, null, maxDiskCacheBytes); } //传入两个参数 public static RequestQueue newRequestQueue(Context context, HttpStack stack) { return newRequestQueue(context, stack, -1); } //只有一个传入参数 public static RequestQueue newRequestQueue(Context context) { return newRequestQueue(context, null); } //
由此可以梳理一下调用newRequest()方法之后所发生的的操作。
首先生成了文件的缓存路径
然后判断是否自定义了stack队列,若没有定义队列,则根据SDK版本生成默认的stack,SDK大于9使用HurlStack,否则使用HttpClientStack
声明网络请求的实例
根据传入的maxDiskCacheBytes来构造请求队列
可以看到其工作流程很清晰,其中主要分为stack的创建,network的创建,和requestqueue的创建。
stack创建
//传入的stack为空,也就是没有自定义stack,创建默认的stack if (stack == null) { //根据不同的SDK版本使用不同的Stack进行存储 if (Build.VERSION.SDK_INT >= 9) { stack = new HurlStack(); } else { stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent)); } }
默认的stack根据SDK版本的不同分为HurlStack和HttpClientStack,两者其实都是HttpStack的实现类,主要区别在于适用的SDK版本不一样,HurlStack更加适用于Android版本在2.3和更高版本,因为其内部实现是用了 HttpURLConnection这个类。而HttpClientStack适用于Android版本在2.2和更低版本(现在市面上已经没有了)。
首先来看一下低版本中使用的HttpClientStack的实现:
HttpClientStack
@Override public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders) throws IOException, AuthFailureError { //首先是创建http的request HttpUriRequest httpRequest = createHttpRequest(request, additionalHeaders); addHeaders(httpRequest, additionalHeaders); addHeaders(httpRequest, request.getHeaders()); onPrepareRequest(httpRequest); //获取到request请求中的参数 HttpParams httpParams = httpRequest.getParams(); int timeoutMs = request.getTimeoutMs(); // TODO: Reevaluate this connection timeout based on more wide-scale // data collection and possibly different for wifi vs. 3G. //请求超时机制 HttpConnectionParams.setConnectionTimeout(httpParams, 5000); HttpConnectionParams.setSoTimeout(httpParams, timeoutMs); //将Request提交给HttpClient去执行,并将执行结果返回 return mClient.execute(httpRequest); }
在这里边主要有三个动作:
创建HttpRequest
封装参数
将HttpRequest提交给HttpClient去执行(在外部是传入了一个AndroidHttpClient的实例)
其实HurlStack所做的操作和HttpClientStack类似,只是在最后执行网络请求的时候HurlStack使用的是HttpURLConnection,而HttpClientStack使用HttpClient来执行任务。接下来看一下HurlStack的具体实现:
HurlStack:
以下是HurlStack中的核心方法performRequest,实现了HttpStack接口定义的方法@Override public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders) throws IOException, AuthFailureError { //根据请求来获取url地址 String url = request.getUrl(); //保存请求头 HashMap<String, String> map = new HashMap<String, String>(); map.putAll(request.getHeaders()); map.putAll(additionalHeaders); //如果有需要可以通过UrlRewriter接口重写url地址 if (mUrlRewriter != null) { String rewritten = mUrlRewriter.rewriteUrl(url); if (rewritten == null) { throw new IOException("URL blocked by rewriter: " + url); } url = rewritten; } //生成最终使用的url URL parsedUrl = new URL(url); //开启连接 HttpURLConnection connection = openConnection(parsedUrl, request); //吧request中请求头的信息添加进去 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); 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); //从connecttion中取出实体并保存在response中 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); } } //返回响应response return response; }
以上这两种方式就是Volley中两种不同的请求队列的方式,在构造完stack之后就需要用构造好的stack来创建一个Netwoork实例。
构造网络请求
Network network = new BasicNetwork(stack);
在下边这个方法中完成了对网络请求的发起和对返回的response的校验封装,最终返回一个NetworkResponse 实例
@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 { // Gather headers. Map<String, String> headers = new HashMap<String, String>(); //添加缓存头,从请求中获取需要缓存的字段 addCacheHeaders(headers, request.getCacheEntry()); //调用HttpStack定义的方法来执行请求,具体使用哪种stack由当前SDK版本决定 httpResponse = mHttpStack.performRequest(request, headers); //从之前返回的httpResponse中获取返回信息 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); } // A HTTP 304 response does not have all header fields. We // have to use the header fields from the cache entry plus // the new ones from the response. // http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.5 entry.responseHeaders.putAll(responseHeaders); return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, entry.data, entry.responseHeaders, true, SystemClock.elapsedRealtime() - requestStart); } // Handle moved resources重定向url进行请求 if (statusCode == HttpStatus.SC_MOVED_PERMANENTLY || statusCode == HttpStatus.SC_MOVED_TEMPORARILY) { String newUrl = responseHeaders.get("Location"); request.setRedirectUrl(newUrl); } // Some responses such as 204s do not have content. We must check. //检查返回的response是否有内容,有的话取出保存,没有的话用byte数组填充。 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; //用llog输出这是一次缓慢的网络请求 logSlowRequests(requestLifetime, request, responseContents, statusLine); //如果返回的状态码小于200或者是大于299,报出异常 if (statusCode < 200 || statusCode > 299) { throw new IOException(); } return new NetworkResponse(statusCode, responseContents, responseHeaders, false, SystemClock.elapsedRealtime() - requestStart); //下边就是异常的处理 } catch (SocketTimeoutException e) { attemptRetryOnException("socket", request, new TimeoutError()); } catch (ConnectTimeoutException e) { attemptRetryOnException("connection", request, new TimeoutError()); } catch (MalformedURLException e) { throw new RuntimeException("Bad URL " + request.getUrl(), e); } catch (IOException e) { int statusCode = 0; NetworkResponse networkResponse = null; if (httpResponse != null) { statusCode = httpResponse.getStatusLine().getStatusCode(); } else { throw new NoConnectionError(e); } if (statusCode == HttpStatus.SC_MOVED_PERMANENTLY || statusCode == HttpStatus.SC_MOVED_TEMPORARILY) { VolleyLog.e("Request at %s has been redirected to %s", request.getOriginUrl(), request.getUrl()); } else { VolleyLog.e("Unexpected response code %d for %s", statusCode, request.getUrl()); } if (responseContents != null) { networkResponse = new NetworkResponse(statusCode, responseContents, responseHeaders, false, SystemClock.elapsedRealtime() - requestStart); if (statusCode == HttpStatus.SC_UNAUTHORIZED || statusCode == HttpStatus.SC_FORBIDDEN) { attemptRetryOnException("auth", request, new AuthFailureError(networkResponse)); } else if (statusCode == HttpStatus.SC_MOVED_PERMANENTLY || statusCode == HttpStatus.SC_MOVED_TEMPORARILY) { attemptRetryOnException("redirect", request, new AuthFailureError(networkResponse)); } else { // TODO: Only throw ServerError for 5xx status codes. throw new ServerError(networkResponse); } } else { throw new NetworkError(networkResponse); } } } }
这样就完成了一个Network的工作,实际上的网络请求都是由这一部分来完成。
构造requestQueue
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();
在创建好实际的网络请求类后创建请求队列,并将网络请求类的实例传入,这样在queue开始循环之后就可以不断的进行网络请求的操作。上边的代码显示核心的方法为RequestQueue的构造方法和他的start方法。
/** * @param cache A Cache to use for persisting responses to disk * @param network A Network interface for performing HTTP requests * @param threadPoolSize Number of network dispatcher threads to create * @param delivery A ResponseDelivery interface for posting responses and errors * 可以看到主要是该构造函数的使用,其中有用于缓存的cache,用于执行任务的dispatcher,用于定义了网络请求的network,用于返回response和error的delivery * */ public RequestQueue(Cache cache, Network network, int threadPoolSize, ResponseDelivery delivery) { mCache = cache; mNetwork = network; mDispatchers = new NetworkDispatcher[threadPoolSize]; mDelivery = delivery; } /** * Creates the worker pool. Processing will not begin until {@link #start()} is called. * * @param cache A Cache to use for persisting responses to disk * @param network A Network interface for performing HTTP requests * @param threadPoolSize Number of network dispatcher threads to create */ public RequestQueue(Cache cache, Network network, int threadPoolSize) { this(cache, network, threadPoolSize, new ExecutorDelivery(new Handler(Looper.getMainLooper()))); } /** * Creates the worker pool. Processing will not begin until {@link #start()} is called. * * @param cache A Cache to use for persisting responses to disk * @param network A Network interface for performing HTTP requests */ public RequestQueue(Cache cache, Network network) { this(cache, network, DEFAULT_NETWORK_THREAD_POOL_SIZE); }
在构造好RequestQueue之后就可以通过start方法来开始网络请求,其实主要是开启了NetWorkDispatcher的start方法
/** * Starts the dispatchers in this queue. */ 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(); } }
在上边中的NetworkDispatcher其实是一个继承自Thread类的工作线程,他通过在run方法中进行不断循环来不停的执行任务,通过start方法来开启线程。下边是其核心的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"); //检查Request是否已经被取消 // If the request was cancelled already, do not perform the // network request. if (request.isCanceled()) { request.finish("network-discard-cancelled"); continue; } //添加tag标记 addTrafficStatsTag(request); //真正的通过NetWork实例来执行网络请求 // 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; } //将response进行拼接组装 // Parse the response here on the worker thread. Response<?> response = request.parseNetworkResponse(networkResponse); request.addMarker("network-parse-complete"); //进行cache的更新 // 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); } } }
到此为止Volley已经完成了任务队列的初始化和创建,也完成了从任务被提交到任务队列中,到被工作线程取出执行,到结果的返回和拼装,以及最后response的分发的整个流程,我们可以来梳理一下整个过程。
这就是整个的请求流程的源码,接下来重点就在于对各类型的Request和Response的解读上。
相关文章推荐
- 详细解读Volley(五)—— 通过源码来分析业务流程
- Volley请求源码解读 (1) ---无缓存请求
- Volley请求源码解读 (1) ---无缓存请求
- Volley(2) 源码解读
- 温故而知新 Volley源码解读与思考
- Volley源码解读
- Volley 源码解读
- Volley源码解读(下)
- Android Volley 源码解读
- [Hadoop源码解读](五)MapReduce篇之Writable相关类
- Netty源码解读(一)概述
- Alamofire源码解读系列之错误处理(AFError)
- [Hadoop源码解读](五)MapReduce篇之Writable相关类
- pyspider源码解读--调度器scheduler.py
- 金山卫士界面源码解读及界面库分离 (5)
- faster rcnn源码解读(四)之数据类型imdb.py和pascal_voc.py(主要是imdb和roidb数据类型的解说)
- Redux源码解读(一)
- struct2源码解读(5)之解析bean标签
- Docker网络详解及pipework源码解读与实践
- jQuery scrollLeft()与scrollTop() 源码解读