您的位置:首页 > 理论基础 > 计算机网络

简析开源网络模块_volley

2016-01-26 20:48 477 查看

简析开源网络模块_volley

从这里开始

RequestQueue requestQueue = Volley.newRequestQueue(this);
String url = "http://www.baidu.com";
StringRequest stringRequest = new StringRequest(Request.Method.GET,url,new Response.Listener<String>() {
@Override
public void onResponse(String response) {
resultTextView.setText(response);
}
},
@Override
public void onErrorResponse(VolleyError error) {
resultTextView.setText(error.getMessage());
});

requestQueue.add(stringRequest);


1、Volley的回调为什么运行在主线程里?

在以上调用时,并没有传递主线程handler到volley,为什么回调直接就运行在主线程中呢?

原因:

Volley中RequestQueue的构造函数:

public RequestQueue(Cache cache, Network network, int threadPoolSize,ResponseDelivery delivery)


其中ResponseDelivery从名称就可以看出,是请求返回的代理类。
我们一般不自定义,使用默认delivery:

new ExecutorDelivery(new Handler(Looper.getMainLooper())


在这里获取的主线程的looper。

public class ExecutorDelivery implements ResponseDelivery {
....
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);
}
};
}
@Override
public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {
request.markDelivered();
request.addMarker("post-response");
mResponsePoster.execute(new
ResponseDeliveryRunnable(request, response, runnable));
}
....
}


这里使用了自定义的Excutor,在MainLooper中执行回调方法。

2、requestQueue.add之后,volley内部都做了些什么?

做了一堆比较优雅的乱七八糟的操作:



volley里概念上是基于线程池的,支持缓存的并发网络请求框架。线程池、缓存从上面流程图大概可以看出来。

a、线程池。

使用一个数组实现:NetworkDispatcher[] mDispatchers;在RequestQueue初始化之后,调用start时,启动所有dispatcher。

dispatcher负责从mNetworkQueue中取request任务,取到之后执行网络请求,得到请求后通过上面说得delivery回调发送请求结果。

b、mNetworkQueue。

请求任务队列。使用优先级阻塞队列实现。最终要执行的request任务都从这个队列中取出。

c、mCacheQueue。

缓存任务队列。也是优先级缓存队列,当一个request任务支持缓存的时候,addRequest不会直接加入mNetworkQueue中,会先查看mWaitingRequests中有没有正在等待的cacheKey相同的任务,如果有,放入mWaitingRequests中。否则,放入key,request放入mCacheQueue中。

d、CacheDispatcher。

从1、2、3可以看出,如果request使用缓存,那么是不会直接放入mNetworkQueue中的。而线程池又只从mNetworkQueue中取任务执行。CacheDispatcher的任务就是先查看Cache中是否有该Request的缓存。有的话直接调用Delivery返回结果。 Cache miss的话,将request加入mNetworkQueue。

e、在ResponseDelivery中,response回调时,会同时调用RequestQueue中的finish方法。

finish时,会根据CacheKey查看mWaitingRequests中是否存在正在等待的request。如果存在,将所有request取出,都添加到mCacheQueue中。之后按3的逻辑继续。

总结:

功能丰富、实现优雅、基于接口编程,可随意扩展。

3、都缓存了什么东西?

Volley里定义的缓存接口是这样的:

public interface Cache {
public Entry get(String key);
public void put(String key, Entry entry);
public void initialize();
public void invalidate(String key, boolean fullExpire);
public void remove(String key);
public void clear();

public static class Entry {
public byte[] data;
public String etag;
public long serverDate;
public long lastModified;
public long ttl;
public long softTtl;
public Map<String, String> responseHeaders = Collections.emptyMap();
public boolean isExpired() {
return this.ttl < System.currentTimeMillis();
}
public boolean refreshNeeded() {
return this.softTtl < System.currentTimeMillis();
}
}


就是一般概念上,一个cache应该有的东西。

调用Volley.newRequestQueue时,默认使用的是DiskBasedCache。一个磁盘缓存的实现。具体的cache实现有很多种,不过重要的是实现了上述Cache接口。我们可以使用任何cache策略替换这个DiskCache。参照策略模式。

Cache的缓存对象是一个byte[]数组和一个存Header字段的Map。缓存的并不是一个response对象。一个组件只做自己应该做的事情,不参与外部操作,是很不容易做到的。。

Response<?> response = request.parseNetworkResponse(                        new NetworkResponse(entry.data, entry.responseHeaders));

request.addMarker("cache-hit-parsed");


在CacheDispatcher里,如果缓存命中,根据缓存内容生成response对象,然后返回。

4、Volley在哪里发起的网络请求?

初始化RequestQueue

public static RequestQueue newRequestQueue(Context context, HttpStack stack) {
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 = new RequestQueue(new DiskBasedCache(cacheDir), network);
queue.start();
return queue;
}


在之前的逻辑里,NetworkDispatcher从mNetworkQueue中取出任务,然后调用mNetwork.performRequest发起请求,得到NetworkResponse。performRequest是一个接口方法。具体的实现类还是在初始化RequestQueue时指定的。在初始化RequestQueue时,设置了UA,由HttpStack生成了NetWork,然后设置给了RequestQueue。

这里的NetWork也是一个接口(面向接口编程随处可见)。它只有一个方法
javaperformRequest(Request<?> request)
,就是发起网络请求。

BasicNetwork是它的一个基础实现。

@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 {
Map<String, String> headers = new HashMap<String, String>();
addCacheHeaders(headers, request.getCacheEntry());
httpResponse = mHttpStack.performRequest(request, headers);
StatusLine statusLine = httpResponse.getStatusLine();
int statusCode = statusLine.getStatusCode();
responseHeaders = convertHeaders(httpResponse.getAllHeaders());
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);
}
entry.responseHeaders.putAll(responseHeaders);
return new NetworkResponse(HttpStatus.SC_NOT_MODIFIED, entry.data,                            entry.responseHeaders, true,                            SystemClock.elapsedRealtime() - requestStart);
}
if (httpResponse.getEntity() != null) {
responseContents = entityToBytes(httpResponse.getEntity());
} else {
responseContents = new byte[0];
}
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);
} 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);
}
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 {
throw new ServerError(networkResponse);
}
} else {
throw new NetworkError(networkResponse);
}
}
}


BasicNetwork对请求返回的httpResponse做了一系列的处理。包括转换httpResponse到NetworkResponse、responseHeader转换、异常封装(将各种异常封装为VolleyError)、请求重试等逻辑。合理的抛出Exception是library里异常比较好的处理方式。用合理的方式告诉调用者他的错误,而不是全部帮他处理。NetworkDispatcher捕获到异常后自己处理。

5、Volley到底怎么发的网络请求?

在BaseNetwork中,通过调用HttpStack的performRequest发起网络请求。而HttpStack则是在newRequestQueue时,实例化的HurlStack。HttpStack还是一个接口。。。。

Volley网络请求逻辑里所有使用接口的地方,我们都可以使用自己的实现来扩展、自定义具体的操作。而不是直接修改源码。

在4中可以看到,HttpStack有两个实现。根据SDK_VERSION区分。2.3之前使用的HttpClientStack是HttpClient,之后HurlStack使用的是HttpUrlConnection。原因见上层文章末尾。

请求网络:

HurlStack.java
@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);
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);
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);
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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  网络 volley Android Java