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

一、前言

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

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

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

* 支持緩存。

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

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

* 支持自定義。

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

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

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

最簡單添加 Volley 的方法是將以下依賴項添加到應用程序的 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 庫模塊導入到應用項目中。

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

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

1. 使用 newRequestQueue

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

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 總是在主線程上傳遞已解析的響應,這樣可以很方便地使用接收到的數(shù)據(jù)填充 UI 控件。

2. 發(fā)送請求

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

當調(diào)用 add() 時,Volley 運行一個高速緩存處理線程和一個網(wǎng)絡分派線程池。當向隊列添加請求時,它會被緩存線程拾取并進行分類:如果請求可以從緩存中獲取服務,則緩存響應將在緩存線程上進行解析,并將解析后的響應在主線程上傳遞。如果無法從緩存中為請求提供服務,則將其置于網(wǎng)絡隊列中。第一個可用的網(wǎng)絡線程從隊列中獲取請求,執(zhí)行 HTTP 事務,在子線程上解析響應,然后將響應寫入緩存,并將解析的響應發(fā)送回主線程來進行傳遞。

可以在任意線程中添加請求,但響應始終在主線程上傳遞。

請求的生命周期

3. 取消請求

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

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

以下是使用字符串標記的示例:

  1. 定義標記并將其添加到你的請求中。
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() 方法中,取消所有具有此標記的請求。
@Override
protected void onStop () {
    super.onStop();
    if (mRequestQueue != null) {
        mRequestQueue.cancelAll(TAG);
    }
}

取消請求時要注意,該請求的響應是否是必要的。

三、設(shè)置 RequestQueue

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

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

BasicNetwork 是 Volley 的默認網(wǎng)絡實現(xiàn)。BasicNetwork 必須被用來連接到網(wǎng)絡的 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,并在得到響應后調(diào)用 stop() 來實現(xiàn)單次請求。

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

2. 使用單例模式

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

一個關(guān)鍵概念是 RequestQueue 必須使用 Application 上下文進行實例化,而不是 Activity 上下文。這樣可以確保 RequestQueue在應用的生命周期內(nèi)持續(xù)使用,而不是每次重新創(chuàng)建 Activtiy 時重新實例化(例如當用戶旋轉(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);

四、提出標準請求

Volley 支持的常見請求類型:

  • StringRequest。指定 URL 并接收原始字符串作為響應。

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

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

1. 請求 JSON

Volley 為 JSON 請求提供以下類:

  • JsonArrayRequest - 使用給定的 URL 獲取 JSONArray 響應體。

  • JsonObjectRequest - 使用給定的 URL 獲取 JSONObject 響應體,允許將 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),如果響應是字符串、圖像或 JSON,則不需要實現(xiàn)自定義請求。

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

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

  • 實現(xiàn)抽象方法,parseNetworkResponse()deliverResponse()

1.1 parseNetworkResponse

對于給定類型(例如字符串,圖像或 JSON),Response 封裝解析的響應用來傳遞。以下是一個示例實現(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ù) ,其中包含響應有效負載作為 byte []、HTTP 狀態(tài)代碼和響應頭。

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

如果你的協(xié)議具有非標準緩存語義,你可以自己構(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 自動使用響應數(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)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

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