关于android的http的请求,相信大家都清楚主要的调用方式有两种,一个是HttpURLConnection,另一个是HttpClient,这两个相信大家都用过,也可能和笔者有同样的感受,就是仍然需要我们自己实现一些功能,调动起来并不是很便捷,当然既然这样我们便会寻找一些框架,来简化我们的工作,而volley无疑是比较好的选择,关于volley的使用,大家可以自行百度,比较简单,这里就不再介绍,我们主要从源码层面上,分析下volley的实现原理。
Volley源码的下载地址:
首先我们看下volley调用的入口toolbox下的volley类,其逻辑比较简单,只一个newRequestQueue()的静态方法,如下:
public static RequestQueue newRequestQueue(Context context, HttpStack stack)
{
File cacheDir = new File(context.getCacheDir(), "volley");
String userAgent = "volley/0";
try {
String packageName = context.getPackageName();
PackageInfo info = context.getPackageManager().getPackageInfo(packageName, 0);
userAgent = packageName + "/" + info.versionCode;
}
catch (PackageManager.NameNotFoundException localNameNotFoundException) {
}
if (stack == null) {
if (Build.VERSION.SDK_INT >= 9) {
stack = new HurlStack();
}
else
{
stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
}
}
Network network = new BasicNetwork(stack);
RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
queue.start();
return queue;
}
可以看到这个方法主要目的获取RequestQueue这个请求队列的对象,并调用start()方法开启队列的轮询。而在new RequestQueue()对象的时候,我们会传入两个参数,一个缓存Cache的具体实现类DiskBasedCache对象,另一个是对HttpStack进行二次封装的Network对象,具体这两个参数的作用,我们后续还会再次分析,接下来我们先看RequestQueue,这个类是我们在使用volley这个框架时主要的接触对象。
首先我们看下RequestQueue的构造方法RequestQueue():
public RequestQueue(Cache cache, Network network, int threadPoolSize, ResponseDelivery delivery)
{
this.mCache = cache;
this.mNetwork = network;
this.mDispatchers = new NetworkDispatcher[threadPoolSize];
this.mDelivery = delivery;
}
这个方法很简单,主要是些赋值操作,我们在此列出来是想主要说下这些参数的含义,方便我们的后续代码阅读和理解:
cache 负责存储http 缓存的类。
network 负责具体执行http请求的类,对HttpStack进行了二次封装。
threadPoolSize 在新建网络请求处理对象mDispatchers时传递的线程数,默认为4。
delivery: 负责将经过newwork二次封装后的response的具体执行,将response进行error和正常情况的分流处理。
通过上面的参数我们可以看出,其实RequestQueue主要是将请求分为缓存请求和网络请求,然后内部进行调用,因而逻辑并不复杂。
接下来我们看下volley调用的RequestQueue的start()方法:
public void start()
{
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();
}
}
首先调用stop(),中断并清空之前的缓存和网络请求;再重新创建对象并调用start()方法开启线程,这里我们要说明下,CacheDispatcher和NetworkDispatcher作为缓存请求和网络请求的分发机制,其本质都是Thread,只是根据业务逻辑进行了二次封装。看到这里我们会很想探知CacheDispatcher和NetworkDispatcher的具体实现,先稍等下,我们还需要分析下RequestQueue的add()方法,这个会是我们平时接触最多的方法。
public Request add(Request request)
{
request.setRequestQueue(this);
synchronized (this.mCurrentRequests) {
this.mCurrentRequests.add(request);
}
request.setSequence(getSequenceNumber());
request.addMarker("add-to-queue");
if (!request.shouldCache()) {
this.mNetworkQueue.add(request);
return request;
}
synchronized (this.mWaitingRequests) {
String cacheKey = request.getCacheKey();
if (this.mWaitingRequests.containsKey(cacheKey))
{
Queue stagedRequests = (Queue)this.mWaitingRequests.get(cacheKey);
if (stagedRequests == null) {
stagedRequests = new LinkedList();
}
stagedRequests.add(request);
this.mWaitingRequests.put(cacheKey, stagedRequests);
if (VolleyLog.DEBUG) {
VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold.", new Object[] { cacheKey });
}
}
else
{
this.mWaitingRequests.put(cacheKey, null);
this.mCacheQueue.add(request);
}
return request;
}
}
将resquest添加到mCurrentRequests集合,判断resquest是否需要缓存,如果不需要则将request添加到网络请求队列mNetworkQueue,并返回request。如果需要缓存,获取request对应的缓存key cacheKey,判断mWaitingRequests是已否含有这个key,如果没有,则将cacheKey添加到mWaitingRequests,并将将request添加到mCacheQueue;如果含有则request添加到cacheKey对应的mWaitingRequests中的queue中。
下面我们分别看CacheDispatcher和NetworkDispatcher。我们知道在volley中http的请求策略是如果需要缓存则会先从缓存当中去,如果缓存中没有则会再去网络中请求,当然如果不需要缓存,则会之后走网络请求。所以我们先分析CacheDispatcher。
我们依然先看构造函数:
public CacheDispatcher(BlockingQueue<Request> cacheQueue, BlockingQueue<Request> networkQueue, Cache cache, ResponseDelivery delivery)
{
this.mCacheQueue = cacheQueue;
this.mNetworkQueue = networkQueue;
this.mCache = cache;
this.mDelivery = delivery;
}
仍然都是赋值的操纵,可以看出CacheDispatcher主要是对缓存请求队列cacheQueue,网络请求队列networkQueue,response的执行者delivery,以及缓存策略实体cache进行操作。看到这里我们可以猜测下,这个类的主要策略思路,因为之前我们有分析过ImageLoader的缓存,同理我们猜测这里先是在缓存队列cacheQueue获得请求,并从cache获取请求对应的缓存resposne,将response通过delivery进行解包、分流;如果操作失败,则将请求转入到网络请求当中。
因为CacheDispatcher是继承的Thread,我们主要看run()方法,首先设定线程优先级,然后初始化cache,当mQuit不为true时进行while循环,我们来看while循环中的具体逻辑:
final Request request = (Request)this.mCacheQueue.take();
request.addMarker("cache-queue-take");
if (request.isCanceled()) {
request.finish("cache-discard-canceled");
}
else
{
Cache.Entry entry = this.mCache.get(request.getCacheKey());
if (entry == null) {
request.addMarker("cache-miss");
this.mNetworkQueue.put(request);
}
else if (entry.isExpired()) {
request.addMarker("cache-hit-expired");
request.setCacheEntry(entry);
this.mNetworkQueue.put(request);
}
else
{
request.addMarker("cache-hit");
Response response = request.parseNetworkResponse(
new NetworkResponse(entry.data, entry.responseHeaders));
request.addMarker("cache-hit-parsed");
if (!entry.refreshNeeded())
{
this.mDelivery.postResponse(request, response);
}
else
{
request.addMarker("cache-hit-refresh-needed");
request.setCacheEntry(entry);
response.intermediate = true;
this.mDelivery.postResponse(request, response, new Runnable()
{
public void run() {
try {
CacheDispatcher.this.mNetworkQueue.put(request);
} catch (InterruptedException localInterruptedException) {
}
} } );
}
}
}
这里的逻辑主要是在mCacheQueue中取出request,判断它是否结束,如果结束则直接返回;否则从mCache中获取request的对应缓存内容,如果内容为空则走网络请求,如果不为空判断是否失效,失效的话也走网络请求;如果没有失效,则将缓存中的内容封装为NetworkResponse并同过parseNetworkResponse()方法返回response。最后看缓存是否有刷新需求,没有的话直接调用mDelivery.postResponse()返回response,有的话返回response并通过Runnable启个延时线程,将request添加到网络请求队列。
接下来我们看网络请求分发线程NetworkDispatcher,关于构造方法我们就不再复述了,直接看run()函数:
public void run()
{
Process.setThreadPriority(10);
while (true)
{
Request request;
try {
request = (Request)this.mQueue.take();
} catch (InterruptedException e) {
}
if (this.mQuit) {
return;
try
{
request.addMarker("network-queue-take");
if (request.isCanceled()) {
request.finish("network-discard-cancelled");
}
else
{
if (Build.VERSION.SDK_INT >= 14) {
TrafficStats.setThreadStatsTag(request.getTrafficStatsTag());
}
NetworkResponse networkResponse = this.mNetwork.performRequest(request);
request.addMarker("network-http-complete");
if ((networkResponse.notModified) && (request.hasHadResponseDelivered())) {
request.finish("not-modified");
}
else
{
Response response = request.parseNetworkResponse(networkResponse);
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 volleyError) { parseAndDeliverNetworkError(request, volleyError); }
catch (Exception e)
{
Request request;
VolleyLog.e(e, "Unhandled exception %s", new Object[] { e.toString() });
this.mDelivery.postError(request, new VolleyError(e));
}
}
}
}
首先设定线程的优先级,从网络请求队列中获取request,判断request是否结束,如果没结束则通过mNetwork调用performRequest()方法执行网络请求,并将得到reponse,并根据需求进行处理,如果需要缓存则将缓存添加到mCache,然后将reponse通过mDelivery进行分发。
接下来我们看下response的处理机制ExecutorDelivery,ExecutorDelivery是接口ResponseDelivery的具体实现类,关于ResponseDelivery比较简单,定义了异常返回和正常返回的处理方法,这里不再做说明,我们先看下他的构造函数:
public ExecutorDelivery(final Handler handler)
{
this.mResponsePoster = new Executor()
{
public void execute(Runnable command) {
handler.post(command);
}
};
}
定义了一个Executor对象,通过主线程UI的Handler的post()方法来处理Runnable对象。
下面我们来看正常情况下的返回处理逻辑:
public void postResponse(Request<?> request, Response<?> response, Runnable runnable)
{
request.markDelivered();
request.addMarker("post-response");
this.mResponsePoster.execute(new ResponseDeliveryRunnable(request, response, runnable));
}
新建ResponseDeliveryRunnable对象,并通过execute进行处理,当然这时的该Runnable对象是交给构造方法中的Handler进行处理的,在默认的逻辑,这个Handler是UI主线程的。
关于异常的处理。