什么是Volley
- Volley 的单词涵义是:迸发、万箭齐发。 是比喻的命名方式,寓指网络请求并发效率高。
Volley 的重要方法
- mQueue = Volley.newRequestQueue(context);
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;
}
Volley 在新建RequestQueue 的时候,其实是新建了两个 Queue, 一个CacheQueue(用于获取获取本地缓存结果),一个NetworkQueue(用于网络请求); 在 queue.start(); 中, start 的是 一个 CacheDispatcher 和 4个 NetworkDispatcher, 分别作用在前面的两个Queue
mQueue = Volley.newRequestQueue(context); 最好放在 Application 中,整个app 只有一个requestQueue.
new ExecutorDelivery(new Handler(Looper.getMainLooper())) 在RequestQueue 的参数中,有一个delivey 将handler 传入其中,后面就是通过handler 的post 在主线程中回调 Listener 的。
需要注意一下的是,网络请求,在android 版本2.2 以前建议使用的是 httpclient, 而在 2.2 以后建议使用的是HttpUrlConnection 。 5.2 以后,若使用 HttpClient 会出现通篇的warning, 这是因为google 已经强烈要求使用 HttpUrlConnection 了。 原因是: HttpUrlConnection 相比于 HttpClient 体积更小,API 更为简单,而且扩展性比HttpClient 好很多,在压缩、缓存、省电、减少流量等各方面都要突出一些。 所以HttpUrlConnection 更适用于 android。
- mQueue.add(new Request());
/**
* Adds a Request to the dispatch queue.
* @param request The request to service
* @return The passed-in request
*/
public Request add(Request 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 the request is uncacheable, skip the cache queue and go straight to the network.
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();
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);
if (VolleyLog.DEBUG) {
VolleyLog.v("Request for cacheKey=%s is in flight, putting on hold.", cacheKey);
}
} 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 是完全可以自定义的。Volley 提供的 Request 有 StringRequest 、 JsonObjectRequest、 JsonArrayRequest、 ImageRequest 等等。
往Queue中添加Request 的过程是: 先判断 Request 的 shouldCache 属性,如果该属性为 true 则将 Request 添加到 CacheQueue 中,当然其中会有 waitingRequest 的 list 来控制相同的 请求不再重复添加。 如果该属性为 false, 则直接加入到 NetworkRequest 中。如果本地没有缓存,即在CacheQueue处理过后,发现没有数据,则重新添加到NetworkQueue 中。 在请求数据回来之后,再次判断 showCache 来写缓存及返回数据到UI线程。
自定义Request
- 覆盖 ContentType 属性
@Override
public String getBodyContentType() {
// super.getBodyContentType();
return "application/json";
}
- 覆盖 getBody() , Volley 默认的添加 content params 的方法是添加 “&”等等,与我们经常使用的是不一样的,所以覆盖 getParams() 还达不到我们想要的效果,所以直接覆盖 getBody() 方法。
if (mParams != null && !mParams.isEmpty()) {
String ss = JSON.toJSONString(mParams);
return ss.getBytes();
}
return super.getBody();
- 添加自定义 header
@Override
public Map<String, String> getHeaders() throws AuthFailureError {
return mHeaderParams;
}
- 结果处理
@Override
protected Response<T> parseNetworkResponse(NetworkResponse response) {
// 在这处理
}
- 其他
默认的 get 请求, 传入的 params 不会被处理,所以需要在外面将 url 自行拼接好再传入 Volley 中。
private String toGetUrl(String url, Map<String, Object> entity) {
StringBuilder urlSB = new StringBuilder();
if (entity != null && !entity.isEmpty()) {
for (String key : entity.keySet()) {
if (urlSB.length() != 0) {
urlSB.append("&");
}
urlSB.append(key + "="
+ Uri.encode(JSON.toJSONString(entity.get(key))));
}
}
if (urlSB == null || "".equals(urlSB.toString())) {
return url;
}
return url + "?" + urlSB.toString();
}
- 常用属性设置
setShouldCache(false);
setRetryPolicy(
// 超时时间、retry次数、超时时间乘数 例如第一次5秒 第二次超时想要设置10秒, 乘数就为2 默认为1 可以是float
new DefaultRetryPolicy(5000, DefaultRetryPolicy.DEFAULT_MAX_RETRIES, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT)
);
setTag("tag"); // 标记 可用于cancle request
ImageLoader、 NetworkImageView
mNetworkImageView.setDefaultImageResId(R.drawable.ic_launcher); // 默认图片
mNetworkImageView.setErrorImageResId(R.drawable.ic_launcher); // 发生错误时
mNetworkImageView.setImageUrl(URLS[1], MyApplication.genImageLoader());// 请求地址,及 imageloader
- MyApplication
@Override
public void onCreate() {
super.onCreate();
mInstance = this;
mQueue = Volley.newRequestQueue(this);
VolleyImageCache lruImageCache = VolleyImageCache.genInstance();
mImageLoader = new ImageLoader(mQueue,lruImageCache);
}
- VolleyImageCache
public class VolleyImageCache implements ImageLoader.ImageCache {
private static LruCache<String, Bitmap> mMemoryCache;
private static VolleyImageCache mVolleyImageCache = new VolleyImageCache();
private VolleyImageCache() {
// Get the Max available memory
int maxMemory = (int) Runtime.getRuntime().maxMemory();
int cacheSize = maxMemory / 8;
mMemoryCache = new LruCache<String, Bitmap>(cacheSize) {
@Override
protected int sizeOf(String key, Bitmap bitmap) {
return bitmap.getRowBytes() * bitmap.getHeight();
}
};
}
public static VolleyImageCache genInstance() {
return mVolleyImageCache;
}
@Override
public Bitmap getBitmap(String arg0) {
return mMemoryCache.get(arg0);
}
@Override
public void putBitmap(String arg0, Bitmap arg1) {
if (getBitmap(arg0) == null) {
mMemoryCache.put(arg0, arg1);
}
}
}
使用Volley 上传文件
private MultipartEntity mReqEntity = new MutipartEntity();
@Override
public String getBodyContentType() {
return mReqEntity.getContentType().getValue();
}
@Override
public byte[] getBody() throws AuthFailureError {
if (!CollectionUtils.isCollectionEmpty(mFilePath)) {
// upload files
ByteArrayOutputStream bos = new ByteArrayOutputStream();
try {
mReqEntity.writeTo(bos);
} catch (IOException e) {
VolleyLog.e("IOException writing to ByteArrayOutputStream");
}
return bos.toByteArray();
}
return super.getBody();
}
private void buildMultipartEntity() {
try {
for (String filepath : mFilePath) {
File file = new File(filepath);
FileBody filePart = new FileBody(file);
mReqEntity.addPart(file.getName(), filePart);
}
if (mParams != null && !mParams.isEmpty()) {
Iterator<String> keySet = mParams.keySet().iterator();
while (keySet.hasNext()) {
String key = keySet.next();
String ss = JSON.toJSONString(mParams.get(key));
mReqEntity.addPart(key, new StringBody(ss));
}
}
} catch (UnsupportedEncodingException e) {
// empty here
}
}