Volley拓展框架——Netroid,以及與Volley的差異
Netroid是一個基于Volley實現的Android Http庫。提供執行網絡請求、緩存返回結果、批量圖片加載、大文件斷點下載的常見Http交互功能。致力于避免每個項目重復開發基礎Http功能,實現顯著地縮短開發周期的愿景。
功能上的區別:
作為Volley的拓展框架,netroid增加了大文件斷點下載,并且netroid的可定制性更強。
實現上的區別:
1. 緩存的處理;
在volley中,緩存的過期時間是通過 ttl 和 softTtl 控制
/** True if the entry is expired. */ public boolean isExpired() { return this.ttl < System.currentTimeMillis(); } /** True if a refresh is needed from the original data source. */ public boolean refreshNeeded() { return this.softTtl < System.currentTimeMillis(); }
而這兩個值的來源是HttpHeaderParser.parseCacheHeaders
public static Cache.Entry parseCacheHeaders(NetworkResponse response) { long now = System.currentTimeMillis(); 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; 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); // Cache-Control takes precedence over an Expires header, even if both exist and Expires is more restrictive. // 如果服務器返回的header中有Cache-Control字段,則可以按照制定的規則進行設定 if (hasCacheControl) { softExpire = now + maxAge * 1000; finalExpire = mustRevalidate ? softExpire : softExpire + staleWhileRevalidate * 1000; } // 若服務器返回的header中包含Expires和Date字段 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; return entry; }
根據上述代碼中的中文注釋看,若服務器返回的header中沒有Cache-Control,Expires,Date等字段,則 ttl 和 softExpire 的值均為默認的0,從而使得緩存永遠是過期的,其影響是緩存不僅不能起效,反而每次網絡請求都需要更新緩存,最后就是拖累整體性能。
為此,netroid采用expireTime字段替代了 ttl 和 softExpire ,每次發起請求時,需指定過期時間
// com.duowan.mobile.netroid.Request.java public void setCacheExpireTime(TimeUnit timeUnit, int amount) { this.mCacheExpireTime = System.currentTimeMillis() + timeUnit.toMillis(amount); } public final boolean shouldCache() { return mCacheExpireTime > 0; }
從上述代碼看出,若沒有設置過期時間時,不會產生緩存
/** True if the entry is expired. */ public boolean isExpired() { return expireTime < System.currentTimeMillis(); } /** True if a refresh is needed from the original data source. */ public boolean refreshNeeded() { // still unimplemented, might be use a constant like 'refreshTime'? return this.expireTime < System.currentTimeMillis(); }
2. 網絡數據處理;
首先貼一段帶注釋的代碼:
public NetworkResponse performRequest(Request<?> request) throws VolleyError { // Determine if request had non-http perform. // 若該請求不需要訪問網絡,則直接復寫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; try { // prepare to perform this request, normally is reset the request headers. // 此方法默認實現為空,若請求有需要預處理的話,該設計也是極好的。使用場景如,在進行大文件斷點下載時,需要設置Range頭字段,但是網絡異常進行retry時就不太好處理range了,但是有這個方法就很簡單了 request.prepare(); httpResponse = mHttpStack.performRequest(request); StatusLine statusLine = httpResponse.getStatusLine(); int statusCode = statusLine.getStatusCode(); if (statusCode < 200 || statusCode > 299) throw new IOException(); // 此方法的默認實現為volley的實現方法,但是可以復寫該方法。volley的實現方式是直接把請求到的數據轉為byte[],此方式會限制請求的數據量不能太大,否則會OOM。 // 若下載大文件時,就得復寫這個方法,將網絡請求的數據流讀寫到文件,而不是內存 responseContents = request.handleResponse(httpResponse, mDelivery); // if the request is slow, log it. long requestLifetime = SystemClock.elapsedRealtime() - requestStart; logSlowRequests(requestLifetime, request, responseContents, statusLine); return new NetworkResponse(statusCode, responseContents, parseCharset(httpResponse)); } 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) { 。。。 } } }
3. 數據請求過程回調;
在volley的實現中,是通過ExecutorDelivery將數據請求的結果回調給調用者。
public interface ResponseDelivery { /** * Parses a response from the network or cache and delivers it. */ public void postResponse(Request<?> request, Response<?> response); /** * Parses a response from the network or cache and delivers it. The provided * Runnable will be executed after delivery. */ public void postResponse(Request<?> request, Response<?> response, Runnable runnable); /** * Posts an error for the given request. */ public void postError(Request<?> request, VolleyError error); }
netroid在此基礎上增加了一些回調:
public interface Delivery { /** Posts request finished callback for the given request. */ void postFinish(Request<?> request); /** Parses a response from the network or cache and delivers it. */ public void postResponse(Request<?> request, Response<?> response); /** * Parses a response from the network or cache and delivers it. The provided * Runnable will be executed after delivery. */ public void postResponse(Request<?> request, Response<?> response, Runnable runnable); /** Posts an error for the given request. */ public void postError(Request<?> request, VolleyError error); /** Posts a cancel callback for the given request. */ void postCancel(Request<?> request); /** Posts starting execute callback for the given request. */ void postPreExecute(Request<?> request); /** Posts cache used callback for the given request. */ void postUsedCache(Request<?> request); /** Posts networking callback for the given request. */ void postNetworking(Request<?> request); /** Posts request retry callback for the given request. */ void postRetry(Request<?> request); /** Posts file download progress stat. */ void postDownloadProgress(Request<?> request, long fileSize, long downloadedSize); }
可以看出,這些回調基本覆蓋了請求過程中的關鍵點,主要是有postDownloadProgress方法,進行回調文件下載進度。
// com.duowan.mobile.netroid.NetworkDispatcher.java request.addMarker("network-queue-take"); mDelivery.postPreExecute(request); // 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; } // Perform the network request. NetworkResponse networkResponse = mNetwork.performRequest(request); request.addMarker("network-http-complete"); // Parse the response here on the worker thread. Response<?> response = request.parseNetworkResponse(networkResponse); request.addMarker("network-parse-complete"); // Write to cache if applicable. if (mCache != null && request.shouldCache() && response.cacheEntry != null) { response.cacheEntry.expireTime = request.getCacheExpireTime(); mCache.putEntry(request.getCacheKey(), response.cacheEntry); request.addMarker("network-cache-written"); } // Post the response back. request.markDelivered(); mDelivery.postResponse(request, response);
從上述代碼看出,回調確實很多,若在這些回調中添加太多操作的話,肯定會影響數據請求的速度。
總的來說,netroid相對volley的改進還是不錯的,這也是這兩天看代碼的總結,如有遺漏,后面再補充!
來自: http://blog.csdn.net/brian512/article/details/50499423?ref=myread