Android 網路程式設計系列(5)Volley 網路框架入門

顧小魚發表於2017-11-16

前言

上篇文章中我們對 HttpUrlConnection 的相關用法稍作介紹,可以看到如果不對它進行封裝,那麼每次使用時就必須寫很多重複的程式碼,並且需要自己建立執行緒進行網路連線,獲取到響應結果後還需要切換回主執行緒來更新 UI。這樣的過程已經足夠麻煩,如果在獲取網路圖片或者進行檔案下載的場景時,更是需要藉助 AsyncTask 等進行實現,這樣繁雜的步驟顯然是不利於開發的。

Google 在 2013 年的 IO 大會上針對 Android 中缺少一個功能強大、體驗良好的網路載入類這個問題,推出了我們今天介紹的主角——Volley。Volley 框架可以說是將AsyncHttpClient、Universal-Image-Loader 等第三方網路載入框架和圖片載入框架的優點集於一身。既將網路載入功能封裝的非常簡單,又能夠實現非常複雜的網路載入任務,比如圖片壓縮和快取。Volley 的含義翻譯過來是萬箭齊發,它非常適合於處理那些資料量不大但通訊極為頻繁的網路請求任務,但在涉及通訊資料量很大的任務中(如檔案下載),Volley 的表現就要差很多。下圖是 IO 大會上介紹 Volley 時配圖:

pic
pic

匯入 Volley

Volley 並不在 Android SDK 中,所以如果要使用 Volley 就必須在專案中匯入 Volley 這個庫。Volley 沒有官方的Maven repository 或者 Jcenter repository,所以必須依賴於 Volley 的原始碼。

在專案中匯入 Volley 主要有這樣幾種方式:

1. 下載 volley 的原始碼並將其作為專案的一個 module。

在 Android Studio 中我們可以將第三方的程式碼作為我們專案工程的一個 module。這個做法是最合適的,不僅可以直接在 IDE 中檢視 Volley 的原始碼,還可以在原始碼基礎是上進行修改。

第一步,下載 Volley 原始碼,使用 Git 可以輕鬆實現這一步:

git clone https://github.com/google/volley.git複製程式碼

第二步:在 Android Studio 中選擇「File」→「New」→「Import Module」,然後選擇下載好的 Volley 資料夾。這樣就匯入了 Volley 的原始碼作為我們專案中的一個 module。

第三步:選擇「File」→「Project Structure」,將 Volley module 作為我們 app module 的依賴。

dependent
dependent

經過上述三步,就可以順利地在我們自己的程式碼中使用 Volley 的功能了。

2. 在依賴列表中新增如下一段依賴:

compile 'com.mcxiaoke.volley:library-aar:1.0.15'複製程式碼

這是官方 repository 的一個映象,最簡單直接,但不被官方所支援。

3. 將打包好的 jar 檔案新增到專案 libs 目錄下。

點選這裡下載 CSDN 上打包好的 jar 檔案。

Volley 基礎用法

1. StringRequest 的使用

在專案中成功匯入 Volley 後,下面我們就來進入正題,學習一下 Volley 的相關用法。Volley 的總體設計思路是基於一個 RequestQueue(請求佇列),開發者只需要建立適合應用場景的 Request,並將其新增到 RequestQueue 即可實現網路請求。

結合這個設計思路,我們很快就可以提取出 Volley 通用的使用步驟:

  1. 建立 RequestQueue 物件。
  2. 建立 Request 物件,通常是 Request 的子類物件。
  3. 將 Request 物件新增到 RequestQueue 中。

首先來看看 StringRequest 的使用。它是 Request 的一個子類,顧名思義這個 Request 會返回一段字串型別的響應資料。

我們按照上述的三個步驟來進行說明。

建立 RequestQueue 物件

使用 Volley.newRequestQueue() 方法建立 RequestQueue 物件,傳入當前的上下文(Context)。

RequestQueue mQueue = Volley.newRequestQueue(this);複製程式碼

RequestQueue 可以快取所有的 Http 請求,並且適合高併發的網路請求,所以不需要為每個 Http 請求都建立一個 RequestQueue 物件,通常在一個 Activity 中建立一個全域性的 RequestQueue 物件,甚至如果你的應用程式網路功能很少時,整個應用中只需有一個 RequestQueue 物件即可(對應 Application)。

建立 StringRequest 物件

接下來需要我們指定具體請求的內容,所以我們新建一個 StringRequest 物件,如以下程式碼所示:

//獲取控制元件
TextView tvCode = (TextView) findViewById(R.id.string_code);
//請求的 URL 地址
String url = "http://lixiaoyu.cc";
//建立 StringRequest 物件
StringRequest request = new StringRequest(url, new Response.Listener<String>() {
    @Override
    public void onResponse(String response) {
        tvCode.setText(response);
    }
}, new Response.ErrorListener() {
    @Override
    public void onErrorResponse(VolleyError error) {
        error.printStackTrace();
        tvCode.setText("出錯了");
    }
});複製程式碼

StringRequest 共有兩個構造方法,一個有 3 個引數,用於預設以 GET 方式傳送網路請求,這 3 個引數分別是:

String url:請求的網址
Listener< String> listener:響應成功的回撥函式
ErrorListener errorListener:響應失敗的回撥函式

例如上面程式碼中,網址為我的個人部落格網站的 URL,響應成功時直接將該頁面的 HTML 程式碼顯示在 TextView 中,響應失敗則在 TextView 顯示一段錯誤提示。(上述兩個回撥函式都是直接在主執行緒中執行,所以不需要我們自己去切換執行緒,太方便了)

如果需要使用 POST 方法向網頁提交資料,就需要使用 4 個引數的構造方法。引數分別為:

int method:網路請求方式,取值可以是 Request.Method.POST,Request.Method.HEAD 等
String url
Listener listener
ErrorListener errorListener

後 3 個引數與上面都是一致的。使用 POST 時,我們需要將資料傳送給伺服器,Volley 並沒有設計在構造方法中加入類似 Params 這種引數,但是 Volley 在處理 POST 請求時,會呼叫 StringRequest 的父類 Request 的 getParams() 方法,我們可以在這個方法中加入想要提交到伺服器的資料。如以下程式碼所示:

//獲取控制元件
TextView tvCode = (TextView) findViewById(R.id.string_code);
//請求的 URL 地址
String url = "http://ip.taobao.com/service/getIpInfo.php";
StringRequest request = new StringRequest(Request.Method.POST, url,
                new Response.Listener<String>() {
            @Override
            public void onResponse(String response) {
                tvCode.setText(response);
            }
        },
        new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                tvCode.setText("出錯了");
            }
        }){
    @Override
    protected Map<String, String> getParams() throws AuthFailureError {
        Map<String, String> map = new HashMap<>();
        map.put("ip","192.168.0.188");
        return map;
    }
};複製程式碼

在程式碼中,首先指定構造方法的第一個引數為 POST,然後其他引數的設定與之前一致。覆蓋父類中的 getParams() 方法,通過 Map 構造想要提交的資料,最後返回給 Request。

將 StringRequest 物件新增到 RequestQueue 中

最後一步就是將 Request 物件新增到 RequestQueue 中,RequestQueue 就能自動處理這個請求,並將相應結果傳送到請求的回撥函式中。如以下程式碼所示:

mQueue.add(request);複製程式碼

2. JsonRequest 的使用

網路請求的相應結果通常是 XML 格式和 Json 格式,在 Android 開發中很多時候都是 Json 格式。Volley 中定義了 JsonRequest 來方便、快速地實現響應結果為 Json 的網路請求。JsonRequest 是一個抽象類,有兩個子類,JsonObjectRequest 和 JsonArrayRequest,返回結果分別是 JsonObject 物件和 JsonArray 物件。

JsonRequest 的使用方式和 StringRequest 有著高度的一致性,我們以 JsonObjectRequest 為例(JsonArrayRequest 用法完全一樣):

/**
 * 1.建立全域性的 RequestQueue,為了顯示方便寫在一塊了
 */
mQueue = Volley.newRequestQueue(this);
/**
 * 2.建立 JsonObjectRequest 物件,設定 URL,這裡使用了 Gank 的 API 介面
 * 和請求一起 POST 到伺服器的 JsonObject 物件,這裡設定為 null,
 * 響應成功或失敗的回撥函式
 */
String url = "http://gank.io/api/random/data/Android/2";
JsonObjectRequest request = new JsonObjectRequest(url, null,
                new Response.Listener<JSONObject>() {
    @Override
    public void onResponse(JSONObject response) {
        Log.i("TAG", "onResponse: " + response.toString());
        try {
            boolean isError = response.getBoolean("error");
            if (!isError) {
                JSONArray results = response.getJSONArray("results");
                JSONObject index = results.getJSONObject(0);
                String title = index.getString("desc");
                tvJson.setText(title);
            }
        } catch (JSONException e) {
            e.printStackTrace();
        }
   }
},new Response.ErrorListener() {
    @Override
    public void onErrorResponse(VolleyError error) {
        tvJson.setText("出錯了");
    }
});
/**
 * 3.將 JsonObjectRequest 物件新增到 RequestQueue 中
 */
mQueue.add(request);複製程式碼

通過上面的程式碼可以看到,使用步驟是完全一致的,只是在響應成功時回撥函式的泛型從 String 型別變成了 JsonObject 型別,我們在這個回撥函式中就可以對獲取到的 Json 資料進行處理,提取出想要的資訊。

使用 Volley 載入網路圖片

Volley 的另一個強大功能就體現在能夠非常方便地進行網路圖片的載入,並且能夠對圖片進行壓縮、快取等處理,不用擔心圖片過大時所導致的記憶體洩漏等問題。

使用 Volley 來載入網路圖片主要有三種方式,一是使用 ImageRequest,二是使用 ImageLoader,三是使用 NetworkImageView 這個自定義控制元件。下面依次來介紹一下。

使用 ImageRequest

由於載入網路圖片也是非常常見的網路請求之一,所以 Volley 中也封裝了專門用於圖片載入的 ImageRequest,它同樣是 Request 的子類,所以使用方法和上述的 StringRequest、JsonRequest 一模一樣。示例程式碼如下:

ImageView imageView = (ImageView) findViewById(R.id.image_img);
RequestQueue queue = Volley.newRequestQueue(this);
String imgUrl = "http://image.wufazhuce.com/FiBzmwDotMhGiJjSuqi1Lg5h_Zjm";
ImageRequest request = new ImageRequest(imgUrl, new Response.Listener<Bitmap>() {
    @Override
    public void onResponse(Bitmap response) {
        imageView.setImageBitmap(response);
    }
}, 0, 0, Bitmap.Config.RGB_565, new Response.ErrorListener() {
    @Override
    public void onErrorResponse(VolleyError error) {
        imageView.setImageResource(R.mipmap.ic_launcher);
    }
});
queue.add(request);複製程式碼

如果載入圖片成功就設定給 ImageView,如果載入失敗就在 ImageView 上顯示一張預設的圖片。ImageRequest 使用時需要注意其構造方法,它有兩個構造方法,一個有 6 個引數,一個有 7 個引數,直接看看最全引數列表:

String url:需要載入的圖片地址
Response.Listener< Bitmap> listener:響應成功的回撥,比如設定給 ImageView
int maxWidth:圖片的最大寬度
int maxHeight:圖片的最大高度,如果圖片的實際寬高超過設定的最大寬高值,會對圖片進行壓縮,以指定大小進行顯示,最大寬高均設定為 0 表示不進行任何壓縮。
ScaleType scaleType:圖片的縮放方式,這個引數可以省略,預設是以整體向中心縮放的方式
Config decodeConfig:圖片 Bitmap 的編碼方式,值為 Bitmap.Config 類中的常量,有 RGB_565, ARGB_8888等值。
Response.ErrorListener errorListener:響應失敗的回撥,比如給 ImageView 設定一張預設顯示的圖片

實際執行效果如下:

imgrequest
imgrequest

使用 ImageLoader

ImageLoader 內部同樣是用 ImageRequest 實現的,並且能夠在此基礎上實現圖片的快取,還可以過濾掉重複的連結,避免重複傳送請求,比 ImageRequest 更加高效。

ImageLoader 的用法就和前面提到的 Request 子類的用法有所不同。步驟主要有:

1. 建立一個 RequestQueue 物件。這步與上述一致,不用多說。

RequestQueue mQueue = Volley.newRequestQueue(this);複製程式碼

2. 建立一個 ImageLoader 物件。傳入 RequestQueue 物件和 ImageCache 物件作為引數,這裡先建立一個空的 ImageCache 例項。

ImageLoader loader = new ImageLoader(queue, new ImageLoader.ImageCache() {
    @Override
    public Bitmap getBitmap(String url) {
        return null;
    }

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

    }
});複製程式碼

3. 獲取一個 ImageListener 物件。使用 ImageLoader 的 getImageListener() 方法可以獲取到 ImageListener 物件,在引數中設定需要載入圖片的 ImageView,預設圖片和載入失敗顯示的圖片。

ImageView imageView = (ImageView) findViewById(R.id.image_img);

ImageLoader.ImageListener listener = ImageLoader.getImageListener(imageView,
            R.mipmap.ic_launcher,R.mipmap.ic_launcher_round);複製程式碼
  1. 呼叫 ImageLoader 的 get() 方法載入圖片,傳入網路圖片地址、ImageListener 物件以及最大寬高。
loader.get(imgUrl, listener,200,200);複製程式碼

實際執行效果如圖所示,和 ImageRequest 不同, ImageLoader 會先在 ImageView 上顯示設定的預設圖片,等圖片載入成功後進行替換,若載入失敗則顯示設定的失敗圖片。

loader
loader

我們這裡雖然實現了與 ImageRequest 相同效果的功能,但是並沒有將 ImageLoader 的優勢發揮出來,因為我們在建立 ImageLoader 時第二個引數是 ImageCache 的一個空實現。我們應該在這裡對圖片進行快取處理。

以下這段程式碼來自郭神的 Volley 文章,演示了使用 LruCache 來快取圖片,避免重複載入和記憶體洩漏。

public class BitmapCache implements ImageCache {  

    private LruCache<String, Bitmap> mCache;  

    public BitmapCache() {  
        int maxSize = 10 * 1024 * 1024;  
        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 imageLoader = new ImageLoader(mQueue, new BitmapCache());複製程式碼

使用 NetworkImageView

NetworkImageView 是 Volley 中的一個自定義控制元件,繼承自 ImageView,擁有 ImageView 的全部功能,並且實現了載入網路圖片的功能。它的用法也可以大致分為如下幾步:

1. 建立一個 RequestQueue 物件。
2. 建立一個 ImageLoader 物件。

以上兩步就不用多說了,和 ImageLoader 中是一樣的。

RequestQueue mQueue = Volley.newRequestQueue(this);
ImageLoader loader = new ImageLoader(queue, new ImageLoader.ImageCache() {
    @Override
    public Bitmap getBitmap(String url) {
        return null;
    }

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

    }
});複製程式碼

3. 在佈局檔案中申明 NetworkImageView 控制元件。

<com.android.volley.toolbox.NetworkImageView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_margin="20dp"
    android:id="@+id/networkiv"/>複製程式碼

4. 在 Activity 中對控制元件進行例項化。同時可以進行一些自定義的設定。

ImageView imageView = (NetworkImageView) findViewById(R.id.networkiv);

imageView.setDefaultImageResId(R.mipmap.ic_launcher);
        imageView.setErrorImageResId(R.mipmap.ic_launcher_round);複製程式碼

這裡我們同樣設定了預設顯示的圖片和載入失敗時顯示的圖片。

5. 呼叫NetworkImageView的setImageUrl() 方法載入圖片。將網路圖片地址和 ImageLoader 物件傳入即可。

String imgUrl = "http://image.wufazhuce.com/FiBzmwDotMhGiJjSuqi1Lg5h_Zjm";
imageView.setImageUrl(imgUrl, loader);複製程式碼

這裡有一個需要注意的地方是,在 ImageRequest 和 ImageLoader 中,我們都可以指定圖片的最大寬高,而在 NetworkImageView 中,並沒有相應的實現。這是因為 NetworkImageView 是一個控制元件,在申明控制元件時就會進行寬高的設定,NetworkImageView 會將載入的圖片寬高和控制元件寬高進行比較,如果超過就會進行壓縮,如果控制元件的寬高都指定為 wrap_content,則不會壓縮。

結語

這篇文章中比較詳細地介紹了 Volley 的來歷和基礎的用法,包括獲取普通的字串資料和 Json 格式的資料,以及如何使用 Volley 載入網路圖片。在有關圖片快取的部分,因為不瞭解 LruCache 的有關內容,所以引用了部分其他人的程式碼,以作示例。

謝謝,下篇文章見。

參考資料

郭霖:初識Volley的基本用法
blog.csdn.net/guolin_blog…
郭霖:使用Volley載入網路圖片
blog.csdn.net/guolin_blog…
劉望舒:Volley 用法全解析
liuwangshu.cn/application…
泡在網上的日子:Android 網路通訊框架Volley簡介
www.jcodecraeer.com/a/anzhuokai…
泡在網上的日子:網路請求庫Volley詳解
www.jcodecraeer.com/a/anzhuokai…

相關文章