简介:由于我们工程一历史渊源比较久远,所以最开始用的volley,直到最近我才下定决心,将工程底层库完全替换成okhttp3,这里介绍一下volley的整体策略,来怀念一下。
PS:由于我在去年早就已经将底层基础库与上层应用,中间加了一层接口层,隔离,所以替换okhttp3几乎没什么工作量,一直没做一是风险,二是项目进度的原因。
正文:
首先来看一下这个是我很久之前给我们组分享的时候,做的一张图,为了省事,直接拿来用了,
可以看到volley与okhttp3类似,虽然实现方式不一样,但是在缓存这块都是下了一定功夫的,但是volley出现的比较早,所以做的并没有okhttp那么全,
HttpHeaderParser.java
如果看过我的okhttp详解的话,看懂这块代码就很容易了public static Cache.Entry parseCacheHeaders(NetworkResponse response) { long now = System.currentTimeMillis(); Map<String, String> headers = response.headers; long serverDate = 0; long lastModified = 0; long serverExpires = 0; long softExpire = 0; long finalExpire = 0; long maxAge = 0; long staleWhileRevalidate = 0; boolean hasCacheControl = false; boolean mustRevalidate = false; String serverEtag = null; String headerValue; headerValue = headers.get("Date"); if (headerValue != null) { serverDate = parseDateAsEpoch(headerValue); } headerValue = headers.get("Cache-Control"); if (headerValue != null) { hasCacheControl = true; String[] tokens = headerValue.split(","); for (int i = 0; i < tokens.length; i++) { String token = tokens[i].trim(); if (token.equals("no-cache") || token.equals("no-store")) { return null; } else if (token.startsWith("max-age=")) { try { maxAge = Long.parseLong(token.substring(8)); } catch (Exception e) { } } else if (token.startsWith("stale-while-revalidate=")) { try { staleWhileRevalidate = Long.parseLong(token.substring(23)); } catch (Exception e) { } } else if (token.equals("must-revalidate") || token.equals("proxy-revalidate")) { mustRevalidate = true; } } } headerValue = headers.get("Expires"); if (headerValue != null) { serverExpires = parseDateAsEpoch(headerValue); } headerValue = headers.get("Last-Modified"); if (headerValue != null) { lastModified = parseDateAsEpoch(headerValue); } serverEtag = headers.get("ETag"); // Cache-Control takes precedence over an Expires header, even if both exist and Expires // is more restrictive. if (hasCacheControl) { softExpire = now + maxAge * 1000; finalExpire = mustRevalidate ? softExpire : softExpire + staleWhileRevalidate * 1000; } else if (serverDate > 0 && serverExpires >= serverDate) { // Default semantic for Expire header in HTTP specification is softExpire. softExpire = now + (serverExpires - serverDate); finalExpire = softExpire; } Cache.Entry entry = new Cache.Entry(); entry.data = response.data; entry.etag = serverEtag; entry.softTtl = softExpire; entry.ttl = finalExpire; entry.serverDate = serverDate; entry.lastModified = lastModified; entry.responseHeaders = headers; return entry; }
接下来说的一个重点就是volley的线程池管理。
对,你没有看错,就是直接跑N个线程,一直跑,进行网络请求与解析。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(); } }
NetworkDispatch.java
@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"); mDelivery.postPreExecute(request); //通过deliver将预处理发给用户, // If the request was cancelled already, do not perform the // network request. if (request.isCanceled()) { request.finish("network-discard-cancelled"); mDelivery.postCancel(request); mDelivery.postFinish(request); 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"); //304 从上次到目前,没有改变, 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 (this.mCache != null && 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); //将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); } } }
BasicNetwork.java
HurlStack.java@Override public NetworkResponse performRequest(Request<?> request) throws VolleyError { // Determine if request had non-http perform. NetworkResponse networkResponse = request.perform(); if (networkResponse != null) return networkResponse; long requestStart = SystemClock.elapsedRealtime(); //记录一下网络请求开始时间 while (true) { // If the request was cancelled already, // do not perform the network request. if (request.isCanceled()) { //判断是否取消 request.finish("perform-discard-cancelled"); mDelivery.postCancel(request); throw new NetworkError(networkResponse); } 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()); // prepare to perform this request, normally is reset the request headers. request.prepare(); 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) { // 304 网络数据没有变化 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 if (statusCode == HttpStatus.SC_MOVED_PERMANENTLY || statusCode == HttpStatus.SC_MOVED_TEMPORARILY) { //重定向 String newUrl = responseHeaders.get("Location"); request.setRedirectUrl(newUrl); } //将entityToBytes方法提取出来,防盗handleResponse中,使用多态增加灵活性 responseContents = request.handleResponse(httpResponse, mDelivery);//用户端复写,增加灵活性 if(request instanceof FileDownloadRequest && ((FileDownloadRequest)request).isReloaded()){ //这个是我增加的文件下载功能,源码没有 continue; } // 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); //返回,content,header等内容 } 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 = null; if (httpResponse != null) { statusCode = httpResponse.getStatusLine().getStatusCode(); } else { attemptRetryOnException("fileDownloadRetry", request, 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 RedirectError(networkResponse)); } else { // TODO: Only throw ServerError for 5xx status codes. throw new ServerError(networkResponse); } } else { attemptRetryOnException("redirect", request, new NetworkError(e)); } } } }
@Override 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); //写好header if (mUrlRewriter != null) { String rewritten = mUrlRewriter.rewriteUrl(url); //是否是重定向,这里和okhttp比较少了不少条件啊 if (rewritten == null) { throw new IOException("URL blocked by rewriter: " + url); } url = rewritten; } URL parsedUrl = new URL(url); HttpURLConnection connection = openConnection(parsedUrl, request); //建立连接,这里适用url.openConnection(),这里会转入okhttp,系统源码, for (String headerName : map.keySet()) { connection.addRequestProperty(headerName, map.get(headerName)); } setConnectionParametersForRequest(connection, request); //设置methods // 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); if (hasResponseBody(request.getMethod(), responseStatus.getStatusCode())) { response.setEntity(entityFromConnection(connection)); //有返回数据,从InputStream中读取数据 } 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; }
由于我对volley使用的比较多,我觉得很简单,所以很多没有说的很细,如果有需要的,可以提出来,一起讨论。
volley和okhttp比较,其实没有太大优势,应该是比较出的比较早的原因,