Android 網絡通訊框架Volley基本介紹

 Volley主頁 https://android.googlesource.com/platform/frameworks/volley
html

 http://www.youtube.com/watch?v=yhv8l9F44qo&feature=player_embedded
java

1. 什麼是Volley

Google I/O 2013上。Volley公佈了volley。在這以前,咱們在程序中需要和網絡通訊的時候,大致使用的東西莫過於android

  • AsyncTaskLoader
  • HttpURLConnection
  • AsyncTask
  • HTTPClient(Apache)等

Volley是Android平臺上的網絡通訊庫。能使網絡通訊更快,更簡單,更健壯。git

Volley名稱的由來: a burst or emission of many things or a large amount at oncegithub

在Google IO的演講上。其配圖是一幅發射火弓箭的圖。有點相似流星。見下圖

事實上。從這幅圖,咱們也可以看出來,Volley特別適合數據量不大但是通訊頻繁的場景。volley適合高速,簡單的請求(Json對象,圖片載入)。json

Volley引入的背景

在曾經,咱們可能面臨例如如下很是多麻煩的問題。緩存

比方曾經從網上下載圖片的步驟多是這種流程:網絡

  • 在ListAdapter#getView()裏開始圖像的讀取。
  • 經過AsyncTask等機制使用HttpURLConnection從server去的圖片資源
  • 在AsyncTask#onPostExecute()裏設置對應ImageView的屬性。

而在Volley下,僅僅需要一個函數就能夠,具體見後面的樣例。架構

再比方,屏幕旋轉的時候,有時候會致使再次從網絡取得數據。爲了不這樣的沒必要要的網絡訪問,咱們可能需要本身寫很是多針對各類狀況的處理。比方cache什麼的。app

再有。比方ListView的時候,咱們滾動過快,可能致使有些網絡請求返回的時候,早已經滾過了當時的位置,根本不是必需顯示在list裏了,儘管咱們可以經過ViewHolder來保持url等來實現防止兩次取得,但是那些已經沒有必須要的數據。仍是會浪費系統的各類資源。

Volley提供的功能

簡單來講,它提供了例如如下的便利功能:

  • JSON,圖像等的異步下載;
  • 網絡請求的排序(scheduling)
  • 網絡請求的優先級處理
  • 緩存
  • 多級別取消請求
  • 和Activity和生命週期的聯動(Activity結束時同一時候取消所有網絡請求)

使用前的準備

引入Volley很easy,首先,從git庫先克隆一個下來:

git clone https://android.googlesource.com/platform/frameworks/volley 
view

而後編譯爲jar包,再在本身的project裏import進來。

注意。這個庫要求最低SDK版本號爲Froyo,即至少要設置android:minSdkVersion爲8以上。

使用樣例

最簡單的get請求

這個樣例很是easy,從網絡取得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的方法都需要從主線程裏來調用。

使用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。

另外,注意這裏:

mImageLoader = new ImageLoader(mRequestQueue, new BitmapLruCache());  <span style="color:#000000;"><a target=_blank target="_blank" href="http://blog.csdn.net/t12x3456/article/details/9221611#" class="ViewSource" title="view plain" style="text-decoration: none; background-color: inherit; border: medium none; padding: 1px; margin: 0px 10px 0px 0px; font-size: 9px; display: inline-block; width: 16px; height: 16px; text-indent: -2000px;">ew plain</a></span>


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實現樣例。請參考:

https://github.com/suwa-yuki/VolleySample/blob/master/src/jp/classmethod/android/sample/volley/BitmapCache.java

使用本身定製的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) {  
            return Response.error(new ParseError(e));  
        }  
    }  

這段代碼節選自:  https://gist.github.com/ficusk/5474673

裏面使用的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();  
            }  
        }  
    }  

Volley的架構設計

Volley使用了線程池來做爲基礎結構,主要分爲主線程。cache線程和network線程。

主線程和cache線程都僅僅有一個。而NetworkDispatcher線程可以有多個。這樣能解決比並行問題。

例如如下圖:


其中藍色部分表明主線程,綠色部分表明緩存線程,橙色部分表明網絡線程。咱們在主線程中調用RequestQueue的add()方法來增長一條網絡請求。這條請求會先被增長到緩存隊列其中,假設發現可以找到對應的緩存結果就直接讀取緩存並解析。而後回調給主線程。假設在緩存中沒有找到結果,則將這條請求增長到網絡請求隊列中,而後處理髮送HTTP請求,解析響應結果,寫入緩存,並回調主線程。

假設在一個Activity裏面啓動了網絡請求,而在這個網絡請求還沒返回結果的時候。假設Activity被結束了。則咱們需要寫例如如下代碼做爲防守:

@Override public void onPostExecute(Result r) {  
    if (getActivity() == null) {  
        return;  
    }  
    // ...  
} 

Activity被終止以後,假設繼續使用當中的Context等,除了無辜的浪費CPU,電池,網絡等資源。有可能還會致使程序crash,因此,咱們需要處理這樣的一場狀況。

使用Volley的話,咱們可以在Activity中止的時候,同一時候取消所有或部分未完畢的網絡請求。

Volley裏所有的請求結果會返回給主進程。假設在主進程裏取消了某些請求,則這些請求將不會被返回給主線程。


比方。可以針對某些個request作取消操做y

    @Override  
    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());  
        ...  


總結

從演講的樣例來看。Volley應該是簡化了網絡通訊的一些開發,特別是針對例如如下兩種狀況:

  • JSON對象
  • 圖片載入

但是這個東西也有不有用的地方,比方大數據(large payloads ),流媒體,這些case,還需要使用原始的方法,比方Download Manager等。


案例分析

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

首先拿到一個請求隊列

(RequestQueue僅僅需要一個實例就能夠,不像AsyncTask每次使用都要new一個)

    // 初始化RequestQueue一個activity僅僅需要一個  
        private void initRequestQueue() {  
            mQueue = Volley.newRequestQueue(getApplicationContext());  
        }  


實現volley的異步請求類

(JsonObjectRequest,JsonArrayRequest,StringRequest,ImageRequest)

由於使用方法都相差不大。我就不一一舉例了。舉幾個常用有表明性的樣例:

StringRequest的get請求
    private void loadGetStr(String url) {  
      
        StringRequest srReq = new StringRequest(Request.Method.GET, url,  
                new StrListener(), new StrErrListener()) {  
      
            protected final String TYPE_UTF8_CHARSET = "charset=UTF-8";  
      
            // 重寫parseNetworkResponse方法改變返回頭參數解決亂碼問題  
            // 主要是看server編碼,假設server編碼不是UTF-8的話那麼就需要本身轉換,反之則不需要  
            @Override  
            protected Response<String> parseNetworkResponse(  
                    NetworkResponse response) {  
                try {  
                    String type = response.headers.get(HTTP.CONTENT_TYPE);  
                    if (type == null) {  
                        type = TYPE_UTF8_CHARSET;  
                        response.headers.put(HTTP.CONTENT_TYPE, type);  
                    } else if (!type.contains("UTF-8")) {  
                        type += ";" + TYPE_UTF8_CHARSET;  
                        response.headers.put(HTTP.CONTENT_TYPE, type);  
                    }  
                } catch (Exception e) {  
                }  
                return super.parseNetworkResponse(response);  
            }  
        };  
        srReq.setShouldCache(true); // 控制是否緩存  
        startVolley(srReq);  
    }  


JsonObjectRequest的post請求:

    // post請求  
    private void loadPostJson(String url) {  
        // 第二個參數說明:  
        // Constructor which defaults to GET if jsonRequest is null, POST  
        // otherwise.  
        // 默認狀況下設成null爲get方法,不然爲post方法。

JsonObjectRequest srReq = new JsonObjectRequest(url, null, new JsonListener(), new StrErrListener()) { @Override protected Map<String, String> getParams() throws AuthFailureError { Map<String, String> map = new HashMap<String, String>(); map.put("w", "2459115"); map.put("u", "f"); return map; } }; srReq.setShouldCache(false); // 控制是否緩存 startVolley(srReq); }



你們注意看的話,不論是 JsonObjectReques的postt仍是StringRequest的get都需要傳入兩個監聽函數一個是成功一個是失敗。成功監聽他們會返回對應類型的數據:
    // Str請求成功回調  
    private class StrListener implements Listener<String> {  
      
        @Override  
        public void onResponse(String arg0) {  
            Log.e(Tag, arg0);  
      
        }  
      
    }  
      
    // Gson請求成功回調  
    private class GsonListener implements Listener<ErrorRsp> {  
      
        @Override  
        public void onResponse(ErrorRsp arg0) {  
            Toast.makeText(mContext, arg0.toString(), Toast.LENGTH_LONG).show();  
        }  
      
    }  
    // 共用失敗回調  
    private class StrErrListener implements ErrorListener {  
      
        @Override  
        public void onErrorResponse(VolleyError arg0) {  
            Toast.makeText(mContext,  
                    VolleyErrorHelper.getMessage(arg0, mContext),  
                    Toast.LENGTH_LONG).show();  
        }  
      
    }  

ImageRequest
    /** 
     * 第三第四個參數分別用於指定贊成圖片最大的寬度和高度。假設指定的網絡圖片的寬度或高度大於這裏的最大值。則會對圖片進行壓縮, 
     * 指定成0的話就表示不管圖片有多大。都不會進行壓縮。 
     *  
     * @param url 
     *            圖片地址 
     * @param listener 
     * @param maxWidth 
     *            指定贊成圖片最大的寬度 
     * @param maxHeight 
     *            指定贊成圖片最大的高度 
     * @param decodeConfig 
     *            指定圖片的顏色屬性。Bitmap.Config下的幾個常量. 
     * @param errorListener 
     */  
    private void getImageRequest(final ImageView iv, String url) {  
        ImageRequest imReq = new ImageRequest(url, new Listener<Bitmap>() {  
      
            @Override  
            public void onResponse(Bitmap arg0) {  
                iv.setImageBitmap(arg0);  
            }  
        }, 60, 60, Bitmap.Config.ARGB_8888, new StrErrListener());  
        startVolley(imReq);  
    }  

看到現在你們確定會疑惑寫了這麼多不一樣類型的Request究竟怎樣執行?接下請看:
    // 加入及開始請求  
    private void startVolley(Request req) {  
      
        // 設置超時時間  
        // req.setRetryPolicy(new DefaultRetryPolicy(20 * 1000, 1, 1.0f));  
        // 將請求加入隊列  
        mQueue.add(req);  
        // 開始發起請求  
        mQueue.start();  
    }  


volley不只提供了這些請求的方式。還提供了載入圖片的一些方法和控件:

比方咱們一個列表需要載入很是多圖片咱們可以使用volley給咱們提供的ImageLoader( ImageLoader比ImageRequest更加高效,因爲它不只對圖片進行緩存。還可以過濾掉反覆的連接。避免反覆發送請求。

    public class ImageAdapter extends ArrayAdapter<String> {  
          
        private RequestQueue mQueue;  
        private ImageLoader mImageLoader;  
      
        public ImageAdapter(Context context, List<String> objects) {  
            super(context, 0, objects);  
            mQueue = Volley.newRequestQueue(getContext());  
            mImageLoader = new ImageLoader(mQueue, new BitmapCache());  
        }  
          
        @Override  
        public View getView(int position, View convertView, ViewGroup parent) {  
            String url = getItem(position);  
            ImageView imageView;  
            if (convertView == null) {  
                imageView = new ImageView(getContext());  
            } else {  
                imageView = (ImageView) convertView;  
            }  
            // getImageListener(imageView控件對象。默認圖片地址。失敗圖片地址);  
            ImageListener listener = ImageLoader.getImageListener(imageView, android.R.drawable.ic_menu_rotate, android.R.drawable.ic_delete);  
            // get(圖片地址。listener。寬,高);本身主動幫你處理圖片的寬高不再怕大圖片的oom了  
            mImageLoader.get(url, listener,100,200);  
            return imageView;  
        }  
      
    }  


固然還需要重寫ImageCache這個類 //使用LruCache不再用怕載入多張圖片oom了

    public class <span style="font-family: Arial;">BitmapCache</span><span style="font-family: Arial;"> extends LruCache<String, Bitmap> implements ImageCache {</span>  
        // LruCache 原理:Cache保存一個強引用來限制內容數量,每當Item被訪問的時候,此Item就會移動到隊列的頭部。 當cache已滿的時候增長新的item時,在隊列尾部的item會被回收。  
        // 解釋:當超出指定內存值則移除近期最少用的圖片內存  
        public static int getDefaultLruCacheSize() {  
            // 拿到最大內存  
            final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);  
            // 拿到內存的八分之中的一個來作圖片內存緩存  
            final int cacheSize = maxMemory / 8;  
      
            return cacheSize;  
        }  
      
        public BitmapLruCache() {  
            this(getDefaultLruCacheSize());  
        }  
      
        public BitmapLruCache(int sizeInKiloBytes) {  
            super(sizeInKiloBytes);  
        }  
      
        @Override  
        protected int sizeOf(String key, Bitmap value) {  
            return value.getRowBytes() * value.getHeight() / 1024;  
        }  
      
        @Override  
        public Bitmap getBitmap(String url) {  
            return get(url);  
        }  
      
        @Override  
        public void putBitmap(String url, Bitmap bitmap) {  
            put(url, bitmap);  
        }  
    }  

Volley還提供的載入圖片的控件 com.android.volley.NetworkImageView。(這個控件在被從父控件detach的時候。會本身主動取消網絡請求的,即全然不用咱們操心相關網絡請求的生命週期問題。而且NetworkImageView還會依據你對圖片設置的width和heigh本身主動壓縮該圖片不會產生多的內存,還有NetworkImageView在列表中使用不會圖片錯誤)
<com.android.volley.toolbox.NetworkImageView  
    android:id="@+id/network_image_view"  
    android:layout_width="100dp"  
    android:layout_height="100dp" /> 

用法:
    private void networkImageViewUse(NetworkImageView iv, String url) {  
            ImageLoader imLoader = new ImageLoader(mQueue, new BitmapLruCache());  
            iv.setDefaultImageResId(R.drawable.ic_launcher);  
            iv.setErrorImageResId(R.drawable.ic_launcher);  
            iv.setImageUrl(url, imLoader);  
        }  



咱們說了這麼多都是請求,那麼怎樣取消請求呢?

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

2.給請求設置標籤:

    request.setTag("My Tag");  
 

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

    request.cancelAll("My Tag");    
相關文章
相關標籤/搜索