Netroid是一个基于Volley实现的Android Http库。提供执行网络请求、缓存返回结果、批量图片加载、大文件断点下载的常见Http交互功能。致力于避免每个项目重复开发基础Http功能,实现显著地缩短开发周期的愿景。
作为Volley的拓展框架,netroid增加了大文件断点下载,并且netroid的可定制性更强。
在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();
}
首先贴一段带注释的代码:
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) {
。。。
}
}
}
在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的改进还是不错的,这也是这两天看代码的总结,如有遗漏,后面再补充!
我写的CSDN博客客户端介绍:http://blog.csdn.net/brian512/article/details/43168141
点击查看应用详情