流行網絡庫第(一)篇---Volley用法解析

Volley是2013年Google I/O大會上推出了一個新的網絡通信框架,特麼今年都2016年年尾了,這兩年新技術出來的真是多的花了眼睛。雖然是三年前的開原庫,但是並不妨礙我們研究它。這一篇先了解其基本用法,下一篇是源碼解析,以後的網絡庫方面的分析,一般也是以這個套路來。


這是一幅經典的圖,Volley的中文翻譯爲“齊射、併發”,這表示,Volley特別適合數據量不大但是通信頻繁的場景,不支持 post 大數據,不適合上傳文件。本文主要介紹一下Volley的常規用法,就是五個類,StringRequest, JsonRequest ,ImageRequest, ImageLoader, NetWorkImageView

準備工作:
所需要添加的引用庫

compile 'com.mcxiaoke.volley:library:1.0.19'

所需要的請求鏈接

淘寶商品搜索建議:https://suggest.taobao.com/sug?code=utf-8&q=商品名稱

所需要的權限

<uses-permission android:name="android.permission.INTERNET" />
一、StringRequest的用法

StringRequest,字面上理解就是String請求,請求的結果是字符串,這個怎麼用,先看構造方法。

 /**

    /**
     * Creates a new request with the given method.
     *
     * @param method the request {@link Method} to use
     * @param url URL to fetch the string at
     * @param listener Listener to receive the String response
     * @param errorListener Error listener, or null to ignore errors
     */
    public StringRequest(int method, String url, Listener<String> listener,
            ErrorListener errorListener) {
        super(method, url, errorListener);
        mListener = listener;
    }

    /**
     * Creates a new GET request.
     *
     * @param url URL to fetch the string at
     * @param listener Listener to receive the String response
     * @param errorListener Error listener, or null to ignore errors
     */
    public StringRequest(String url, Listener<String> listener, ErrorListener errorListener) {
        this(Method.GET, url, listener, errorListener);
    }

總共有兩個,第一個構造函數有四個參數, 可以指定請求類型,第二個構造函數,有三個參數,默認是GET請求。現在我們嘗試發起一個請求,代碼就可以這麼寫。

RequestQueue mQueue = Volley.newRequestQueue(MainActivity.this);
mQueue.add(getStringRequest());

構建一個消息隊列RequestQueue,RequestQueue是什麼?下面是RequestQueue的類註釋

/**
 * A request dispatch queue with a thread pool of dispatchers.
 *
 * Calling {@link #add(Request)} will enqueue the given Request for dispatch,
 * resolving from either cache or network on a worker thread, and then delivering
 * a parsed response on the main thread.
 */

大概意思就是講:一個具有線程池的請求調度隊列,調用add方法,將會給指定的請求調度,解決從一個工作線程(幹活用的線程,即子線程)的緩存或網絡,然後提供在主線程上的解析響應。

OK,明確告訴我們需要一個請求,並且add。我們先來個簡單的Get請求。

  public StringRequest getStringRequest() {

        return new StringRequest("https://suggest.taobao.com/sug?code=utf-8&q=beizi",

                new Response.Listener<String>() {
                    @Override
                    public void onResponse(String response) {

                        Log.e(getClass().getName(),response);
                    }
                },
                new Response.ErrorListener() {
                    @Override
                    public void onErrorResponse(VolleyError error) {
                        Log.e(getClass().getName(),error.getMessage());
                    }
                }
        );

第一個參數是請求的鏈接地址,第二個參數是正確的回調,第三個參數是錯誤回調。
打印結果:

E/test.volley.com.volley.MainActivity$2: {"result":[["杯子","5198589"],["被子","5508160"],["被子冬被","1657880"],["杯子陶瓷","887481"],["杯子 玻璃","964491"],["杯子陶瓷 帶蓋","360083"],["被子被芯","1835894"],["杯子創意","1946812"],["杯子 保溫杯","1615177"],["被子被芯 冬被","1436705"]]}

上面是GET請求,現在使用第一個四個參數的構造函數試一下POST請求,

public StringRequest(int method, String url, Listener<String> listener, ErrorListener errorListener) { 
        super(method, url, errorListener); mListener = listener; 
}

構造中竟然不能傳遞post參數,這怎麼搞?不必大驚小怪,基本上所有的網絡框架,參數怎麼會在構造中傳呢。肯定是封裝了。看StringRequest的父類Request有不少請求配置相關的方法,其中一個是getParams。

   /**
     * Returns a Map of parameters to be used for a POST or PUT request.  Can throw
     * {@link AuthFailureError} as authentication may be required to provide these values.
     *
     * <p>Note that you can directly override {@link #getBody()} for custom data.</p>
     *
     * @throws AuthFailureError in the event of auth failure
     */
    protected Map<String, String> getParams() throws AuthFailureError {
        return null;
    }

所以,辦法有了,在StringRequest中重寫父類getParams方法,在getParams中返回我們自己的map。所以上面的Request改成這樣

public StringRequest getPostStringRequest() {

        return new StringRequest(Request.Method.POST,"https://suggest.taobao.com/sug?code=utf-8&q=beizi",

                new Response.Listener<String>() {
                    @Override
                    public void onResponse(String response) {

                        Log.e(getClass().getName(),response);
                    }
                },
                new Response.ErrorListener() {
                    @Override
                    public void onErrorResponse(VolleyError error) {
                        Log.e(getClass().getName(),error.getMessage());
                    }
                }
        ){
            @Override
            protected Map<String, String> getParams() throws AuthFailureError {
                Map<String, String> paramMap=new HashMap<>();
                paramMap.put("param1","value1");
                paramMap.put("param2","value3");
                return paramMap;
            }
        };
    }

總結一下步驟:
- 第一步:構建一個RequestQueue
- 第二步:構建一個合適的Request
- 第三步:把第二步的Request add到RequestQueue中
StringRequest就是這麼簡單,然而我們更常用的是JsonRequest

二、JsonRequest的用法

廢話不多說,直接上代碼了。

 private void dispatcherJsonObjectRequest() {

        Volley.newRequestQueue(getApplicationContext()).add(new JsonObjectRequest(Request.Method.POST, "https://suggest.taobao.com/sug?code=utf-8&q=diannao",
                new Response.Listener<JSONObject>() {
                    @Override
                    public void onResponse(JSONObject response) {
                        Log.e(getClass().getName(), response.toString());
                    }
                }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                Log.e(getClass().getName(), error.getMessage());
            }
        }
        ));

    }

打印結果:

E/test.volley.com.volley.MainActivity$2: {"result":[["電腦桌","3558727"],["電腦椅","1740042"],["電腦包","7051240"],["電腦主機","265635"],["電腦音箱","826213"],["電腦","23633255"],["電腦桌 臺式 家用","432711"],["電腦顯示器","401223"],["電腦耳機","577117"],["電腦椅家用","366804"]]}

對JsonRequest請求,可以引入GSON等類似的類庫將JSON字符串一部解析到位。

三、ImageRequest的用法

ImageRequest 的構造函數如下

  /**
     * Creates a new image request, decoding to a maximum specified width and
     * height. If both width and height are zero, the image will be decoded to
     * its natural size. If one of the two is nonzero, that dimension will be
     * clamped and the other one will be set to preserve the image's aspect
     * ratio. If both width and height are nonzero, the image will be decoded to
     * be fit in the rectangle of dimensions width x height while keeping its
     * aspect ratio.
     *
     * @param url URL of the image
     * @param listener Listener to receive the decoded bitmap
     * @param maxWidth Maximum width to decode this bitmap to, or zero for none
     * @param maxHeight Maximum height to decode this bitmap to, or zero for
     *            none
     * @param scaleType The ImageViews ScaleType used to calculate the needed image size.
     * @param decodeConfig Format to decode the bitmap to
     * @param errorListener Error listener, or null to ignore errors
     */
    public ImageRequest(String url, Response.Listener<Bitmap> listener, int maxWidth, int maxHeight,
            ScaleType scaleType, Config decodeConfig, Response.ErrorListener errorListener) {
        super(Method.GET, url, errorListener); 
        setRetryPolicy(
                new DefaultRetryPolicy(IMAGE_TIMEOUT_MS, IMAGE_MAX_RETRIES, IMAGE_BACKOFF_MULT));
        mListener = listener;
        mDecodeConfig = decodeConfig;
        mMaxWidth = maxWidth;
        mMaxHeight = maxHeight;
        mScaleType = scaleType;
    }

參數不少,總共有7個,第一個參數就是圖片的URL地址,第二個參數是圖片請求成功的回調,第三第四個參數分別用於指定允許圖片最大的寬度和高度,第五個參數,指定圖片的縮放類型,第六個參數用於指定圖片的顏色屬性,Bitmap.Config下的幾個常量都可以在這裏使用,其中ARGB_8888可以展示最好的顏色屬性,每個圖片像素佔據4個字節的大小,而RGB_565則表示每個圖片像素佔據2個字節大小。第六個參數是圖片請求失敗的回調,當請求失敗時在ImageView中顯示一張默認圖片。其實個人覺得這不是一種好的設計,參數太多,不利於用戶使用。用這個構造函數創建一個新的圖像請求,解碼到一個最大指定的寬度和高度。如果兩個寬度和高度都爲零,圖像將被解碼爲其圖片本身的大小。如果指定的網絡圖片的寬度或高度大於這裏的最大值,則會對圖片進行壓縮。

 private  void dispatherImageRequest(){
        Volley.newRequestQueue(getApplicationContext()).add(new ImageRequest("https://ss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/img/logo/bd_logo1_31bdc765.png", new Response.Listener<Bitmap>() {
            @Override
            public void onResponse(Bitmap response) {
                mImageView.setImageBitmap(response);
            }
        },0,0, ImageView.ScaleType.CENTER, Bitmap.Config.ALPHA_8,new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                mImageView.setImageResource(R.mipmap.ic_launcher);
            }
        }));
    }
四、ImageLoader的用法

ImageLoader的內部也是用ImageRequest來實現,它的構造器可以傳入一個ImageCache緩存形參,實現了圖片緩存的功能,同時還可以過濾重複鏈接,避免重複發送請求。


    private void dispatherImageLoader() {
        RequestQueue mQueue = Volley.newRequestQueue(MainActivity.this);
        ImageLoader imageLoader = new ImageLoader(mQueue, new ImageLoader.ImageCache() {
            int maxSize = 10 * 1024 * 1024;
            private LruCache<String, Bitmap> mCache = new LruCache<String, Bitmap>(maxSize) {
                @Override
                protected int sizeOf(String key, Bitmap bitmap) {
                    return bitmap.getRowBytes() * bitmap.getHeight();
                }
            };
            @Override
            public Bitmap getBitmap(String url) {
                return mCache.get(url);
            }

            @Override
            public void putBitmap(String url, Bitmap bitmap) {
                mCache.put(url,bitmap);
            }
        });
        imageLoader.get("https://ss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/img/logo/bd_logo1_31bdc765.png", new ImageLoader.ImageListener() {
            @Override
            public void onResponse(ImageLoader.ImageContainer response, boolean isImmediate) {
                mImageView.setImageBitmap(response.getBitmap());
            }

            @Override
            public void onErrorResponse(VolleyError error) {
                mImageView.setImageResource(R.mipmap.ic_launcher);

            }
        });

    }

與ImageRequest實現效果不同的是,ImageLoader加載圖片會先顯示默認的圖片,等待圖片加載完成纔會顯示在ImageView上。 

5、NetworkImageView用法

NetworkImageView是一個自定義控件,繼承自ImageView,封裝了請求網絡加載圖片的功能。 這種思想不錯,在項目中經常可以使用這種思路加強封裝性。
Layout中添加

    <com.android.volley.toolbox.NetworkImageView
        android:id="@+id/networkview"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
  RequestQueue mQueue = Volley.newRequestQueue(MainActivity.this);
        ImageLoader imageLoader = new ImageLoader(mQueue, new ImageLoader.ImageCache() {
            int maxSize = 10 * 1024 * 1024;
            private LruCache<String, Bitmap> mCache = new LruCache<String, Bitmap>(maxSize) {
                @Override
                protected int sizeOf(String key, Bitmap bitmap) {
                    return bitmap.getRowBytes() * bitmap.getHeight();
                }
            };
            @Override
            public Bitmap getBitmap(String url) {
                return mCache.get(url);
            }

            @Override
            public void putBitmap(String url, Bitmap bitmap) {
                mCache.put(url,bitmap);
            }
        });
        mNetworkImageView.setDefaultImageResId(R.mipmap.ic_launcher);
        mNetworkImageView.setErrorImageResId(R.mipmap.ic_launcher);
        mNetworkImageView.setImageUrl("https://ss0.bdstatic.com/5aV1bjqh_Q23odCf/static/superman/img/logo/bd_logo1_31bdc765.png",imageLoader);

調用mNetworkImageView的setDefaultImageResId()方法、setErrorImageResId()方法和setImageUrl()方法來分別設置加載中顯示的圖片,加載失敗時顯示的圖片,以及目標圖片的URL地址。和ImageRequest與ImageLoader不同,不需要顯示的指定圖片的寬高是否要壓縮,這是因爲,NetworkImageView可以根據我們設置控件的寬和高結合網絡圖片的寬和高,內部會自動去實現壓縮,如果我們不想要壓縮可以設置NetworkImageView控件的寬和高都爲wrap_content。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章