1. 程式人生 > >Android網路框架Volley

Android網路框架Volley

                Volley是Google I/O 2013推出的網路通訊庫,在volley推出之前我們一般會選擇比較成熟的第三方網路通訊庫,如:

他們各有優劣,之前個人則比較喜歡用android-async-http, 如今Google推出了官方的針對Android平臺上的網路通訊庫,能使網路通訊更快,更簡單,更健壯,Volley在提供了高效能網路通訊功能的同時,對網路圖片載入也提供了良好的支援,完全可以滿足簡單REST客戶端的需求, 我們沒有理由不跟上時代的潮流

使用Volley

下載Volley原始碼並build jar包。

$ git clone https://android.googlesource.com/platform/frameworks/volley$ cd volley$ android update project -p$ ant jar

然後把生成的jar包引用到我們的專案中,extras目錄下則包含了目前最新的volley原始碼。

說明

此Demo主要介紹了日常網路開發常用的基本功能,但volley的擴充套件性很強,可以根據需要定製你自己的網路請求。

以上是在Google IO的演講上ppt的配圖,從上面這張圖我們可以看出,volley適合快速,簡單的請求(Json物件,圖片載入)。

volley的特性:

  • JSON,影象等的非同步下載;
  • 網路請求的排序(scheduling)
  • 網路請求的優先順序處理
  • 快取
  • 多級別取消請求
  • 和Activity和生命週期的聯動(Activity結束時同時取消所有網路請求)

接下來,我們來學習簡單的使用下volley給我提供的API吧。

1.首先拿到一個請求佇列(RequestQueue只需要一個例項即可,不像AsyncTask每次使用都要new一個)

[java] view plaincopyprint?
  1. // 初始化RequestQueue一個activity只需要一個
  2.     privatevoid initRequestQueue() {  
  3.         mQueue = Volley.newRequestQueue(getApplicationContext());  
  4.     }  
2.實現volley的非同步請求類(JsonObjectRequest,JsonArrayRequest,StringRequest,ImageRequest

由於用法都相差不大,我就不一一舉例了,舉幾個常用有代表性的例子:

以下程式碼是StringRequest的get請求:

[java] view plaincopyprint?
  1. // get請求
[java] view plaincopyprint?
  1. privatevoid loadGetStr(String url) {  
  2.     StringRequest srReq = new StringRequest(Request.Method.GET, url,  
  3.             new StrListener(), new StrErrListener()) {  
  4.         protectedfinal String TYPE_UTF8_CHARSET = "charset=UTF-8";  
  5.         // 重寫parseNetworkResponse方法改變返回頭引數解決亂碼問題
  6.         // 主要是看伺服器編碼,如果伺服器編碼不是UTF-8的話那麼就需要自己轉換,反之則不需要
  7.         @Override
  8.         protected Response<String> parseNetworkResponse(  
  9.                 NetworkResponse response) {  
  10.             try {  
  11.                 String type = response.headers.get(HTTP.CONTENT_TYPE);  
  12.                 if (type == null) {  
  13.                     type = TYPE_UTF8_CHARSET;  
  14.                     response.headers.put(HTTP.CONTENT_TYPE, type);  
  15.                 } elseif (!type.contains("UTF-8")) {  
  16.                     type += ";" + TYPE_UTF8_CHARSET;  
  17.                     response.headers.put(HTTP.CONTENT_TYPE, type);  
  18.                 }  
  19.             } catch (Exception e) {  
  20.             }  
  21.             returnsuper.parseNetworkResponse(response);  
  22.         }  
  23.     };  
  24.     srReq.setShouldCache(true); // 控制是否快取
  25.     startVolley(srReq);  
  26. }  
以下程式碼是JsonObjectRequest的post請求:[java] view plaincopyprint?
  1. // post請求
  2. privatevoid loadPostJson(String url) {  
  3.     // 第二個引數說明:
  4.     // Constructor which defaults to GET if jsonRequest is null, POST
  5.     // otherwise.
  6.     // 預設情況下設成null為get方法,否則為post方法。
  7.     JsonObjectRequest srReq = new JsonObjectRequest(url, null,  
  8.             new JsonListener(), new StrErrListener()) {  
  9.         @Override
  10.         protected Map<String, String> getParams() throws AuthFailureError {  
  11.             Map<String, String> map = new HashMap<String, String>();  
  12.             map.put("w""2459115");  
  13.             map.put("u""f");  
  14.             return map;  
  15.         }  
  16.     };  
  17.     srReq.setShouldCache(false); // 控制是否快取
  18.     startVolley(srReq);  
  19. }  
大家注意看的話,無論是JsonObjectReques的postt還是StringRequest的get都需要傳入兩個監聽函式一個是成功一個是失敗,成功監聽他們會返回相應型別的資料:[java] view plaincopyprint?
  1. // Str請求成功回撥
  2. privateclass StrListener implements Listener<String> {  
  3.     @Override
  4.     publicvoid onResponse(String arg0) {  
  5.         Log.e(Tag, arg0);  
  6.     }  
  7. }  
  8. // Gson請求成功回撥
  9. privateclass GsonListener implements Listener<ErrorRsp> {  
  10.     @Override
  11.     publicvoid onResponse(ErrorRsp arg0) {  
  12.         Toast.makeText(mContext, arg0.toString(), Toast.LENGTH_LONG).show();  
  13.     }  
  14. }  
  15. // 共用失敗回撥
  16. privateclass StrErrListener implements ErrorListener {  
  17.     @Override
  18.     publicvoid onErrorResponse(VolleyError arg0) {  
  19.         Toast.makeText(mContext,  
  20.                 VolleyErrorHelper.getMessage(arg0, mContext),  
  21.                 Toast.LENGTH_LONG).show();  
  22.     }  
  23. }  
接下來是ImageRequest[java] view plaincopyprint?
  1. /** 
  2.  * 第三第四個引數分別用於指定允許圖片最大的寬度和高度,如果指定的網路圖片的寬度或高度大於這裡的最大值,則會對圖片進行壓縮, 
  3.  * 指定成0的話就表示不管圖片有多大,都不會進行壓縮。 
  4.  *  
  5.  * @param url 
  6.  *            圖片地址 
  7.  * @param listener 
  8.  * @param maxWidth 
  9.  *            指定允許圖片最大的寬度 
  10.  * @param maxHeight 
  11.  *            指定允許圖片最大的高度 
  12.  * @param decodeConfig 
  13.  *            指定圖片的顏色屬性,Bitmap.Config下的幾個常量. 
  14.  * @param errorListener 
  15.  */
  16. privatevoid getImageRequest(final ImageView iv, String url) {  
  17.     ImageRequest imReq = new ImageRequest(url, new Listener<Bitmap>() {  
  18.         @Override
  19.         publicvoid onResponse(Bitmap arg0) {  
  20.             iv.setImageBitmap(arg0);  
  21.         }  
  22.     }, 6060, Bitmap.Config.ARGB_8888, new StrErrListener());  
  23.     startVolley(imReq);  
  24. }  
看到現在大家肯定會疑惑寫了這麼多不同型別的Request到底如何執行?接下請看:[java] view plaincopyprint?
  1. // 新增及開始請求
  2. privatevoid startVolley(Request req) {  
  3.     // 設定超時時間
  4.     // req.setRetryPolicy(new DefaultRetryPolicy(20 * 1000, 1, 1.0f));
  5.     // 將請求加入佇列
  6.     mQueue.add(req);  
  7.     // 開始發起請求
  8.     mQueue.start();  
  9. }  

volley不僅提供了這些請求的方式,還提供了載入圖片的一些方法和控制元件:

比如我們一個列表需要載入很多圖片我們可以使用volley給我們提供的ImageLoader( ImageLoader比ImageRequest更加高效,因為它不僅對圖片進行快取,還可以過濾掉重複的連結,避免重複傳送請求。

[java] view plaincopyprint?
  1. publicclass ImageAdapter extends ArrayAdapter<String> {  
  2.     private RequestQueue mQueue;  
  3.     private ImageLoader mImageLoader;  
  4.     public ImageAdapter(Context context, List<String> objects) {  
  5.         super(context, 0, objects);  
  6.         mQueue = Volley.newRequestQueue(getContext());  
  7.         mImageLoader = new ImageLoader(mQueue, new BitmapCache());  
  8.     }  
  9.     @Override
  10.     public View getView(int position, View convertView, ViewGroup parent) {  
  11.         String url = getItem(position);  
  12.         ImageView imageView;  
  13.         if (convertView == null) {  
  14.             imageView = new ImageView(getContext());  
  15.         } else {  
  16.             imageView = (ImageView) convertView;  
  17.         }  
  18.         // getImageListener(imageView控制元件物件,預設圖片地址,失敗圖片地址);
  19.         ImageListener listener = ImageLoader.getImageListener(imageView, android.R.drawable.ic_menu_rotate, android.R.drawable.ic_delete);  
  20.         // get(圖片地址,listener,寬,高);自動幫你處理圖片的寬高再也不怕大圖片的oom了
  21.         mImageLoader.get(url, listener,100,200);  
  22.         return imageView;  
  23.     }  
  24. }  
當然還需要重寫ImageCache這個類 //使用LruCache再也不用怕載入多張圖片oom了[java] view plaincopyprint?
  1. publicclass <span style="font-family: Arial;">BitmapCache</span><span style="font-family: Arial;"extends LruCache<String, Bitmap> implements ImageCache {</span>  
  2.     // LruCache 原理:Cache儲存一個強引用來限制內容數量,每當Item被訪問的時候,此Item就會移動到佇列的頭部。 當cache已滿的時候加入新的item時,在佇列尾部的item會被回收。
  3.     // 解釋:當超出指定記憶體值則移除最近最少用的圖片記憶體
  4.     publicstaticint getDefaultLruCacheSize() {  
  5.         // 拿到最大記憶體
  6.         finalint maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);  
  7.         // 拿到記憶體的八分之一來做圖片記憶體快取
  8.         finalint cacheSize = maxMemory / 8;  
  9.         return cacheSize;  
  10.     }  
  11.     public BitmapLruCache() {  
  12.         this(getDefaultLruCacheSize());  
  13.     }  
  14.     public BitmapLruCache(int sizeInKiloBytes) {  
  15.         super(sizeInKiloBytes);  
  16.     }  
  17.     @Override
  18.     protectedint sizeOf(String key, Bitmap value) {  
  19.         return value.getRowBytes() * value.getHeight() / 1024;  
  20.     }  
  21.     @Override
  22.     public Bitmap getBitmap(String url) {  
  23.         return get(url);  
  24.     }  
  25.     @Override
  26.     publicvoid putBitmap(String url, Bitmap bitmap) {  
  27.         put(url, bitmap);  
  28.     }  
  29. }  
Volley還提供的載入圖片的控制元件com.android.volley.NetworkImageView。(這個控制元件在被從父控制元件detach的時候,會自動取消網路請求的,即完全不用我們擔心相關網路請求的生命週期問題,而且NetworkImageView還會根據你對圖片設定的width和heigh自動壓縮該圖片不會產生多的記憶體,還有NetworkImageView在列表中使用不會圖片錯誤[html] view plaincopyprint?
  1. <com.android.volley.toolbox.NetworkImageView
  2.     android:id="@+id/network_image_view"
  3.     android:layout_width="100dp"
  4.     android:layout_height="100dp"/>
使用方法:[java] view plaincopyprint?
  1. privatevoid networkImageViewUse(NetworkImageView iv, String url) {  
  2.         ImageLoader imLoader = new ImageLoader(mQueue, new BitmapLruCache());  
  3.         iv.setDefaultImageResId(R.drawable.ic_launcher);  
  4.         iv.setErrorImageResId(R.drawable.ic_launcher);  
  5.         iv.setImageUrl(url, imLoader);  
  6.     }  
我們說了這麼多都是請求,那麼如何取消請求呢?

1.activity自動銷燬時它會自定取消所有請求。

2.給請求設定標籤:

[java] view plaincopyprint?
  1. request.setTag("My Tag");    

取消所有指定標記的請求:

[java] view plaincopyprint?
  1. request.cancelAll("My Tag");    
Volley的架構設計:

其中藍色部分代表主執行緒,綠色部分代表快取執行緒,橙色部分代表網路執行緒。我們在主執行緒中呼叫RequestQueue的add()方法來新增一條網路請求,這條請求會先被加入到快取隊列當中,如果發現可以找到相應的快取結果就直接讀取快取並解析,然後回撥給主執行緒。如果在快取中沒有找到結果,則將這條請求加入到網路請求佇列中,然後處理髮送HTTP請求,解析響應結果,寫入快取,並回調主執行緒。

接下來我們要看看如何把volley使用到實戰專案裡面,我們先考慮下一些問題:

從上一篇來看 mQueue 只需要一個物件即可,new RequestQueue物件對資源一種浪費,我們應該在application,以及可以把取消請求的方法也在application進行統一管理,看以下程式碼:

[java] view plaincopyprint?
  1. package com.chronocloud.lib.base;  
  2. import android.app.Application;  
  3. import android.text.TextUtils;  
  4. import com.android.volley.Request;  
  5. import com.android.volley.RequestQueue;  
  6. import com.android.volley.VolleyLog;  
  7. import com.android.volley.toolbox.Volley;  
  8. publicclass ApplicationController extends Application {  
  9.     /** 
  10.      * Log or request TAG 
  11.      */
  12.     publicstaticfinal String TAG = "VolleyPatterns";  
  13.     /** 
  14.      * Global request queue for Volley 
  15.      */
  16.     private RequestQueue mRequestQueue;  
  17.     /** 
  18.      * A singleton instance of the application class for easy access in other 
  19.      * places 
  20.      */
  21.     privatestatic ApplicationController sInstance;  
  22.     @Override
  23.     publicvoid onCreate() {  
  24.         super.onCreate();  
  25.         // initialize the singleton
  26.         sInstance = this;  
  27.     }  
  28.     /** 
  29.      * @return ApplicationController singleton instance 
  30.      */
  31.     publicstaticsynchronized ApplicationController getInstance() {  
  32.         return sInstance;  
  33.     }  
  34.     /** 
  35.      * @return The Volley Request queue, the queue will be created if it is null 
  36.      */
  37.     public RequestQueue getRequestQueue() {  
  38.         // lazy initialize the request queue, the queue instance will be
  39.         // created when it is accessed for the first time
  40.         if (mRequestQueue == null) {  
  41.             // 1
  42.             // 2
  43.             synchronized (ApplicationController.class) {  
  44.                 if (mRequestQueue == null) {  
  45.                     mRequestQueue = Volley  
  46.                             .newRequestQueue(getApplicationContext());  
  47.                 }  
  48.             }  
  49.         }  
  50.         return mRequestQueue;  
  51.     }  
  52.     /** 
  53.      * Adds the specified request to the global queue, if tag is specified then 
  54.      * it is used else Default TAG is used. 
  55.      *  
  56.      * @param req 
  57.      * @param tag 
  58.      */
  59.     public <T> void addToRequestQueue(Request<T> req, String tag) {  
  60.         // set the default tag if tag is empty
  61.         req.setTag(TextUtils.isEmpty(tag) ? TAG : tag);  
  62.         VolleyLog.d("Adding request to queue: %s", req.getUrl());  
  63.         getRequestQueue().add(req);  
  64.     }  
  65.     /** 
  66.      * Adds the specified request to the global queue using the Default TAG. 
  67.      *  
  68.      * @param req 
  69.      * @param tag 
  70.      */
  71.     public <T> void addToRequestQueue(Request<T> req) {