Android應用開發:網絡工具——Volley(一)

引言


網絡一直是我我的的盲點,前一陣子抽空學習了一下Volley網絡工具的用法,也透過源代碼進行了進一步的學習,有一些心得想分享出來。在Android開發中,成熟的網絡工具很多,Android自帶了HttpClient。還有okhttp,還有koush大神建立的ion開源項目,而後就是google後來增長到Android項目源代碼中的Volley。html

爲何使用Volley,是因爲Volley使用簡單,邏輯清晰,即便在調試過程當中出現了問題。也可以高速的經過源代碼進行定位。java


Volley編譯


因爲已經習慣了使用Gradle構架應用。因此我在第一次想要使用Volley的時候嘗試尋找可否夠經過gradle的配置文件進行庫依賴。惋惜的是,並無。但即便這樣Volley的庫也很是easy作出來增長到咱們的project中。android


首先需要ant編譯工具,而後假設有Android系統源代碼的話,Volley在frameworks/volley文件夾下。git

假設沒有Android源代碼,也很是好辦,可以單獨從Android的倉儲中克隆出Volley源代碼:github

git clone https://android.googlesource.com/platform/frameworks/volley
不幸的是,volley庫的源代碼Android並無託管在其在Github的賬號上,因此僅僅能在googlesource上進行克隆,固然在國內也就需要先FQ才幹夠了。

下圖爲Volley源代碼結構:數據庫


克隆成功後。可以方便的使用ant進行編譯,固然,假設是在完整的Android源代碼下,也可以直接經過make進行編譯,但是時間一定會長很是多。這裏使用ant編譯爲例,運行:json

ant jar
結果如圖所看到的:


這樣jar包就生成了,很是方便吧,接下來將其加入到project中就可以使用了。

api

Volley使用


Volley的網絡請求父類爲Request<T>。可以提供給開發人員進行繼承。同一時候也預置了幾種開發中常用的請求類型。下邊介紹兩個:StringRequest和JsonObjectRequest。cookie

爲了更加貼近實際使用,下邊將使用Volley與Cloudant進行通信作演示樣例。網絡

Cloudant是一家提供雲服務業務的公司。其向開發人員提供免費的雲存儲、雲數據庫服務。關於其註冊等流程本文不作敘述,很是easy的。

直接從登陸開始:


1. 申請網絡請求隊列

Volley的一個很是大的特點。就是所有的網絡請求無需開發人員本身運行,而是在請求構造完畢後扔到Volley的請求隊列中,隊列依次進行請求,這樣就省去了很是多麻煩。開發人員也不用操心網絡請求是否會衝突。是否會在主線程,這些煩心事Volley的網絡隊列都幫咱們攻克了。

通常來講,一個應用程序假設網絡請求沒有特別頻繁則全然可以僅僅有一個請求隊列(相應Application),假設許多或其它狀況,則可以是一個Activity相應一個網絡請求隊列,詳細狀況詳細分析。

下邊的代碼展現了怎樣申請一個Volley網絡請求隊列:

RequestQueue mQueue;
mQueue = Volley.newRequestQueue(getApplicationContext());

這樣就成功申請了一個網絡請求隊列,假設僅僅有一個,則可以在Application中進行申請。


2. 使用Volley登陸Cloudant

若是已經成功註冊。登陸名foo。passwordbar。

經過查閱Cloudant的登陸認證文檔:https://docs.cloudant.com/api/authn.html。

可以發現Cloudant登陸認證相關接口有三個:


這裏咱們使用POST方法進行cookie登陸認證。結合上邊若是的username和password可知:

要訪問的url爲 foo.cloudant.com/_session
頭信息爲 Content-Type: application/x-www-form-urlencoded
參數爲 name = foo, password = bar

若訪問成功,咱們就可以在網絡迴應中獲取cookie,以備以後其它操做使用。顯然,這個請求跟json毫無關係。應該使用StringRequest。StringRequest有兩種構造方法:

public StringRequest(int method, String url, Listener<String> listener, ErrorListener errorListener)

public StringRequest(String url, Listener<String> listener, ErrorListener errorListener)
第二個方法僅僅有GET請求才幹夠使用,第一個方法的method參數可以用來本身定義請求類型,這裏咱們需要的是POST,因此應該使用第一個構造方法:

StringRequest request = new StringRequest(
                Request.Method.POST,
                "http://foo.cloudant.com/_session",
                new Response.Listener<String>() {
                    @Override
                    public void onResponse(String s) {  //收到成功應答後會觸發這裏

                    }
                },
                new Response.ErrorListener() {
                    @Override
                    public void onErrorResponse(VolleyError volleyError) { //出現鏈接錯誤會觸發這裏
                    }
                }
        );

上邊的代碼中,咱們成功構造了一個StringRequest,當中已經包括了咱們需要的POST和正確的URL,同一時候還加入了網絡迴應監聽器。但是,還缺乏文檔要求咱們的頭信息和參數。StringRequest在構造中並不提供這些信息的定義,這也是與其它常用網絡工具不一樣的地方,剛接觸的同窗可能會很是不適用,經過複寫StringRequest的兩個方法就可以將這些信息放進去了。下邊來無缺這個請求:

StringRequest request = new StringRequest(
                Request.Method.POST,
                "http://foo.cloudant.com/_session",
                new Response.Listener<String>() {
                    @Override
                    public void onResponse(String s) {

                    }
                },
                new Response.ErrorListener() {
                    @Override
                    public void onErrorResponse(VolleyError volleyError) {

                    }
                }
        ) {
            @Override
            public Map<String, String> getHeaders() throws AuthFailureError {  //設置頭信息
                Map<String, String> map = new HashMap<String, String>();
                map.put("Content-Type", "application/x-www-form-urldecoded");
                return map;
            }

            @Override
            protected Map<String, String> getParams() throws AuthFailureError {  //設置參數
                Map<String, String> map = new HashMap<String, String>();
                map.put("name", "foo");
                map.put("password", "bar");
                return map;
            }
        };

相比第一次咱們的構造過程,這一次多了兩個複寫的方法來設置頭信息和參數,很是easy吧。這個時候請求基本完畢了,但是卻缺乏還有一個很是重要的東西。咱們的登陸認證爲的是拿回屬於本身的cookie,假設不能獲取cookie的話,多麼正確的請求格式都是白費力氣啊。想要拿到cookie同樣也是經過複寫還有一個方法進行獲取:

            @Override
            protected Response<String> parseNetworkResponse(NetworkResponse response) {
                for (String s : response.headers.keySet()) {
                    if (s.contains("Set-Cookie")) {
                        mCookie = response.headers.get(s);
                        break;
                    }
                }
                return super.parseNetworkResponse(response);
            }
在網絡請求成功後。服務端返回應答信息。而咱們所需的Cookie信息就在這些應答信息中,經過相應答信息的遍歷查找。很是方便就可以找到咱們所需的信息了。

到這裏,咱們的登陸認證請求就構造完畢了,最後需要作的就是將這個StringRequest扔到咱們的請求隊列中去:

mQueue.add(request);
網絡通暢的狀況下。很是快就行獲取Cookie信息了。


3. 查看測試文檔

在註冊Cloudant成功後,Cloudant會在咱們的賬號中建立一個默認數據庫——crud,當中保存着一行測試數據welcome。


讓咱們用Volley來訪問這條數據。查閱Cloudant API文檔Documents相關可以發現:


經過簡單的GET請求搭配正確的URL就能夠獲得文件(數據)內容。固然。這一切的前提是咱們已經掌握了正確的Cookie數據。那麼,咱們需要:

1. 請求頭數據中包括正確的Cookie信息
2. 訪問正確的URL
3. 請求類型:GET
若是經過上一步登錄認證後咱們將Cookie信息保存在了mCookie字符串變量中。

而咱們需要訪問的URL經過查閱文檔也可以得出路徑爲 數據庫名 + 文檔名,即foo.cloudant.com/crud/welcome。

萬事俱備。使用StringRequest:

        StringRequest request = new StringRequest(
                "http://foo.cloudant.com/crud/welcome",
                new Response.Listener<String>() {
                    @Override
                    public void onResponse(String s) {

                    }
                },
                new Response.ErrorListener() {
                    @Override
                    public void onErrorResponse(VolleyError volleyError) {

                    }
                }
        ) {
            @Override
            public Map<String, String> getHeaders() throws AuthFailureError {
                Map<String, String> map = new HashMap<String, String>();
                map.put("Cookie", mCookie);
                return map;
            }
        };
        mQueue.add(request);

在onResponse中咱們會收到welcome這條數據的json形式字符串:

簡單的網絡請求StringRequest全然處理得來。使用也比較簡單,就介紹到這裏。

下邊介紹JsonObjectRequest應用方法。


4. 使用JsonObjectRequest建立新數據


首先看一下JsonObjectRequest的構造方法:
public JsonObjectRequest(int method, String url, JSONObject jsonRequest, Listener<JSONObject> listener, ErrorListener errorListener)

public JsonObjectRequest(String url, JSONObject jsonRequest, Listener<JSONObject> listener, ErrorListener errorListener)
第一種方法參數以此爲:請求方法,訪問的URL,Json數據對象,請求成功監聽器,請求失敗監聽器。
另一種構造方法中。若jsonRequest爲空,則方法本身主動爲GET。不爲空則本身主動切換爲POST。其它參數含義一樣。

Cloudant的文檔( https://docs.cloudant.com/api/documents.html )要求建立文檔可以使用POST或PUT方法進行。所攜帶的數據均爲json格式。這樣以來,StringRequest就顯得力不從心了,咱們需要使用到Volley的還有一個自帶請求類型:JsonObjectRequest。

下邊以POST方式建立數據爲例,經過查看Cloudant文檔,可知:

1. 訪問的URL path爲數據庫文件夾
2. Content-Type被要求爲application/json
3. 攜帶的數據要求爲json數據

既然方法要求爲POST,咱們又是建立數據。確定數據內容不會爲空,因此咱們選擇另一種構造方法。首先,建立一個Json對象:

        JSONObject jsonObject = new JSONObject();
        jsonObject.put("_id", "testinfo");
        jsonObject.put("person", "foo");
        jsonObject.put("phone", "bar");

在Cloudant數據存儲系統中,id可以由開發人員指定。接下來進行JsonObjectRequest的構造和請求:

        JsonObjectRequest request = new JsonObjectRequest(
                "http://foo.cloudant.com/crud",
                jsonObject,
                new Response.Listener<JSONObject>() {
                    @Override
                    public void onResponse(JSONObject jsonObject) {

                    }
                },
                new Response.ErrorListener() {
                    @Override
                    public void onErrorResponse(VolleyError volleyError) {

                    }
                }
        ) {
            @Override
            public Map<String, String> getHeaders() throws AuthFailureError {
                Map<String, String> map = new HashMap<String, String>();
                map.put("Cookie", mCookie);
                return map;
            }
        };
        mQueue.add(request);

jsonObject數據不爲空,因此請求方式本身主動切換爲POST,url爲所要建立數據所在的數據庫所在路徑。而後就是請求結果的監聽器,最後別忘了將Cookie帶上。不然會出現認證錯誤的。

最後。將構造完畢的請求丟進隊列中。由Volley進行調度處理。這個時候最好仍是再回頭看一看以前分析的請求所需要哪些元素,不難發現。Volley的json請求中。並無對Content-Type進行特殊設定。JsonObjectRequest是繼承於JsonRequest的。而JsonRequest已經幫咱們完畢了這個動做:

    @Override
    public String getBodyContentType() {
        return PROTOCOL_CONTENT_TYPE;
    }

PS:設置Content-Type也可以經過複寫getBodyContentType這個函數,而不用老是麻煩的使用getHeader中的map進行設定,兩種設定方式效果一致。而且也不用操心編碼格式。因爲默認就是utf-8的:

    /** Charset for request. */
    private static final String PROTOCOL_CHARSET = "utf-8";

    /** Content type for request. */
    private static final String PROTOCOL_CONTENT_TYPE =
        String.format("application/json; charset=%s", PROTOCOL_CHARSET);

到這裏,Json請求的相關使用方法也就介紹完了。下一節將會從源代碼角度分析一下Volley請求的邏輯順序究竟是如何的,假設咱們需要書寫本身的請求類型,都需要複寫哪些函數。以及需要注意些什麼。


源代碼


關於Volley和Cloudant不少其它的通訊細節,見CloudantVolley項目:https://github.com/airk000/CloudantVolley

相關文章
相關標籤/搜索