Volley源码解析
很早之前就想写下关于Volley的源码解析。一开始学android网络访问都是使用HttpClient,刚接触么Volley的时候就瞬间爱不释手,虽说现在项目中使用OkHttp多些(Volley更新慢),但是作为google自家推出的网络框架,Volley还是有很多值得学习的地方。这篇博客是我对Volley源码分析后的一个总结。
Volley的使用
Volley的使用非常简单,相信大家都很熟悉。首先需要获取到一个RequestQueue对象。
RequestQueue mQueue = Volley.newRequestQueue(context);
如果想通过网络获取json,如下:
StringRequest stringRequest = new StringRequest("http://www.baidu.com",
new Response.Listener() {
@Override
public void onResponse(String response) {
Log.d("TAG", response);
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.e("TAG", error.getMessage(), error);
}
});
只要在onResponse中处理返回的response即可。如果访问出错,则会调用onErrorResonse方法。 注意Volley是异步,是在子线程中进行网络访问,而onResponse里的代码是在主线程中执行。所以使用Volley的地方切记不要把它当成单线程,这是初学者经常犯错的地方。最后,将这个StringRequest对象添加到RequestQueue里面就可以了。
mQueue.add(stringRequest);
如果要加载图片,则首先要定义一个ImageCache,用于定义图片的缓存。通过ImageLoader来加载图片,ImageListener则用于指定ImageView以及加载失败和加载过程中默认图片 :
private final LruCache mLruCache = new LruCache(
(int) (Runtime.getRuntime().maxMemory() / 10))
{
@Override
protected int sizeOf(String key, Bitmap value)
{
return value.getRowBytes() * value.getHeight();
}
};
@Override
public void putBitmap(String url, Bitmap bitmap)
{
mLruCache.put(url, bitmap);
}
@Override
public Bitmap getBitmap(String url)
{
return mLruCache.get(url);
}
});
ImageListener listener = ImageLoader.getImageListener(imageView,
R.drawable.default, R.drawable.failed);
imageLoader.get(imageurl, listener);
介绍完简单用法之后,就来分析源代码了。
Volley源码分析
先看下官网给出的介绍图:
vc34wufH68fzttPB0NbQo6zIu7rztKbA7beiy81IVFRQx+vH86OsveLO9s/s06a94bn7o6zQtMjru7q05qOssqK72LX31vfP37PMoaO908/CwLTP6s+4tcS9+NDQt9bO9qGjPGJyIC8+DQqyu9PDy7WjrMjrv9q/z7aoysdWb2xsZXkubmV3UmVxdWVzdFF1ZXVlKGNvbnRleHQpoaPPyL+0z8JuZXdSZXF1ZXN0UXVldWW1xLT6wuujujwvcD4NCjxwcmUgY2xhc3M9"brush:java;">
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;
}
首先封装得到userAgent,User-Agent 字段设置为 App 的packageName/{versionCode},如果异常则使用 “volley/0”。上面代码主要是实例化stack ,如果SDK版本大于9,使用HurlStack,否则使用HttpClientStack。实际上HurlStack的内部就是使用HttpURLConnection进行网络通讯的,而HttpClientStack的内部则是使用HttpClient进行网络通讯的。也就是说android2.2以上的都是使用HttpURLConnection,否则使用HttpClient。接着new了一个RequestQueue,并调用它的start方法。来看下它的RequestQueue构造方法:
/** Number of network request dispatcher threads to start. */
private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4;
/** Cache interface for retrieving and storing responses. */
private final Cache mCache;
/** Network interface for performing requests. */
private final Network mNetwork;
/** Response delivery mechanism. */
private final ResponseDelivery mDelivery;
/** The network dispatchers. */
private NetworkDispatcher[] mDispatchers;
public RequestQueue(Cache cache, Network network, int threadPoolSize,
ResponseDelivery delivery) {
mCache = cache;
mNetwork = network;
mDispatchers = new NetworkDispatcher[threadPoolSize];
mDelivery = delivery;
}
初始化主要就是4个参数:mCache、mNetwork、mDispatchers、mDelivery。第一个是硬盘缓存;第二个主要用于Http相关操作;第三个用于转发请求的;第四个参数用于把结果转发到UI线程,通过它来对外声明接口。接下来看下start方法。
private static final int DEFAULT_NETWORK_THREAD_POOL_SIZE = 4;
/** Cache interface for retrieving and storing responses. */
private final Cache mCache;
/** Network interface for performing requests. */
private final Network mNetwork;
/** Response delivery mechanism. */
private final ResponseDelivery mDelivery;
/** The network dispatchers. */
private NetworkDispatcher[] mDispatchers;
/**
* Starts the dispatchers in this queue.
*/
public void start() {
stop(); // Make sure any currently running dispatchers are stopped.
// Create the cache dispatcher and start it.
mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
mCacheDispatcher.start();
// Create network dispatchers (and corresponding threads) up to the pool size.
for (int i = 0; i < mDispatchers.length; i++) {
NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,
mCache, mDelivery);
mDispatchers[i] = networkDispatcher;
networkDispatcher.start();
}
}
/**
* Stops the cache and network dispatchers.
*/
public void stop() {
if (mCacheDispatcher != null) {
mCacheDispatcher.quit();
}
for (int i = 0; i < mDispatchers.length; i++) {
if (mDispatchers[i] != null) {
mDispatchers[i].quit();
}
}
}
首先调用stop()方法,确保此时所有转发器都处于停止状态。接下来就new了一个CacheDispatcher转发器,它其实就是一个线程,用于硬盘缓存。再new了四个NetworkDispatcher转发器,用于网络请求。并分别调用这些线程的start()方法。如果是加载图片,我们还需定义一个imageLoader,来看看Volley中为我们定义的ImageLoader,主要看它的get方法: