Android框架學習筆記04Volley框架

上一篇中我們學習了Retrofit框架,這一篇我們學習另外一個網絡請求框架——Volley。Volley框架是Google在2013年I/O大會上推出的一個基于HttpUrlconnection封裝的網絡通信框架,上一篇我們學習的Retrofit是基于Okhttp封裝的。Volley將AsyncHttpClient和Universal-Image-Loader的優點集于了一身,我們只需要很簡單的幾句代碼就可以實現網絡通信,我們也不用再關心怎么從網絡獲取圖片、不用關心線程、不用關心圖片資源回收等。Volley除了簡單易用之外,性能也非常好,對于數據量不大,但是操作很頻繁的操作,Volley表現的非常好;但是對于下載文件等大數據量的網絡操作,Volley就不是個很好的選擇了。

概述

一句話概括Volley框架:Volley是Google推出的一個異步的網絡操作和圖片加載框架。

Volley網絡通信操作是基于隊列的,我們只需要把請求添加到請求隊列中就可以了,請求隊列會一次執行請求,一般情況下,應用的網絡操作沒有很多很頻繁,一個Application對應一個請求隊列就可以了;如果是請求網絡操作很多很頻繁,那么可以一個Activity對應一個請求隊列。

Volley框架有以下特征:

  • 自動調度網絡請求

  • 多個并發的網絡連接

  • 通過使用標準的HTTP緩存機制保持磁盤和內存響應的一致

  • 可擴展性:Volley支持請求優先級、支持取消請求(可以取消一個或者多個請求)

  • 健壯性:Volley是面向接口編程,可定制性強

  • 包含調試和追蹤工具

  • 提供簡單易用的圖片加載

我們看一下Volley的總體設計圖

圖片來源于網絡。從上圖中我們可以看出,volley主要是通過兩種Diapatch Thread不斷從RequestQueue中取出請求,根據是否已緩存調用Cache或Network這兩類數據獲取接口之一,從內存緩存或是服務器取得請求的數據,然后交由ResponseDelivery去做結果分發及回調處理。

下面我們通過例子使用一下Volley這個框架:

使用

Volley的基本使用步驟是:

  1. 構建一個RequestQueue。

  2. 創建一個Request,Volley提供的Request有:StringRequest、JsonObjectRequest、JsonArrayRequest、ImageRequest,這四種。

  3. 將Request添加到RequestQueue中。

Volley框架的網絡操作基本是這幾步,下面我們通過實例學習一下Volley的網絡操作。

跟其它框架一樣,需要在AS的Gradle中添加引用:

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

將上面代碼添加到項目的build.gradle中sync就可以了,不過建議先去Volley的GitHub中添加最新的版本,除了添加引用還需要添加網絡訪問的權限。

Google建議我們使用Volley單例工具類,所以我們寫了一個單例工具類

package com.example.frame.common;

import android.content.Context;
import android.graphics.Bitmap;

import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.toolbox.ImageLoader;
import com.android.volley.toolbox.Volley;

/**
 * Created by :Huawen
 * Created time : 2016/9/26 11:16
 * Description :
 * Github: https://github.com/Devin1102
 */

public class VolleyController {
private static VolleyController instance;
private Context mContext;
private ImageLoader mImageLoader;
private RequestQueue mRequestQueue;

private VolleyController(Context mContext) {
    this.mContext = mContext;
    initData();
}

/**
 * 初始化
 */
private void initData() {
    if (mRequestQueue == null) {
        mRequestQueue = Volley.newRequestQueue(mContext);
    }
    if (mImageLoader == null) {
        mImageLoader = new ImageLoader(mRequestQueue, new ImageLoader.ImageCache() {
            @Override
            public Bitmap getBitmap(String url) {
                return null;
            }

            @Override
            public void putBitmap(String url, Bitmap bitmap) {

            }
        });
    }
}

/**
 * 獲取到VolleyController
 *
 * @param mContext
 * @return
 */
public static synchronized VolleyController getInstance(Context mContext) {
    if (instance == null) {
        instance = new VolleyController(mContext);
    }
    return instance;
}

public RequestQueue getRequestQueue() {
    return mRequestQueue;
}

public ImageLoader getImageLoader() {
    return mImageLoader;
}

public <T> void addToRequestQueue(Request<T> req) {
    getRequestQueue().add(req);
}

}

這里只是一個單例的Volley工具類,提供了過去Volley中RequestQueue和ImageLoader的方法,ImageLoader在我們使用Volley加載圖片的時候用到,所以在這里就添加了一個獲取ImageLoader的方法。

前面我們說過Volley中提供的Request有四種,分別是:StringRequest、JsonObjectRequest、JsonArrayRequest和ImageRequest,下面我們就分別使用這四個Request實現GET請求和POST請求,ImageRequest用于加載圖片。

StringRequest實現GET請求

StringRequest返回的數據是String類型的,StringRequest提供兩個構造方法,我們看一下源碼:

public class StringRequest extends Request<String> {

private Listener<String> mListener;

/**
 * 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);
}
}

我們可以看到,第一個構造器是多一個參數的,這個參數就是設置請求的方法,第二個構造器沒有method這個請求參數,默認就是GET請求。在StringRequest的構造器中,第一個是設置請求方法,第二個是請求的url,第三個是請求成功的回調,第四個是請求失敗的回調。下面我們看一下實例:

private void stringReqGet() {
    StringRequest stringReqGet = new StringRequest(Request.Method.GET, UrlUtils.GET_BASE_URL,
            new Response.Listener<String>() {
                @Override
                public void onResponse(String response) {
                    Log.i(TAG, "onResponse" + response);
                    Log.i(TAG, "onResponse---stringReqGet---" + getResources().getString(R.string.req_success));
                }
            }, new Response.ErrorListener() {
        @Override
        public void onErrorResponse(VolleyError error) {
            Log.i(TAG, "onErrorResponse" + getResources().getString(R.string.req_failed));
        }
    });
    mRequestQueue.add(stringReqGet);
}

如果是GET請求,那么第一個參數可以不指定,默認是GET請求,我們需要實現兩個回調。在一開始的時候我們就說過,Volley是異步的,我們不需要關心線程,請求成功之后更新UI需要通過消息或者其它方式將數據傳遞過去,不能直接在請求回調中更新UI。需要注意的是:只有將請求添加到RequestQueue中,請求才會執行。

StringRequest實現POST請求

Volley框架試下StringRequest的POST請求非常簡單,只需要設置請求方法是POST,然后設置請求參數即可,我們看一下例子源碼:

 private void stringReqPost() {
    StringRequest stringReqPost = new StringRequest(Request.Method.POST, UrlUtils.GET_BASE_URL,
            new Response.Listener<String>() {
                @Override
                public void onResponse(String response) {
                    Log.i(TAG, "onResponse---stringReqPost---" + getResources().getString(R.string.req_success));
                }
            }, new Response.ErrorListener() {
        @Override
        public void onErrorResponse(VolleyError error) {
            Log.i(TAG, "onErrorResponse" + getResources().getString(R.string.req_failed));
        }
    }) {
        @Override
        protected Map<String, String> getParams() throws AuthFailureError {
            Map<String, String> stringMap = new HashMap<>();
            stringMap.put("username", "Devin");
            stringMap.put("password", "Devin");
            return stringMap;
        }
    };
    mRequestQueue.add(stringReqPost);
}

需要注意的是:請求方法是POST的話,執行請求的時候會調用getParams()方法,所以我們需要在這里設置請求參數,可以使用Map來設置。

JsonObjectRequest實現GET請求

JsonObjectRequest實現GET請求不是很復雜,和StringRequest差不多,我們直接看例子:

private void jsonObjectReqGet() {
    JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(UrlUtils.GET_BASE_URL,
            new Response.Listener<JSONObject>() {
                @Override
                public void onResponse(JSONObject response) {
                    Log.i(TAG, "onResponse" + response.toString());
                    Log.i(TAG, "onResponse" + getResources().getString(R.string.req_success));
                }
            }, new Response.ErrorListener() {
        @Override
        public void onErrorResponse(VolleyError error) {
            Log.i(TAG, "onErrorResponse" + getResources().getString(R.string.req_failed));
        }
    });
    mRequestQueue.add(jsonObjectRequest);
}

JsonObjectRequest實現POST請求

private void jsonObjectReqPost() {
    HashMap<String, String> hashMap = new HashMap<>();
    hashMap.put("username", "Devin");
    hashMap.put("password", "Devin");
    JSONObject jsonObject = new JSONObject(hashMap);
    JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(Request.Method.POST, UrlUtils.GET_BASE_URL,
            jsonObject, new Response.Listener<JSONObject>() {
        @Override
        public void onResponse(JSONObject response) {
            Log.i(TAG, "onResponse" + getResources().getString(R.string.req_success));
        }
    }, new Response.ErrorListener() {
        @Override
        public void onErrorResponse(VolleyError error) {
            Log.i(TAG, "onErrorResponse" + getResources().getString(R.string.req_failed));
        }
    }) {

        @Override
        public Map<String, String> getHeaders() throws AuthFailureError {
            HashMap<String, String> headers = new HashMap<>();
            headers.put("Accept", "application/json");
            headers.put("Content-Type", "application/json; charset=UTF-8");
            return headers;
        }
    };

    mRequestQueue.add(jsonObjectRequest);
}

JsonObjectRequest的POST請求和StringRequest不一樣,在JsonObjectRequest中,我們需要封裝一個JSONObject,當成參數傳遞進去,然后還需要調用getHeaders()方法。JsonObjectRequest有5種構造,這里就不貼代碼了,非常簡單,想要了解的可以自己去看一下源碼。

JsonArrayRequest實現GET請求

private void jsonArrayReqGet() {
    JsonArrayRequest jsonArrayRequest = new JsonArrayRequest(Request.Method.GET, UrlUtils.GET_BASE_URL,
            new Response.Listener<JSONArray>() {
                @Override
                public void onResponse(JSONArray response) {
                    Log.i(TAG, "onResponse" + response.toString());
                    Log.i(TAG, "onResponse" + getResources().getString(R.string.req_success));
                }
            }, new Response.ErrorListener() {
        @Override
        public void onErrorResponse(VolleyError error) {
            Log.i(TAG, "onErrorResponse" + getResources().getString(R.string.req_failed));
        }
    });
    mRequestQueue.add(jsonArrayRequest);
}

JsonArrayRequest實現POST請求

private void jsonArrayReqPost() {
    HashMap<String, String> hashMap = new HashMap<>();
    hashMap.put("username", "Devin");
    hashMap.put("password", "Devin");
    JSONArray jsonArray = new JSONArray();
    jsonArray.put(new JSONObject(hashMap));
    JsonArrayRequest jsonArrayRequest = new JsonArrayRequest(Request.Method.POST, UrlUtils.GET_BASE_URL,
            jsonArray, new Response.Listener<JSONArray>() {
        @Override
        public void onResponse(JSONArray response) {
            Log.i(TAG, "onResponse" + getResources().getString(R.string.req_success));
        }
    }, new Response.ErrorListener() {
        @Override
        public void onErrorResponse(VolleyError error) {
            Log.i(TAG, "onErrorResponse" + getResources().getString(R.string.req_failed));
        }
    }) {
        @Override
        public Map<String, String> getHeaders() throws AuthFailureError {
            HashMap<String, String> headers = new HashMap<>();
            headers.put("Accept", "application/json");
            headers.put("Content-Type", "application/json; charset=UTF-8");
            return headers;
        }
    };
    mRequestQueue.add(jsonArrayRequest);
}

JsonArrayRequest的POST請求跟JsonObjectRequest的差不多,具體可以看一下構造器。

使用ImageRequest加載圖片

private void loadImage() {
    net_iv.setVisibility(View.INVISIBLE);
    iv_volley.setVisibility(View.VISIBLE);
    ImageRequest imageRequest = new ImageRequest("https://www.baidu.com/img/bd_logo1.png",
            new Response.Listener<Bitmap>() {
                @Override
                public void onResponse(Bitmap response) {
                    iv_volley.setImageBitmap(response);
                    Log.i(TAG, "onResponse" + getResources().getString(R.string.req_success));
                }
            }, 0, 0, Bitmap.Config.RGB_565, new Response.ErrorListener() {
        @Override
        public void onErrorResponse(VolleyError error) {
            Log.i(TAG, "onErrorResponse" + getResources().getString(R.string.req_failed));
        }
    });
    mRequestQueue.add(imageRequest);
}

從上面的代碼中我們可以看到,我們可以直接在成功的回調中更新UI,非常方便。不過使用ImageRequest加載網絡圖片也逐漸被另外一種方式取代。

使用ImageLoader和NetworkImageView加載圖片

使用ImageLoader和NetworkImageView加載圖片是Volley框架推薦使用的方法,其中NetworkImageView是Volley框架自帶的一個控件,使用非常簡單。

private void imageLoader() {
    String imageUrl = "http://i.weather.com.cn/images/cn/public/2016/09/26/26101505A26375E9BD043626FBA69D86BB7687AC.jpg";
    net_iv.setVisibility(View.VISIBLE);
    iv_volley.setVisibility(View.INVISIBLE);
    ImageLoader mImageLoader = VolleyController.getInstance(getContext()).getImageLoader();
    net_iv.setDefaultImageResId(R.mipmap.ic_menu_icon);
    net_iv.setErrorImageResId(R.mipmap.ic_launcher);
    net_iv.setImageUrl(imageUrl, mImageLoader);
}

使用非常簡單,直接調用setImageUrl()方法就可以了。

這里只是初步使用Volley框架,還有更高級的用法,比如自定義Request等,歡迎大家留言交流!

上面項目的Demo在我的GitHub上,有需要的可以參考參考,GitHub地址

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,362評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,577評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,486評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,852評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,600評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,944評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,944評論 3 447
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,108評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,652評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,385評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,616評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,111評論 5 364
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,798評論 3 350
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,205評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,537評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,334評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,570評論 2 379

推薦閱讀更多精彩內容