Volley 網(wǎng)絡(luò)請求框架介紹與使用說明

一作彤、前言

Volley 是一個基于 HTTP 的網(wǎng)絡(luò)開源庫,讓 Android 應(yīng)用更快更容易地連接網(wǎng)絡(luò)誉碴,在 GitHub 上可以找到它的源項目宦棺。Volley 具有以下優(yōu)點:

* 自動調(diào)度網(wǎng)絡(luò)請求瓣距。

* 支持多并發(fā)網(wǎng)絡(luò)連接黔帕。

* 支持緩存。

* 支持請求優(yōu)先級蹈丸。

* 支持取消請求成黄,可以取消單個請求,也可以取消包含多個請求的請求塊逻杖。

* 支持自定義奋岁。

* 支持異步數(shù)據(jù)排序功能。
 
* 支持調(diào)試和具備跟蹤工具荸百。

Volley 適用于 RPC(遠(yuǎn)程過程調(diào)用:Remote Procedure Call)類型操作闻伶,例如將搜索結(jié)果頁面作為結(jié)構(gòu)化數(shù)據(jù)獲取。Volley 可以很容易與所有協(xié)議集成够话,支持原始字符串蓝翰,圖像和 JSON。

Volley 不適合大型下載或流媒體操作女嘲,因為 Volley 在解析期間將所有響應(yīng)保存在內(nèi)存中畜份。對于大型下載操作,可以考慮使用 DownloadManager欣尼。

最簡單添加 Volley 的方法是將以下依賴項添加到應(yīng)用程序的 build.gradle 文件中:

dependencies {
    ...
    compile 'com.android.volley:volley:1.1.1'
}

除此之外爆雹,你還可以克隆 Volley 項目庫并將其設(shè)置為庫項目:

  1. 通過在命令行鍵入以下內(nèi)容來克隆項目庫:
git clone https://github.com/google/volley
  1. 將下載的源作為 Android 庫模塊導(dǎo)入到應(yīng)用項目中。

二愕鼓、使用 Volley 發(fā)送請求

使用 Volley 之前钙态,必須將 android.permission.INTERNET 權(quán)限添加到應(yīng)用的清單中。不然菇晃,應(yīng)用無法連接到網(wǎng)絡(luò)驯绎。

1. 使用 newRequestQueue

Volley 提供了一個便捷的方法 Volley.newRequestQueue 為你設(shè)置并啟動 RequestQueue 隊列,該 RequestQueue 使用默認(rèn)值谋旦。例如:

final TextView mTextView = (TextView) findViewById(R.id.text);
// ...

// Instantiate the RequestQueue.
RequestQueue queue = Volley.newRequestQueue(this);
String url ="http://www.google.com";

// Request a string response from the provided URL.
StringRequest stringRequest = new StringRequest(Request.Method.GET, url,
            new Response.Listener<String>() {
    @Override
    public void onResponse(String response) {
        // Display the first 500 characters of the response string.
        mTextView.setText("Response is: "+ response.substring(0,500));
    }
}, new Response.ErrorListener() {
    @Override
    public void onErrorResponse(VolleyError error) {
        mTextView.setText("That didn't work!");
    }
});

// Add the request to the RequestQueue.
queue.add(stringRequest);

Volley 總是在主線程上傳遞已解析的響應(yīng)剩失,這樣可以很方便地使用接收到的數(shù)據(jù)填充 UI 控件屈尼。

2. 發(fā)送請求

要發(fā)送請求,只需構(gòu)建一個請求并使用 add() 將其添加到 RequestQueue拴孤,如上所示脾歧。添加請求后,請求將通過網(wǎng)絡(luò)獲取服務(wù)演熟,解析并傳遞其原始響應(yīng)鞭执。

當(dāng)調(diào)用 add() 時,Volley 運行一個高速緩存處理線程和一個網(wǎng)絡(luò)分派線程池芒粹。當(dāng)向隊列添加請求時兄纺,它會被緩存線程拾取并進(jìn)行分類:如果請求可以從緩存中獲取服務(wù),則緩存響應(yīng)將在緩存線程上進(jìn)行解析化漆,并將解析后的響應(yīng)在主線程上傳遞估脆。如果無法從緩存中為請求提供服務(wù),則將其置于網(wǎng)絡(luò)隊列中座云。第一個可用的網(wǎng)絡(luò)線程從隊列中獲取請求疙赠,執(zhí)行 HTTP 事務(wù),在子線程上解析響應(yīng)朦拖,然后將響應(yīng)寫入緩存圃阳,并將解析的響應(yīng)發(fā)送回主線程來進(jìn)行傳遞。

可以在任意線程中添加請求璧帝,但響應(yīng)始終在主線程上傳遞捍岳。

請求的生命周期

3. 取消請求

要取消請求,請對 Request對象調(diào)用 cancel()睬隶。一旦取消锣夹,Volley 保證你的響應(yīng)處理回調(diào)永遠(yuǎn)不會被調(diào)用。一般可以在 Activity 的 onStop() 方法中取消所有待處理的請求理疙。

但是晕城,這樣的話你必須跟蹤所有正在進(jìn)行的請求。有一種更簡單的方法:你可以使用一個標(biāo)記對象與每個請求進(jìn)行關(guān)聯(lián)窖贤。然后砖顷,使用此標(biāo)記對象獲得取消請求的范圍。例如赃梧,使用 Activity 將所有由它發(fā)出的請求進(jìn)行標(biāo)記滤蝠,并從 onStop() 中調(diào)用 requestQueue.cancelAll(this)。同樣授嘀,在 ViewPager 選項卡中使用各自的選項卡標(biāo)記所有縮略圖圖像請求物咳,并在滑動時取消,以確保新選項卡不會被另一個選項卡的請求持有蹄皱。

以下是使用字符串標(biāo)記的示例:

  1. 定義標(biāo)記并將其添加到你的請求中览闰。
public static final String TAG = "MyTag";
StringRequest stringRequest; // Assume this exists.
RequestQueue mRequestQueue;  // Assume this exists.

// Set the tag on the request.
stringRequest.setTag(TAG);

// Add the request to the RequestQueue.
mRequestQueue.add(stringRequest);
  1. 在 Activity 的 onStop() 方法中芯肤,取消所有具有此標(biāo)記的請求。
@Override
protected void onStop () {
    super.onStop();
    if (mRequestQueue != null) {
        mRequestQueue.cancelAll(TAG);
    }
}

取消請求時要注意压鉴,該請求的響應(yīng)是否是必要的崖咨。

三、設(shè)置 RequestQueue

1. 設(shè)置網(wǎng)絡(luò)和緩存

RequestQueue 需要兩樣?xùn)|西都完成它的工作:一個是用于執(zhí)行請求傳輸?shù)木W(wǎng)絡(luò)油吭,一個是用于處理緩存的緩存击蹲。Volley 工具箱中已經(jīng)有標(biāo)準(zhǔn)的實現(xiàn):DiskBasedCache 提供帶有內(nèi)存索引的單文件響應(yīng)緩存,BasicNetwork 根據(jù)你首選的 HTTP 客戶端提供網(wǎng)絡(luò)傳輸婉宰。

BasicNetwork 是 Volley 的默認(rèn)網(wǎng)絡(luò)實現(xiàn)歌豺。BasicNetwork 必須被用來連接到網(wǎng)絡(luò)的 HTTP 客戶端初始化。這個客戶端通常是 HttpURLConnection心包。

下面代碼段顯示了初始化 BasicNetwork 的步驟包括設(shè)置 RequestQueue

RequestQueue mRequestQueue;

// Instantiate the cache
Cache cache = new DiskBasedCache(getCacheDir(), 1024 * 1024); // 1MB cap

// Set up the network to use HttpURLConnection as the HTTP client.
Network network = new BasicNetwork(new HurlStack());

// Instantiate the RequestQueue with the cache and network.
mRequestQueue = new RequestQueue(cache, network);

// Start the queue
mRequestQueue.start();

String url ="http://www.example.com";

// Formulate the request and handle the response.
StringRequest stringRequest = new StringRequest(Request.Method.GET, url,
        new Response.Listener<String>() {
    @Override
    public void onResponse(String response) {
        // Do something with the response
    }
},
    new Response.ErrorListener() {
        @Override
        public void onErrorResponse(VolleyError error) {
            // Handle error
    }
});

// Add the request to the RequestQueue.
mRequestQueue.add(stringRequest);

// ...

你可以在任何時候創(chuàng)建 RequestQueue类咧,并在得到響應(yīng)后調(diào)用 stop() 來實現(xiàn)單次請求。

但更常見的情況是創(chuàng)建一個 RequestQueue 的單例谴咸,使得它在應(yīng)用的生命周期內(nèi)保持運行轮听。

2. 使用單例模式

設(shè)置一個 RequestQueue 的單例通常效率最高骗露。推薦的方法是實現(xiàn)封裝 RequestQueue 和其他 Volley 功能的單例類岭佳。另一種方法是創(chuàng)建 Application 的子類并在 Application.onCreate() 方法中設(shè)置 RequestQueue,但是這種方法并不推薦萧锉,因為靜態(tài)單例可以以更模塊化的方式提供相同的功能珊随。

一個關(guān)鍵概念是 RequestQueue 必須使用 Application 上下文進(jìn)行實例化,而不是 Activity 上下文柿隙。這樣可以確保 RequestQueue在應(yīng)用的生命周期內(nèi)持續(xù)使用叶洞,而不是每次重新創(chuàng)建 Activtiy 時重新實例化(例如當(dāng)用戶旋轉(zhuǎn)設(shè)備時)。

這里是一個單類禀崖,它提供 RequestQueueImageLoader 功能:

public class MySingleton {
    private static MySingleton mInstance;
    private RequestQueue mRequestQueue;
    private ImageLoader mImageLoader;
    private static Context mCtx;

    private MySingleton(Context context) {
        mCtx = context;
        mRequestQueue = getRequestQueue();

        mImageLoader = new ImageLoader(mRequestQueue,
                new ImageLoader.ImageCache() {
            private final LruCache<String, Bitmap>
                    cache = new LruCache<String, Bitmap>(20);

            @Override
            public Bitmap getBitmap(String url) {
                return cache.get(url);
            }

            @Override
            public void putBitmap(String url, Bitmap bitmap) {
                cache.put(url, bitmap);
            }
        });
    }

    public static synchronized MySingleton getInstance(Context context) {
        if (mInstance == null) {
            mInstance = new MySingleton(context);
        }
        return mInstance;
    }

    public RequestQueue getRequestQueue() {
        if (mRequestQueue == null) {
            // getApplicationContext() is key, it keeps you from leaking the
            // Activity or BroadcastReceiver if someone passes one in.
            mRequestQueue = Volley.newRequestQueue(mCtx.getApplicationContext());
        }
        return mRequestQueue;
    }

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

    public ImageLoader getImageLoader() {
        return mImageLoader;
    }
}

以下是使用單例類執(zhí)行 RequestQueue 操作的一些示例:

// Get a RequestQueue
RequestQueue queue = MySingleton.getInstance(this.getApplicationContext()).
    getRequestQueue();

// ...

// Add a request (in this example, called stringRequest) to your RequestQueue.
MySingleton.getInstance(this).addToRequestQueue(stringRequest);

四衩辟、提出標(biāo)準(zhǔn)請求

Volley 支持的常見請求類型:

  • StringRequest。指定 URL 并接收原始字符串作為響應(yīng)波附。

  • JsonObjectRequest 和 JsonArrayRequest(兩個都是 JsonRequest 的子類)艺晴。指定 URL 并分別獲取 JSON 對象或數(shù)組作為響應(yīng)。

如果你的預(yù)期響應(yīng)是這些類型之一掸屡,則不必實現(xiàn)自定義請求封寞。

1. 請求 JSON

Volley 為 JSON 請求提供以下類:

  • JsonArrayRequest - 使用給定的 URL 獲取 JSONArray 響應(yīng)體。

  • JsonObjectRequest - 使用給定的 URL 獲取 JSONObject 響應(yīng)體仅财,允許將 JSONObject 作為請求體的一部分傳入狈究。

這兩個類都基于公共基類 JsonRequest。下面代碼段是提取 JSON 數(shù)據(jù)并在 UI 中將其顯示為文本:

String url = "http://my-json-feed";

JsonObjectRequest jsonObjectRequest = new JsonObjectRequest
        (Request.Method.GET, url, null, new Response.Listener<JSONObject>() {

    @Override
    public void onResponse(JSONObject response) {
        mTextView.setText("Response: " + response.toString());
    }
}, new Response.ErrorListener() {

    @Override
    public void onErrorResponse(VolleyError error) {
        // TODO: Handle error

    }
});

// Access the RequestQueue through your singleton class.
MySingleton.getInstance(this).addToRequestQueue(jsonObjectRequest);

五盏求、實現(xiàn)自定義請求

對于那些不支持開箱即用的數(shù)據(jù)類型抖锥,我們需要實現(xiàn)自定義請求類型亿眠。

1. 寫一個自定義請求

大多數(shù)請求在工具箱中都有現(xiàn)成的實現(xiàn),如果響應(yīng)是字符串磅废、圖像或 JSON缕探,則不需要實現(xiàn)自定義請求。

對于需要實現(xiàn)自定義請求的情況还蹲,你只需執(zhí)行以下操作:

  • 擴(kuò)展 Request<T> 類爹耗,其中 <T> 表示請求所期望的已解析響應(yīng)的類型。因此谜喊,如果解析后的響應(yīng)是字符串潭兽,則通過擴(kuò)展 Request<String> 創(chuàng)建自定義請求。

  • 實現(xiàn)抽象方法斗遏,parseNetworkResponse()deliverResponse()山卦。

1.1 parseNetworkResponse

對于給定類型(例如字符串,圖像或 JSON)诵次,Response 封裝解析的響應(yīng)用來傳遞账蓉。以下是一個示例實現(xiàn) parseNetworkResponse()

@Override
protected Response<T> parseNetworkResponse(
        NetworkResponse response) {
    try {
        String json = new String(response.data,
        HttpHeaderParser.parseCharset(response.headers));
    return Response.success(gson.fromJson(json, clazz),
    HttpHeaderParser.parseCacheHeaders(response));
    }
    // handle errors
// ...
}

請注意:parseNetworkResponse()NetworkResponse 作為參數(shù) ,其中包含響應(yīng)有效負(fù)載作為 byte []逾一、HTTP 狀態(tài)代碼和響應(yīng)頭铸本。

自定義實現(xiàn)必須返回一個 Response<T>,其中包含你自定義的響應(yīng)對象遵堵、緩存元數(shù)據(jù)或錯誤箱玷。

如果你的協(xié)議具有非標(biāo)準(zhǔn)緩存語義芹彬,你可以自己構(gòu)造一個 Cache.Entry丽旅,但大多數(shù)請求如下使用:

return Response.success(myDecodedObject,
        HttpHeaderParser.parseCacheHeaders(response));

Volley 在工作線程中調(diào)用 parseNetworkResponse()。這確保了耗時的解析操作(例如將 JPEG 解碼為 Bitmap)不會阻塞 UI 線程蹬敲。

1.2 deliverResponse

Volley 在 parseNetworkResponse() 方法中攜帶返回的對象回到主線程壳坪。大多數(shù)請求在此處調(diào)用回調(diào)接口舶得,例如:

protected void deliverResponse(T response) {
        listener.onResponse(response);

六、示例:GsonRequest

Gson 是一個通過反射將 Java 對象轉(zhuǎn)換為 JSON 或者將 JSON 轉(zhuǎn)換為 Java 對象的開源庫爽蝴。你可以定義擁有相同 JSON 鍵字段的 Java 對象沐批,將類對象傳給 Gson,Gson 自動使用響應(yīng)數(shù)據(jù)填充字段霜瘪。

下面是使用 Gson 解析 Volley 請求的完整實現(xiàn):

public class GsonRequest<T> extends Request<T> {
    private final Gson gson = new Gson();
    private final Class<T> clazz;
    private final Map<String, String> headers;
    private final Listener<T> listener;

    /**
     * Make a GET request and return a parsed object from JSON.
     *
     * @param url URL of the request to make
     * @param clazz Relevant class object, for Gson's reflection
     * @param headers Map of request headers
     */
    public GsonRequest(String url, Class<T> clazz, Map<String, String> headers,
            Listener<T> listener, ErrorListener errorListener) {
        super(Method.GET, url, errorListener);
        this.clazz = clazz;
        this.headers = headers;
        this.listener = listener;
    }

    @Override
    public Map<String, String> getHeaders() throws AuthFailureError {
        return headers != null ? headers : super.getHeaders();
    }

    @Override
    protected void deliverResponse(T response) {
        listener.onResponse(response);
    }

    @Override
    protected Response<T> 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));
        }
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末珠插,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子颖对,更是在濱河造成了極大的恐慌捻撑,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,252評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異顾患,居然都是意外死亡番捂,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,886評論 3 399
  • 文/潘曉璐 我一進(jìn)店門江解,熙熙樓的掌柜王于貴愁眉苦臉地迎上來设预,“玉大人,你說我怎么就攤上這事犁河”钫恚” “怎么了?”我有些...
    開封第一講書人閱讀 168,814評論 0 361
  • 文/不壞的土叔 我叫張陵桨螺,是天一觀的道長宾符。 經(jīng)常有香客問我,道長灭翔,這世上最難降的妖魔是什么魏烫? 我笑而不...
    開封第一講書人閱讀 59,869評論 1 299
  • 正文 為了忘掉前任,我火速辦了婚禮肝箱,結(jié)果婚禮上哄褒,老公的妹妹穿的比我還像新娘。我一直安慰自己煌张,他們只是感情好呐赡,可當(dāng)我...
    茶點故事閱讀 68,888評論 6 398
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著唱矛,像睡著了一般罚舱。 火紅的嫁衣襯著肌膚如雪井辜。 梳的紋絲不亂的頭發(fā)上绎谦,一...
    開封第一講書人閱讀 52,475評論 1 312
  • 那天,我揣著相機與錄音粥脚,去河邊找鬼窃肠。 笑死,一個胖子當(dāng)著我的面吹牛刷允,可吹牛的內(nèi)容都是我干的冤留。 我是一名探鬼主播,決...
    沈念sama閱讀 41,010評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼树灶,長吁一口氣:“原來是場噩夢啊……” “哼纤怒!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起天通,我...
    開封第一講書人閱讀 39,924評論 0 277
  • 序言:老撾萬榮一對情侶失蹤泊窘,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體烘豹,經(jīng)...
    沈念sama閱讀 46,469評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡瓜贾,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,552評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了携悯。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片祭芦。...
    茶點故事閱讀 40,680評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖憔鬼,靈堂內(nèi)的尸體忽然破棺而出龟劲,到底是詐尸還是另有隱情,我是刑警寧澤轴或,帶...
    沈念sama閱讀 36,362評論 5 351
  • 正文 年R本政府宣布咸灿,位于F島的核電站,受9級特大地震影響侮叮,放射性物質(zhì)發(fā)生泄漏避矢。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 42,037評論 3 335
  • 文/蒙蒙 一囊榜、第九天 我趴在偏房一處隱蔽的房頂上張望审胸。 院中可真熱鬧,春花似錦卸勺、人聲如沸砂沛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,519評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽碍庵。三九已至,卻和暖如春悟狱,著一層夾襖步出監(jiān)牢的瞬間静浴,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,621評論 1 274
  • 我被黑心中介騙來泰國打工挤渐, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留苹享,地道東北人。 一個月前我還...
    沈念sama閱讀 49,099評論 3 378
  • 正文 我出身青樓浴麻,卻偏偏與公主長得像得问,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子软免,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,691評論 2 361

推薦閱讀更多精彩內(nèi)容