上一篇中我們學習了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的基本使用步驟是:
構建一個RequestQueue。
創建一個Request,Volley提供的Request有:StringRequest、JsonObjectRequest、JsonArrayRequest、ImageRequest,這四種。
將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地址