简析开源网络模块_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; }
相关文章推荐
- java对世界各个时区(TimeZone)的通用转换处理方法(转载)
- java-注解annotation
- java-模拟tomcat服务器
- java-用HttpURLConnection发送Http请求.
- java-WEB中的监听器Lisener
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试