1

Android:Volley源码解析

 2 years ago
source link: http://www.androidchina.net/2965.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

简单实例

Volley是一个封装HttpUrlConnection和HttpClient的网络通信框架,集AsyncHttpClient和Universal-Image-Loader的优点于了一身,既可以像AsyncHttpClient一样非常简单地进行HTTP通信,也可以像Universal-Image-Loader一样轻松加载并缓存下载的图片。Volley在性能方面也进行了大幅度的调整,它的设计目标就是进行数据量不大,但通信频繁的网络操作,而对于大数据量的网络操作,比如说下载文件等,Volley的表现就会比较糟糕。从下面这个简单的实例来研究一下源码。

RequestQueue mQueue = Volley.newRequestQueue(MainActivity.this);
StringRequest stringRequest = new StringRequest("http://www.baidu.com", new Response.Listener<String>() {
@Override
public void onResponse(String s) {
tv.setText(s);
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError volleyError) {
}
});
mQueue.add(stringRequest);

流程

1、以静态工厂形式实例化一个RequestQueue对象

RequestQueue mQueue = Volley.newRequestQueue(MainActivity.this);

  • 首先看一下RequestQueue这个类:
public class RequestQueue {
private AtomicInteger mSequenceGenerator;
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;
private final ResponseDelivery mDelivery;
private NetworkDispatcher[] mDispatchers;
private CacheDispatcher mCacheDispatcher;
public RequestQueue(Cache cache, Network network, int threadPoolSize, ResponseDelivery delivery) {
this.mSequenceGenerator = new AtomicInteger();
this.mWaitingRequests = new HashMap();
this.mCurrentRequests = new HashSet();
this.mCacheQueue = new PriorityBlockingQueue();
this.mNetworkQueue = new PriorityBlockingQueue();
this.mCache = cache;
this.mNetwork = network;
this.mDispatchers = new NetworkDispatcher[threadPoolSize];
this.mDelivery = delivery;
}
public RequestQueue(Cache cache, Network network, int threadPoolSize) {
this(cache, network, threadPoolSize, new ExecutorDelivery(new Handler(Looper.getMainLooper())));
}
public RequestQueue(Cache cache, Network network) {
this(cache, network, 4);
}
...
}

从构造函数可知,mWaitingRequests、mCurrentRequests、mCacheQueue、mNetworkQueue是以组合形式实例化,后两者是阻塞队列;而mCache、mNetwork是以聚合形式注入;mDelivery默认也是组合形式new ExecutorDelivery(new Handler(Looper.getMainLooper())))实例化。

  • newRequestQueue方法:
public static RequestQueue newRequestQueue(Context context) {
return newRequestQueue(context, (HttpStack)null);
}
public static RequestQueue newRequestQueue(Context context, HttpStack stack) {
File cacheDir = new File(context.getCacheDir(), "volley");
String userAgent = "volley/0";
try {
String network = context.getPackageName();
PackageInfo queue = context.getPackageManager().getPackageInfo(network, 0);
userAgent = network + "/" + queue.versionCode;
} catch (NameNotFoundException var6) {
;
}
if(stack == null) {
if(VERSION.SDK_INT >= 9) {
stack = new HurlStack();
} else {
stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
}
}
BasicNetwork network1 = new BasicNetwork((HttpStack)stack);
RequestQueue queue1 = new RequestQueue(new DiskBasedCache(cacheDir), network1);
queue1.start();
return queue1;
}

结合RequestQueue类可知,实例化的RequestQueue对象,注入了new DiskBasedCache(cacheDir)network1,缓存方式默认是磁盘缓存,NetWork对象会根据系统版本,选用不同的Http通信方式。

  • queue.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();
}
}

CacheDispatcher和NetworkDispatcher都是继承Thread类,所以这个方法生成一条缓存分发线程,和四条网络线程。

  • CacheDispatcher类继承Thread类,所有参数都是聚合形式注入,看下关键的run()方法,由于代码较长,这里不贴了,分段分析下几个比较重要的方法
while(true){
...
Request e = (Request)this.mCacheQueue.take();
...
}

首先任务是一个死循环,由于mCacheQueue是个阻塞队列,所以将不断地从阻塞队列读取Request

Entry entry = this.mCache.get(e.getCacheKey());
if(entry == null) {
e.addMarker("cache-miss");
this.mNetworkQueue.put(e);
} else if(entry.isExpired()) {
e.addMarker("cache-hit-expired");
e.setCacheEntry(entry);
this.mNetworkQueue.put(e);
} else {
e.addMarker("cache-hit");
Response response = e.parseNetworkResponse(new NetworkResponse(entry.data, entry.responseHeaders));
...
}

判断请求是否有缓存,如果没有或者缓存已经过期,将请求放到网络队列里面。否则找到缓存,则进行下面的操作。

Response response = e.parseNetworkResponse(new NetworkResponse(entry.data, entry.responseHeaders));

parseNetworkResponse是Request类的抽象方法,我们进去StringRequest看下:

protected Response<String> parseNetworkResponse(NetworkResponse response) {
String parsed;
try {
parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
} catch (UnsupportedEncodingException var4) {
parsed = new String(response.data);
}
return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));
}

可看作是对网络下载的数据进行解析处理,然后返回。

this.mDelivery.postResponse(e, response);

最后进行这一步,mDelivery是在RequestQueue里面实例化后注入CacheDispatcher的,具体的实例化对象:new ExecutorDelivery(new Handler(Looper.getMainLooper()))。看下ExecutorDelivery类,找到postResponse方法。

public void postResponse(Request<?> request, Response<?> response) {
this.postResponse(request, response, (Runnable)null);
}
public void postResponse(Request<?> request, Response<?> response, Runnable runnable) {
...
this.mResponsePoster.execute(new ExecutorDelivery.ResponseDeliveryRunnable(request, response, runnable    ));
}

继续往下看

private class ResponseDeliveryRunnable implements Runnable {
...
run(){
...
if(this.mResponse.isSuccess()) {
this.mRequest.deliverResponse(this.mResponse.result);
}
...
}
...
}

deliverResponse方法同样是Request类的抽象方法,我们进去StringRequest看下

protected void deliverResponse(String response) {
this.mListener.onResponse(response);
}

就一句回调

  • NetworkDispatcher类同样继承Thread类,其分析过程和CacheDispatcher差不多,重要的同样是以下几步:

1、从网络阻塞队列读取请求,request = (Request)this.mQueue.take();
2、网络下载,NetworkResponse e = this.mNetwork.performRequest(request);(如果是CacheDispatcher这一步就是缓存判断)
3、处理下载后的数据,Response response = request.parseNetworkResponse(e);
3、对处理后的数据进行回调,this.mDelivery.postResponse(e, response)。

2、实例化一个Request对象

StringRequest stringRequest = new StringRequest (url,listener,errorListener);

public class StringRequest extends Request<String> {
private final Listener<String> mListener;
public StringRequest(int method, String url, Listener<String> listener, ErrorListener errorListener) {
super(method, url, errorListener);
this.mListener = listener;
}
public StringRequest(String url, Listener<String> listener, ErrorListener errorListener) {
this(0, url, listener, errorListener);
}
protected void deliverResponse(String response) {
this.mListener.onResponse(response);
}
protected Response<String> parseNetworkResponse(NetworkResponse response) {
String parsed;
try {
parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
} catch (UnsupportedEncodingException var4) {
parsed = new String(response.data);
}
return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));
}
}

由第一个分析步骤可以知道,这个Request主要就是进行两个操作,也就是重写两个方法。

  1. protected abstract Response<T> parseNetworkResponse(NetworkResponse var1);对下载后的数据进行解析处理;
  2. protected abstract void deliverResponse(T var1);最后回调操作这个数据的方法。

所以构造函数仅需下载地址和回调操作的方法。

3、调用queue.add()方法

if(!request.shouldCache()) {
this.mNetworkQueue.add(request);
return request;
}

如果不需要缓存就直接添加到网络队列里面,Request有个比较重要的布尔字段mShouldCache,默认是用来判断是否要进行磁盘缓存的。

this.mCacheQueue.add(request);

否则将其添加到缓存队列,这个方法上面也会进行一些当前队列和等待队列的防重复的操作。

小结

这里写图片描述

框架部分:
1、实例化一个RequestQueue对象,开启一条缓存线程和默认的四条网络线程,线程不断地从缓存阻塞队列和网络阻塞队列里面读取请求;
2、如果缓存线程从缓存队列里面读取的请求已经缓存过,则解析数据回调操作方法,否则将其添加到网络队列;
3、如果缓存线程从缓存队列里面读取的请求没有缓存过,则添加到网络队列。
4、网络线程从网络阻塞队列不断读取请求,读到请求后则由封装好的HttpStack对象进行网络下载处理、下载后回调对数据处理的方法,处理后回调操作数据的方法。

客户部分:
1、实例化一个请求对象,在请求对象里面重写处理网络下载后的数据的方法,和操作处理后的数据的方法。
2、将请求对象添加到请求队列,请求需要缓存则会被添加到分配到缓存队列,不需要则被添加到网络队列。

之前看过一个问题,说框架和库有什么不同,高人答曰:框架是他调用你代码,库是你调用他代码。优秀的框架拓展性是如此之强,虽然自己远没那个能力,不过也算开了眼界!

转载请注明:Android开发中文站 » Android:Volley源码解析


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK