Volley基本用法
首先创建一个RequestQueue
public class MyApplication extends Application {
private static RequestQueue mQueue;
@Override
public void onCreate() {
super.onCreate();
mQueue = Volley.newRequestQueue(getApplicationContext());
}
public static RequestQueue getVolleyQueue(){
return mQueue;
}
}
1)StringRequest(GET请求)
StringRequest mStrReq = new StringRequest(Request.Method.GET, url,
new Response.Listener<String>() {
@Override
public void onResponse(String response) {
// 这个方法运行在主线程中,可以直接更新ui
// 通过参数返回请求到的数据
mTv_result.setText(response);
Toast.makeText(StrReqActivity.this, "下载成功", Toast.LENGTH_SHORT).show();
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
// 这个方法运行在主线程中,可以直接更新ui
// 失败在这里处理
Toast.makeText(StrReqActivity.this, "下载失败", Toast.LENGTH_SHORT).show();
}
});
//设置Tag值
mStrReq.setTag("100");
btn_req.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//3.把请求对象添加到请求队列中,会自动发出请求
mRequestQueue.add(mStrReq);
}
});
2)StringRequest(POST请求)
String url_post = url;
StringRequest mStrReq = new StringRequest(Request.Method.POST, url_post
, new Response.Listener<String>() {
@Override
public void onResponse(String response) {
//
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
//
}
}) {//这里需要重写getParams方法
@Override
protected Map<String, String> getParams() throws AuthFailureError {
//把post的请求参数,放入请求体中
//请求条件:platform=2&gifttype=1&compare=60841c5b7c69a1bbb3f06536ed685a48
Map<String, String> params = new HashMap<>();
params.put("platform", "2");
params.put("gifttype", "1");
params.put("compare", "60841c5b7c69a1bbb3f06536ed685a48");
return params;
}
};
//点击加入到请求队列中
btn_req_json.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mRequestQueue.add(mStrReq);
}
});
Volley与Activity生命周期关联
手动停止请求任务
@Override
protected void onDestroy() {
super.onDestroy();
//取消请求:有三种方式
//1. 取消对应的请求对象
mStrReq.cancel();
//2. 取消请求队列中对应tag的请求
//mRequestQueue.cancelAll("100");
//3. 取消请求队列中所有的请求
//mRequestQueue.cancelAll(this);
}
3)JsonObjectRequest与JsonArrayRequest(json数据请求)
JsonObjectRequest jsonObjectRequest =new JsonObjectRequest(Request.Method.GET, url, new Response.Listener<JSONObject>() {
@Override
public void onResponse(JSONObject response) {
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
}
});
4)ImageRequest图片请求
//1) 图片下载的url
//2) 下载成功后,返回一个bitmap对象
//3)4) 最大宽度和最大高度,如果超过最大宽度和高度,会进行压缩到你设置的宽度和高度,0不限制
//5) 图片加载的形式
//6)图片显示的质量:RGB_565: 每个像素2字节 ARGB_8888:每个像素占4个字节
//7)下载图片失败后,在这里边处理
ImageRequest imgRequest = new ImageRequest(url_img, new Response.Listener<Bitmap>() {
@Override
public void onResponse(Bitmap response) {
//显示成功的图片
iv_show.setImageBitmap(response);
}
}, 0, 0, ImageView.ScaleType.FIT_XY, Bitmap.Config.RGB_565, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
//设置失败的图片
iv_show.setBackgroundResource(R.mipmap.ic_launcher);
}
});
5)ImageLoader图片加载
//1、首先有请求队列
RequestQueue requestQueue = Volley.newRequestQueue(this);
//2、创建出ImageLoader对象
ImageLoader loader = new ImageLoader(requestQueue, new BitmapCache());
//3、调用ImageLoader的get方法设置图片
//getImageListener中的三个参数:1,下载好的图片设置给哪个控件 2,默认图片 3.下载失败的图片
loader.get(url_img, ImageLoader.getImageListener(iv_show, R.mipmap.ic_default, R.mipmap.ic_error));
重载方法
public ImageContainer get(String requestUrl, final ImageListener listener) {
return get(requestUrl, listener, 0, 0);
}
public ImageContainer get(String requestUrl, ImageListener imageListener,
int maxWidth, int maxHeight) {
return get(requestUrl, imageListener, maxWidth, maxHeight, ScaleType.CENTER_INSIDE);
}
public ImageContainer get(String requestUrl, ImageListener imageListener,
int maxWidth, int maxHeight, ScaleType scaleType) {
}
6)NetworkImageView带下载功能的ImageView
NetworkImageView拓展了ImageView实现了下載的功能,(相对于使用普通的ImageView,避免由于控件重用带来的图片錯位的问题)。使用方法:
把使用到ImageView的地方替換成NetworkImageView
初始化ImageLoader
通过NetWorkImageView对象设置下载图片的配置信息(如最大宽高等)
通过NetWorkImageView对象调用setImageUrl方法进行图片的下载
//事先初始化好的RequestQueue和ImageLoader
RequestQueue requestQueue = Volley.newRequestQueue(this);
ImageLoader imageLoader = new ImageLoader(requestQueue, new BitmapCache());
//NetWorkImageView
netiv.setDefaultImageResId(R.mipmap.ic_default); //设置默认的图片的id
netiv.setErrorImageResId(R.mipmap.ic_error); //设置下载失败后的图片的id
netiv.setImageUrl(url_img, imageLoader); //下载图片
Volley的实现原理
Volley.newRequestQueue() 返回个RequestQueue实例,该静态方法有两个重载,如下:
RequestQueue newRequestQueue(Context context);
RequestQueue newRequestQueue(Context context, HttpStack stack);
当调用第一个方法的时候也会调用第二个方法
public static RequestQueue newRequestQueue(Context context) {
return newRequestQueue(context, null);
}
第二个参数HttpStack,是用来进行网路请求的,由Volley的整体框架图,可以看出其有两个实现子类,选择哪个子类是有SDK的版本决定的,源码如下:
public static RequestQueue newRequestQueue(Context context, HttpStack stack) {
//放置缓存的地方
File cacheDir = new File(context.getCacheDir(), DEFAULT_CACHE_DIR);
String userAgent = "volley/0";
if (stack == null) {
//如果版本号大于9(V2.3)
if (Build.VERSION.SDK_INT >= 9) {
//创建基于HttpURLConnection的HttpStack
stack = new HurlStack();
} else {
//创建基于HttpClient的HttpStack
stack = new HttpClientStack(AndroidHttpClient.newInstance(userAgent));
}
}
//创建网络请求和queue
Network network = new BasicNetwork(stack);
RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir), network);
queue.start();
return queue;
}
PS:主要是由于低版本的情况下HttpURLConnection存在BUG,当对一个可读的InputStream调用close()方法时,就有可能会导致连接池失效了,所以才会这样分版本创建
再来看看RequestQueue的构造方法
/**
* Creates the worker pool. Processing will not begin until {@link #start()} is called.
*
* @param cache A Cache to use for persisting responses to disk
* @param network A Network interface for performing HTTP requests
* @param threadPoolSize Number of network dispatcher threads to create
* @param delivery A ResponseDelivery interface for posting responses and errors
*/
public RequestQueue(Cache cache, Network network, int threadPoolSize,
ResponseDelivery delivery) {
mCache = cache;
mNetwork = network;
mDispatchers = new NetworkDispatcher[threadPoolSize];
mDelivery = delivery;
}
PS:这里的第三个参数是网络请求的线程数,默认值为4 RequestQueue.Start()
public void start() {
stop(); // 确定当前线程已经停下来了
// 只创建一条缓存分发线程并且启动,注入mNetWorkQueue,用于缓存获取失
// 败时进行网络请求
mCacheDispatcher = new CacheDispatcher(mCacheQueue, mNetworkQueue, mCache, mDelivery);
mCacheDispatcher.start();
//创建多条网络请求线程并启动,在创建时注入mCache,用于缓存
for (int i = 0; i < mDispatchers.length; i++) {
NetworkDispatcher networkDispatcher = new NetworkDispatcher(mNetworkQueue, mNetwork,
mCache, mDelivery);
mDispatchers[i] = networkDispatcher;
networkDispatcher.start();
}
}
首先是创建一个CacheDispatcher缓存发送者,它是一个缓存发送的线程,然后调用CacheDispatcher的start()函数。
然后循环创建NetworkDispatcher对象,因为默认的线程数是4,所以会循环4次创建网络调度(和相应的线程)到池大小,接着再调用start()函数。
所以其实这时候有5条线程在后台运行,不断等待网络请求的到来,其中CacheDispatcher是缓存线程,这也是为什么volley不适于数据量大、通讯频繁的网络操作,因为会占用网络请求的访问线程。
RequestQueue.add()
public <T> Request<T> add(Request<T> request) {
// Tag the request as belonging to this queue and add it to the set of current requests.
request.setRequestQueue(this);
synchronized (mCurrentRequests) {
mCurrentRequests.add(request);
}
// Process requests in the order they are added.
request.setSequence(getSequenceNumber());
request.addMarker("add-to-queue");
// 判断是否可缓存,如果不可缓存,直接添加到网络请求队列
if (!request.shouldCache()) {
mNetworkQueue.add(request);
return request;
}
// Insert request into stage if there's already a request with the same cache key in flight.
synchronized (mWaitingRequests) {
String cacheKey = request.getCacheKey();
//如果已经存在当前Request,那么只需要在等待队列里面插入当前请求即可,防止多次网络访问
if (mWaitingRequests.containsKey(cacheKey)) {
// There is already a request in flight. Queue up.
Queue<Request<?>> stagedRequests = mWaitingRequests.get(cacheKey);
if (stagedRequests == null) {
stagedRequests = new LinkedList<Request<?>>();
}
stagedRequests.add(request);
mWaitingRequests.put(cacheKey, stagedRequests);
} else {
// Insert 'null' queue for this cacheKey, indicating there is now a request in
// flight.
mWaitingRequests.put(cacheKey, null);
mCacheQueue.add(request);
}
return request;
}
}
添加请求首先将Request标识为当前队列,然后添加到mCurrentRequests当前请求队列里。接着判断是否可以缓存,如果不行则直接调用网络队列进行请求然后方法结束,如果可以缓存,再判断当前等待队列是否包含这个请求,如果是添加到等待队列value队列中(PS:第一天添加的是的,等待队列的value列是为null的,只是用来指出这个地方有一个请求),防止重复请求;如果不是那就添加到缓存队列中。
请求处理过程
CacheDispatcher的run方法
@Override
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
// Make a blocking call to initialize the cache.
mCache.initialize();
while (true) {
try {
// 阻塞获取一个Request,queue原型为BlockingQueue
final Request<?> request = mCacheQueue.take();
request.addMarker("cache-queue-take");
// 如果请求已经取消,则跳过该请求
if (request.isCanceled()) {
request.finish("cache-discard-canceled");
continue;
}
// 尝试从缓存里面获取数据
Cache.Entry entry = mCache.get(request.getCacheKey());
//如果为空,表示没有缓存,添加请求到网络队列
if (entry == null) {
request.addMarker("cache-miss");
// Cache miss; send off to the network dispatcher.
mNetworkQueue.put(request);
continue;
}
// 如果缓存过期了,添加到网络请求对列
if (entry.isExpired()) {
request.addMarker("cache-hit-expired");
request.setCacheEntry(entry);
mNetworkQueue.put(request);
continue;
}
// We have a cache hit; parse its data for delivery back to the request.
request.addMarker("cache-hit");
Response<?> response = request.parseNetworkResponse(
new NetworkResponse(entry.data, entry.responseHeaders));
request.addMarker("cache-hit-parsed");
if (!entry.refreshNeeded()) {
// 不需要刷新缓存,直接进行结果传递
mDelivery.postResponse(request, response);
} else {
// 需要刷新的缓存,在把缓存结果传递时,同时应该进行缓存的刷新
request.addMarker("cache-hit-refresh-needed");
request.setCacheEntry(entry);
response.intermediate = true;
// Post the intermediate response back to the user and have
// the delivery then forward the request along to the network.
mDelivery.postResponse(request, response, new Runnable() {
@Override
public void run() {
try {
mNetworkQueue.put(request);
} catch (InterruptedException e) {
// Not much we can do about this.
}
}
});
}
} catch (InterruptedException e) {
// We may have been interrupted because it was time to quit.
if (mQuit) {
return;
}
continue;
}
}
}
上面也提到了add()方法只是简单的把请求插入到了网络请求对列或者缓存请求对列,而在消息处理过程中会有CacheDispatcher一直在循环中的,并且阻塞获取获取到请求,然后判断这个请求是否过期,如果过期就跳过这个请求,如果没过期就从缓存中获取到当前请求的缓存,如果缓存为空或者缓存过期就把该请求添加到NetWorkQueue中,如果缓存存在,那就再判断缓存是否需要刷新,如果需要刷新就传递mCache的内容并把这个请求添加到请求队列中,如果不需要刷新就直接传递mCache的内容。
小结
- 当一个Request请求添加到RequestQueue请求队列中,Volley就开始工作了。RequestQueue请求队列中持有一个CacheDispatcher缓存管家和一组NetworkDispatcher网络管家。
- RequestQueue会先叫来CacheDispatcher缓存管家,让他去看看,当前请求的数据在没在cache中。
1) 当前的数据在cache中,那就把数据从cache中取出来,然后经过一番加工,将加工好的数据交付给主线程
2)当前数据没在cache中,进行第3步 - 进行到了这一步,那肯定是数据没有在缓存中,那只能去网络中获取了,这时候RequestQueue会叫来NetworkDispatcher,NetworkDispatcher可是有一帮呢,其实这是一个线程池,默认情况下会启动4个线程去网络下载数据。所以RequestQueue把当前闲着的NetworkDispatcher叫来,给他们分配任务。
- 拿到任务的NetworkDispatcher就会去网络上下载数据了,与此同时,他会判断下载到的数据能否写入到cache缓存中,如果可以的话就写入cache,以便于下一次直接从cache中获取到数据。最后,将数据加工,交付给主线程。(PS:对请求返回的结果,是通过我们的Request.parseNetworkResponse(NetworkResponse response)处理的,也就是说,如果是StringRequest,那么这个方法就是把请求结果转换为String,所以可以通过实现这个方法,自定义一个Request)