带你从源码角度理解Volley实现原理
2015-11-01 14:43
357 查看
访问网络是我们在开发Android应用过程中常常会需要用到,框架层给我们提供HTTPURLConnection可以认为是对网络操作最原子的一个操作,我们只需要按照接口的设定传入参数就可以返回流对象。Volley做为一个框架,它不仅包含了最核心的网络操作之外,还提供了缓存和异步请求队列的功能,也就是做了Cache大部分的工作,用户在使用的过程中就不需要过多的考虑如何做缓存等功能,下面我根据源码和自己的理解和读者一起走进Volley的世界。
Volley整体的架构图:
不管是任何框架或者是任何一个模块,其核心功能都可以高级抽象为:干什么,需要什么,得到什么。回到Volley,其实际上就演变成,操作网络,网络请求参数,操作结果,在Volley中的源码体现在:
Request可以将其认为一条请求,而这条请求当中不仅包含了网络中需要携带的参数信息,还为请求添加一些可以约束的条件。例如源码中重要的属性:
BasicNetwork实现Network接口,它实现的方法体:
上面谈到的策略模式,具体实现用到的策略执行performRequest操作的是HttpStack的实现类,其中一个子类HttpClientStack,我们看一下它的执行过程:
BasicNetwork实现了一个完整的网络操作过程,这仅是针对一个请求的操作。Volley很强大,提供了队列管理的方式允许用户可以创建多个请求,不需要用户手动去管理这些请求是什么时候被执行。RequestQueue类则是完成此类操作的重要工具,重要的属性为:
上文针对Volley处理网络的操作进行了一次梳理,还没有提到缓存相关部分的内容,其核心的思想也是本地保存请求链接和已经从网络上获取下来的数据,如果再发起一次请求,发现有备案,则直接从本地获取返回既可,不必再次访问网络。Volley默认开启4条访问线程,采用并行处理的方式,所以Volley的特性对于访问次数频繁但数据流比较少的应用场景还是比较高效率的。
Volley整体的架构图:
不管是任何框架或者是任何一个模块,其核心功能都可以高级抽象为:干什么,需要什么,得到什么。回到Volley,其实际上就演变成,操作网络,网络请求参数,操作结果,在Volley中的源码体现在:
public interface Network { NetworkResponse performRequest(Request<?> var1) throws VolleyError; }
public interface HttpStack { HttpResponse performRequest(Request<?> var1, Map<String, String> var2) throws IOException, AuthFailureError; }performRequest 可以抽象出整体的网络核心。所以Request表示网络请求的基类,NetworkResponse表示执行网络返回的结果。我们在使用HttpUrlConnection的时候是
URL url = new URL(urlString); //URL对象 conn = (HttpURLConnection)url.openConnection(); conn.setDoInput(true); conn.setDoOutput(true); conn.setUseCaches(false); conn.setRequestMethod("GET"); is = conn.getInputStream();url中携带了参数,HttpUrlConnection则代表了整个网络操作的动作,返回is流对象为操作的结果。
Request可以将其认为一条请求,而这条请求当中不仅包含了网络中需要携带的参数信息,还为请求添加一些可以约束的条件。例如源码中重要的属性:
private final MarkerLog mEventLog; private final int mMethod; // 使用的方法,POST/GET private String mUrl; // 请求地址 private final ErrorListener mErrorListener; // 错误回调 private Integer mSequence; // private RequestQueue mRequestQueue; // 关联到的请求队列 private boolean mShouldCache; // 是否需要缓存 private boolean mCanceled; // 是否已经被取消 private boolean mResponseDelivered; // 是否已经被提交 private long mRequestBirthTime; // 请求被创建的时刻 private static final long SLOW_REQUEST_THRESHOLD_MS = 3000L; private RetryPolicy mRetryPolicy; // 重发代理 private Entry mCacheEntry; // 键值对属性 private Object mTag; // tag把请求封装为一个对象,实体数据。
BasicNetwork实现Network接口,它实现的方法体:
public NetworkResponse performRequest(Request<?> request) throws VolleyError { long requestStart = SystemClock.elapsedRealtime(); while(true) { HttpResponse httpResponse = null; Object responseContents = null; HashMap responseHeaders = new HashMap(); try { HashMap e = new HashMap(); this.addCacheHeaders(e, request.getCacheEntry()); <strong>httpResponse = this.mHttpStack.performRequest(request, e);</strong> StatusLine statusCode1 = httpResponse.getStatusLine(); int networkResponse1 = statusCode1.getStatusCode(); Map responseHeaders1 = convertHeaders(httpResponse.getAllHeaders()); if(networkResponse1 != 304) { byte[] responseContents1; if(httpResponse.getEntity() != null) { responseContents1 = this.entityToBytes(httpResponse.getEntity()); } else { responseContents1 = new byte[0]; } long requestLifetime = SystemClock.elapsedRealtime() - requestStart; this.logSlowRequests(requestLifetime, request, responseContents1, statusCode1); if(networkResponse1 >= 200 && networkResponse1 <= 299) { return <strong>new NetworkResponse(networkResponse1, responseContents1, responseHeaders1, false);</strong> } throw new IOException(); } return <strong>new NetworkResponse(304, request.getCacheEntry() == null?null:request.getCacheEntry().data, responseHeaders1, true);</strong> } catch (Exception var12) { boolean statusCode = false; NetworkResponse networkResponse = null; if(httpResponse == null) { throw new NoConnectionError(var15); } int statusCode2 = httpResponse.getStatusLine().getStatusCode(); VolleyLog.e("Unexpected response code %d for %s", new Object[]{Integer.valueOf(statusCode2), request.getUrl()}); if(responseContents == null) { throw new NetworkError(networkResponse); } networkResponse = new NetworkResponse(statusCode2, (byte[])responseContents, responseHeaders, false); if(statusCode2 != 401 && statusCode2 != 403) { throw new ServerError(networkResponse); } attemptRetryOnException("auth", request, new AuthFailureError(networkResponse)); } } }源码中mHttpStack.performRequest(request, e),为何要单独另起一个HttpStack来执行?这就是一种策略模式,BasicNetwork代表的整体网络操作的一个抽象,即BasicNetwork中的performRequest抽象为执行这个方法,我将给你返回一个结果,但是执行的操作可能不只一种操作方式,而是用策略模式去让上层决定该以何种方式来执行本次操作,方便接口的扩展。接触设计模式比较少的读者朋友建议多去学习体会设计模式,因为设计模式不仅仅可以提高工作效率,而且使你的代码看起来更加整洁,富有扩展性。执行完之后获取得到一个HTTPResponse响应流对象,这时候将执行结果包装到Response对象中,网络操作部分的过程就结束了。
上面谈到的策略模式,具体实现用到的策略执行performRequest操作的是HttpStack的实现类,其中一个子类HttpClientStack,我们看一下它的执行过程:
public HttpResponse performRequest(Request<?> request, Map<String, String> additionalHeaders) throws IOException, AuthFailureError { HttpUriRequest httpRequest = createHttpRequest(request, additionalHeaders); addHeaders(httpRequest, additionalHeaders); addHeaders(httpRequest, request.getHeaders()); this.onPrepareRequest(httpRequest); HttpParams httpParams = httpRequest.getParams(); int timeoutMs = request.getTimeoutMs(); HttpConnectionParams.setConnectionTimeout(httpParams, 5000); HttpConnectionParams.setSoTimeout(httpParams, timeoutMs); return this.mClient.execute(httpRequest); }可以看到,实际上真正访问网络就是使用HttpClient,这只是一个具体的策略实现类,把参数头部信息携带进去execute后则得到响应流对象。createHttpRequest方法则解析request中携带的一些约束信息构成一个执行环境和配置信息,然后就可以真正开始执行网络了。
BasicNetwork实现了一个完整的网络操作过程,这仅是针对一个请求的操作。Volley很强大,提供了队列管理的方式允许用户可以创建多个请求,不需要用户手动去管理这些请求是什么时候被执行。RequestQueue类则是完成此类操作的重要工具,重要的属性为:
private final Map<String, Queue<Request<?>>> mWaitingRequests; // 等待执行的请求 private final Set<Request<?>> mCurrentRequests; // 当前正在执行的请求 private final PriorityBlockingQueue<Request<?>> mCacheQueue; // 带有权重的请求缓存 private final PriorityBlockingQueue<Request<?>> mNetworkQueue; // 带有权重的请求 private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4; private final Cache mCache; private final Network mNetwork; // 针对每个Request执行的实际网络操作 private final ResponseDelivery mDelivery; // 结果处理器 private NetworkDispatcher[] mDispatchers; // 任务执行分发器 private CacheDispatcher mCacheDispatcher; // 缓存分发器在实际使用过程中,我们使用Volley.newRequestQueue创建一个任务调度处理器RequestQueue,并且传入一个BasicNetwork实力对象,启用start方法开启调度器的工作,start源码如下:
public void start() { this.stop(); this.mCacheDispatcher = new CacheDispatcher(this.mCacheQueue, this.mNetworkQueue, this.mCache, this.mDelivery); this.mCacheDispatcher.start(); for(int i = 0; i < this.mDispatchers.length; ++i) { NetworkDispatcher networkDispatcher = new NetworkDispatcher(this.mNetworkQueue, this.mNetwork, this.mCache, this.mDelivery); this.mDispatchers[i] = networkDispatcher; networkDispatcher.start(); } } public void stop() { if(this.mCacheDispatcher != null) { this.mCacheDispatcher.quit(); } for(int i = 0; i < this.mDispatchers.length; ++i) { if(this.mDispatchers[i] != null) { this.mDispatchers[i].quit(); } } }NetworkDispatcher单条任务器继承于Thread,这条线程管理一个任务队列,run方法体源码:
public void run() { Process.setThreadPriority(10); while(true) { Request request; while(true) { try { request = (Request)this.mQueue.take(); // RequestQueue中mNetworkQueue break; } catch (InterruptedException var4) { if(this.mQuit) { return; } } } try { request.addMarker("network-queue-take"); if(request.isCanceled()) { request.finish("network-discard-cancelled"); } else { this.addTrafficStatsTag(request); NetworkResponse e = this.mNetwork.performRequest(request); request.addMarker("network-http-complete"); if(e.notModified && request.hasHadResponseDelivered()) { request.finish("not-modified"); } else { Response response = request.parseNetworkResponse(e); request.addMarker("network-parse-complete"); if(request.shouldCache() && response.cacheEntry != null) { this.mCache.put(request.getCacheKey(), response.cacheEntry); request.addMarker("network-cache-written"); } request.markDelivered(); this.mDelivery.postResponse(request, response); } } } catch (VolleyError var5) { this.parseAndDeliverNetworkError(request, var5); } catch (Exception var6) { VolleyLog.e(var6, "Unhandled exception %s", new Object[]{var6.toString()}); this.mDelivery.postError(request, new VolleyError(var6)); } } }while(true)不断检测当前的队列,此时当队列中一有请求的时候就可以及时的被发出去。上文提到的ResponseDelivery,该类则负责执行结果的接收。
public void postResponse(Request<?> request, Response<?> response, Runnable runnable) { request.markDelivered(); request.addMarker("post-response"); this.mResponsePoster.execute(new ExecutorDelivery.ResponseDeliveryRunnable(request, response, runnable)); } ResponseDeliveryRunnable.run() public void run() { if(this.mRequest.isCanceled()) { this.mRequest.finish("canceled-at-delivery"); } else { if(this.mResponse.isSuccess()) { this.mRequest.deliverResponse(this.mResponse.result); } else { this.mRequest.deliverError(this.mResponse.error); } if(this.mResponse.intermediate) { this.mRequest.addMarker("intermediate-response"); } else { this.mRequest.finish("done"); } if(this.mRunnable != null) { this.mRunnable.run(); } } }所以在我们创建一个请求的时候,通过传入结果回调Listener,即可得到处理后的结果。
上文针对Volley处理网络的操作进行了一次梳理,还没有提到缓存相关部分的内容,其核心的思想也是本地保存请求链接和已经从网络上获取下来的数据,如果再发起一次请求,发现有备案,则直接从本地获取返回既可,不必再次访问网络。Volley默认开启4条访问线程,采用并行处理的方式,所以Volley的特性对于访问次数频繁但数据流比较少的应用场景还是比较高效率的。
相关文章推荐
- 程序员需要的好习惯
- 经典排序算法集锦
- Huatuo's Medicine
- uva 12545——Bits Equalizer
- 统计损失
- 代码整洁之道读书笔记--函数
- DDL、DML和DCL的比较【引用学习】
- Android自定义控件之仿美团下拉刷新
- android编程取消标题栏方法(appcompat_v7、NoTitleBar,2.3版本等)
- MYSQL查询SQL的注意事项和一些技巧总结
- VisionPro笔记:色彩区分
- InputSplit—>RecordReder—>map(key,value,context)的过程解析
- ThinkPHP如何禁止直接通过路径访问
- HDU 2049 错排变种
- CoreData的使用
- 杭电1282-回文
- Codeforces Round #328 (Div. 2) A. PawnChess 暴力
- 洛谷2633 王后万岁
- 一个轻量级的c编写的php接口平台框架
- 基于Jquery实现万圣节快乐特效