1. 什麼是volley
在這之前,我們在程式中需要和網絡通信的時候,大體使用的東西莫過于asynctaskloader,httpurlconnection,asynctask,httpclient(apache)等,今年的google i/o 2013上,volley釋出了。volley是android平台上的網絡通信庫,能使網絡通信更快,更簡單,更健壯。
這是volley名稱的由來: a burst or emission of many things or a large amount at once
在google io的演講上,其配圖是一幅發射火弓箭的圖,有點類似流星。見下圖
其實,從這幅圖,我們也可以看出來,volley特别适合資料量不大但是通信頻繁的場景。
1.1. volley引入的背景
在以前,我們可能面臨如下很多麻煩的問題。
比如以前從網上下載下傳圖檔的步驟可能是這樣的流程:
在listadapter#getview()裡開始圖像的讀取。
通過asynctask等機制使用httpurlconnection從伺服器去的圖檔資源
在asynctask#onpostexecute()裡設定相應imageview的屬性。
而在volley下,隻需要一個函數即可,詳細見後面的例子。
再比如,螢幕旋轉的時候,有時候會導緻再次從網絡取得資料。為了避免這種不必要的網絡通路,我們可能需要自己寫很多針對各種情況的處理,比如cache什麼的。
再有,比如listview的時候,我們滾動過快,可能導緻有些網絡請求傳回的時候,早已經滾過了當時的位置,根本沒必要顯示在list裡了,雖然我們可以通過viewholder來保持url等來實作防止兩次取得,但是那些已經沒有必須要的資料,還是會浪費系統的各種資源。
1.2. volley提供的功能
簡單來說,它提供了如下的便利功能:
json,圖像等的異步下載下傳;
網絡請求的排序(scheduling)
網絡請求的優先級處理
緩存
多級别取消請求
和activity和生命周期的關聯(activity結束時同時取消所有網絡請求)
2. 使用前的準備
引入volley非常簡單,首先,從git庫先克隆一個下來:
git clone https://android.googlesource.com/platform/frameworks/volley
然後編譯為jar包,再在自己的工程裡import進來。
注意,這個庫要求最低sdk版本為froyo,即至少要設定android:minsdkversion為8以上。
3.使用例子
下面簡單看看如何使用volley
3.1. 最簡單的get請求
這個例子很簡單,從網絡取得json對象,然後列印出來。
mqueue = volley.newrequestqueue(getapplicationcontext());
mqueue.add(new jsonobjectrequest(method.get, url, null,
new listener() {
@override
public void onresponse(jsonobject response) {
log.d(tag, "response : " + response.tostring());
}
}, null));
mqueue.start();
3.2. 給imageview設定圖檔源
// imageview是一個imageview執行個體
// imageloader.getimagelistener的第二個參數是預設的圖檔resource id
// 第三個參數是請求失敗時候的資源id,可以指定為0
imagelistener listener = imageloader.getimagelistener(imageview, android.r.drawable.ic_menu_rotate, android.r.drawable.ic_delete);
mimageloader.get(url, listener);
imageloader的方法都需要從主線程裡來調用。
3.3. 使用networkimageview
volley提供了一個新的控件networkimageview來代替傳統的imageview,這個控件的圖檔屬性可以通過
mimageview.setimageurl(url, imageloader)
來設定。而且,這個控件在被從父控件detach的時候,會自動取消網絡請求的,即完全不用我們擔心相關網絡請求的生命周期問題。
示例代碼如下:
mimageloader = new imageloader(mrequestqueue, new bitmaplrucache());
... ...
if(holder.imagerequest != null) {
holder.imagerequest.cancel();
}
holder.imagerequest = mimageloader.get(base_ur + item.image_url, holder.imageview, r.drawable.loading, r.drawable.error);
注意,這裡使用的不是imageview控件,而是volley新提供的com.android.volley.networkimageview。
另外,注意這裡:
imageloader構造函數的第二個參數是一個imagecache的執行個體(嚴格來說,是實作imagecache接口的某具體類的執行個體)
imagecache的定義如下(在imageloader.java裡):
/**
* simple cache adapter interface. if provided to the imageloader, it
* will be used as an l1 cache before dispatch to volley. implementations
* must not block. implementation with an lrucache is recommended.
*/
public interface imagecache {
public bitmap getbitmap(string url);
public void putbitmap(string url, bitmap bitmap);
下面的網址一個lru的cache實作例子,請參考:
<a target="_blank" href="https://github.com/suwa-yuki/volleysample/blob/master/src/jp/classmethod/android/sample/volley/bitmapcache.java">https://github.com/suwa-yuki/volleysample/blob/master/src/jp/classmethod/android/sample/volley/bitmapcache.java</a>
3.5. 使用自己定制的request
我們也可以通過繼承request根據自己的需求來定制自己的request
@override
protected response parsenetworkresponse(networkresponse response) {
try {
string json = new string(
response.data, httpheaderparser.parsecharset(response.headers));
return response.success(
gson.fromjson(json, clazz), httpheaderparser.parsecacheheaders(response));
} catch (unsupportedencodingexception e) {
return response.error(new parseerror(e));
} catch (jsonsyntaxexception e) {
}
裡面使用的gson(com.google.gson.gson)是json的序列化和反序列化的庫,可以在json和java model object之間進行轉換。
以下是使用自定制request的例子:
mrequestqueue.add( new gsonrequest(url, listresponse.class, null,
new listener() {
public void onresponse(listresponse response) {
appenditemstolist(response.item);
notifydatasetchanged();
}
4. volley的架構設計
volley使用了線程池來作為基礎結構,主要分為主線程,cache線程和network線程。
主線程和cache線程都隻有一個,而networkdispatcher線程可以有多個,這樣能解決比并行問題。如下圖:
如果在一個activity裡面啟動了網絡請求,而在這個網絡請求還沒傳回結果的時候,如果activity被結束了,則我們需要寫如下代碼作為防守:
@override public void onpostexecute(result r) {
if (getactivity() == null) {
return;
// ...
activity被終止之後,如果繼續使用其中的context等,除了無辜的浪費cpu,電池,網絡等資源,有可能還會導緻程式crash,是以,我們需要處理這種一場情況。
使用volley的話,我們可以在activity停止的時候,同時取消所有或部分未完成的網絡請求。
volley裡所有的請求結果會傳回給主程序,如果在主程序裡取消了某些請求,則這些請求将不會被傳回給主線程。
比如,可以針對某些個request做取消操作:
public void onstop() {
for (request <?> req : minflightrequests) {
req.cancel();
...
或者,取消這個隊列裡的所有請求:
@override pubic void onstop() {
mrequestqueue.cancelall(this);
也可以根據requestfilter或者tag來終止某些請求:
@override public void onstop() {
mrequestqueue.cancelall( new requestfilter() {})
// or
mrequestqueue.cancelall(new object());
5.總結
從演講的例子來看,volley應該是簡化了網絡通信的一些開發,特别是針對如下兩種情況:
json對象
圖檔加載
但是這個東西也有不實用的地方,比如大資料(large payloads ),流媒體,這些case,還需要使用原始的方法,比如download manager等。
總之,如果你要編寫網絡程式,是不是可以考慮開始使用volley呢?