什麼是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
}
}