Volley传输网络数据
本文翻譯自Transmitting Network Data Using Volley
概述
Volley是一個HTTP的庫,能夠讓Android Apps的網絡請求更容易,更迅速。Vollery在github可以獲取到。
Volley有以下優點:
- 自動調度網絡請求
- 多個并發的網絡連接
- 標準的Http cache coherence
- 支持請求優先級
- 取消請求的API,可以取消簡單的請求,也可以取消一組網絡請求
- 可定制性,比如重試,重定向等
- 強大的排序,可以輕松地使用網絡請求的數據正確的填充到UI上。
- 調試和跟蹤的工具
Volley的優點在于使用RPC-type的操作來填充UI,比如獲取到一頁的搜索結果作為結構化的數據。它可以輕松地與任何協議集成,Volley自帶了字符串,圖片和JSON的支持。通過內嵌一些特性,Volley可以讓你從模板的代碼中擺脫出來,而只需關注App的特定的邏輯。
Volley不適合大型的下載或者流操作。因為Volley在解析的時候會把所有的響應保存到內存中,對于大型的下載操作,可以考慮使用DownloadManager。
Vollery核心庫是在github上開發的,主要有請求分發以及一系列的通用的工具類,可以在Volley的工具箱中獲取到。在項目中添加Volley最容易的方式是在gradle文件中添加以下內容。
dependencies {...compile 'com.android.volley:volley:1.0.0' }你也可以克隆Volley倉庫并且設置它作為一個library工程
通過下列的命令來克隆Volley的倉庫
git clone https://github.com/google/volley導入源碼到你的app工程里作為Android library,在Create an Android Library查看如何創建Android library庫。
發送簡單請求
更高層次上,可以創建一個RequestQueue傳遞給它Request對象。RequestQueue管理工作線程,這些工作線程用于運行網絡請求,讀寫緩存和解析網絡響應。Request解析原始的響應并且Volley分派解析后的響應返回到主線程中。
這節描述如何使用Volley.newRequestQueue方法發送請求,此方法設置一個RequestQueue供我們使用。看設置RequestQueue一節也可以了解如何自己設置RequestQueue。
這節描述如何使用RequestQueue去添加請求和取消請求。
添加網絡權限
使用Volley,必須在manifest文件中添加android.permission.INTERNET權限。
使用newRequestQueue
Volley提供了方便的方法Volley.newRequestQueue去創建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>() {@Overridepublic void onResponse(String response) {// Display the first 500 characters of the response string.mTextView.setText("Response is: "+ response.substring(0,500));} }, new Response.ErrorListener() {@Overridepublic void onErrorResponse(VolleyError error) {mTextView.setText("That didn't work!");} }); // Add the request to the RequestQueue. queue.add(stringRequest);Volley傳遞解析后的響應到主線程中。主線程中使用接受到的數據填充UI是很方便的,同時你也在響應監聽器中可以修改UI控件。
發送請求
為了發送request,需要簡單地構建一個Request并且使用add方法添加到RequestQueue,正如顯示的,一旦添加了Request,它將被傳遞,獲取服務,有它自己解析響應,最終傳遞結果。
當調用add方法的時候,Volley會運行一個緩存處理線程和網絡分發的線程。當添加請求到隊列中,請求就會被cache線程選取并且分類:如果請求能從cache中獲取,緩存的響應將會在cache線程中解析并把解析后的響應傳遞到主線程中。如果請求不能從cache中獲取,請求將被放置到網絡隊列里。首先網絡線程會從網絡隊列中獲取Request,執行HTTP傳輸,在工作線程中解析響應,把響應寫入到緩存,然后把解析后的響應傳遞到主線程中。
注意這些昂貴的開銷像阻塞的I/O和解析和解碼是在工作線程完成的。你可以在任意線程中添加Request,但是響應總是被傳遞到主線程中。
圖1展示了request的生命周期
取消Request
為了取消Request,調用cancel()方法。一旦Request被取消,Volley保證響應的handler不會被調用。這意味著在實踐中你能在Activity的onStop()方法取消所有的request并且也不需要在響應的Handler中有getActivity()==null的檢測,是否onSaveInstanceState()已經被調用了,或者其他的防御的編碼。
為了利用這個行為,需要做的就是跟蹤所有的request,能夠在恰當的時機取消它們。一個比較簡單的方法:可以給每個Request對象分配一個tag對象。你可以使用這個tag去支持取消一定范圍的Request。例如,可以使用activity類標記所有請求,然后在onStop()方法中調用requestQueue.cancelAll(this)。類似情景是可以在ViewPager的當前的Tab標記所有的縮略圖的請求,在滑動到新的Tab的時候然后取消request。
下面的例子使用字符串來添加tag
在你的activity的onStop()方法中,使用tag取消所有的Request
@Override protected void onStop () {super.onStop();if (mRequestQueue != null) {mRequestQueue.cancelAll(TAG);} }取消請求時請留意。如果你依賴于你的響應處理程序(也就是回調)來推進狀態或啟動另一個進程,則需要解決此問題。再次提醒,一旦Request被取消響應的處理代碼(也就是回調)將不被調用。
設置RequestQueue
前面的章節講解了如何使用Volley.newRequestQueue去設置一個默認行為的RequestQueue,這節將會帶你領略創建RequestQueue的真正的步驟,設置自定義的RequestQueue。
這節也描述了推薦的創建RequestQueue的方式單例,這樣可以保證RequestQueue與APP的生命周期一致。
設置Network和Cache
一個RequestQueue需要兩件事來完成它的工作:一個是network去執行Request的傳輸,和一個cache來處理緩存。在Volley的工具庫中有相關的標準實現:DiskBasedCache提供了 one-file-per-response的緩存帶著內存的索引,和BasicNetwork提供了網絡傳輸依據于你的首選的HTTP客戶端。
BasicNetwork是Volley的默認網絡實現。一個BasicNetwork必須被一個HTTP客戶端初始化。典型的是HttpURLConnection。(也可以使用OkHttp來實現)
下面的代碼展示了設置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>() {@Overridepublic void onResponse(String response) {// Do something with the response} },new Response.ErrorListener() {@Overridepublic void onErrorResponse(VolleyError error) {// Handle error} });// Add the request to the RequestQueue. mRequestQueue.add(stringRequest);// ...如果你只需要一次性的請求并且不想離開線程池,您可以在需要的地方創建RequestQueue,并在您的響應或錯誤返回后在RequestQueue上調用stop()。但是更常用的情景是創建RequestQueue作為單例并且保持RequestQueue在APP的生命周期中一直運行,下節將會講解。
使用單例模式
如果你的應用一直使用網絡,那或許需要設置單例的RequestQueue并且保證RequestQueue與在APP的生命周期一致。你可以通過各種方式實現。推薦的方法是實現單例類包裹著RequestQueue和其他的Volley功能。另一個方式實現Application的子類設置RequeueQueue在 Application.onCreate()。但是這種方式是不鼓勵的;靜態的單例可以更加模塊化的方式提供相同的功能。
一個關鍵的點是RequestQueue實例化時使用Appliction context而不是Activity context。確保RequestQueue在應用的生命周期中一直在運行,而不是每次隨著activity的創建一直被銷毀再創建。
下面的單例的例子提供了RequestQueue和ImageLoader的功能:
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);@Overridepublic Bitmap getBitmap(String url) {return cache.get(url);}@Overridepublic 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;} }下面的例子是使用單例的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并且在響應中接受到字符串,對于例子可以看設置RequeseQueue。
JsonObjectRequest和JsonArrayRequest(二者均為JsonRequest的子類):指定特定的URL并且會獲取到JSON或者JSON數據組。
如果你期望的響應是這些類型的一種,那不需要設置自定義的Request。這節將描述怎么去使用這些標準的請求類型,對于如何自定義Request請看下一節。
JSON Request
Volley為JSON請求提供了下面的類:
- JsonArrayRequest :一個Request給定URL,將會接受到JSONArray的響應body
- JsonObjectRequest:一個Request給定的URL,接受到JSONObject響應body,允許JSONObject作為請求body的一部分。
上述的兩個類均為JSONObject的子類。你使用下面的模板來使用其他的JSON類。例如,下面的代碼片段描述了獲取JSON并展示到UI上。
TextView mTxtDisplay; ImageView mImageView; mTxtDisplay = (TextView) findViewById(R.id.txtDisplay); String url = "http://my-json-feed";JsonObjectRequest jsObjRequest = new JsonObjectRequest(Request.Method.GET, url, null, new Response.Listener<JSONObject>() {@Overridepublic void onResponse(JSONObject response) {mTxtDisplay.setText("Response: " + response.toString());} }, new Response.ErrorListener() {@Overridepublic void onErrorResponse(VolleyError error) {// TODO Auto-generated method stub} });// Access the RequestQueue through yozur singleton class. MySingleton.getInstance(this).addToRequestQueue(jsObjRequest);對于實現自定義的Request依賴于Gson, 可以看下節。
實現自定義的Request
本節將描述怎么實現自定義的Request類型。
自定義Request
在工具類中需要的Request都可以拿來即用;如果你的響應的數據類型是String,Image,或者JSON,那不必實現自定義的Request。
對于實現自定義的Request你僅僅需要實現下面的步驟:
- 繼承Request類,為參數化表示期望解析的響應的類型。因此如果希望解析的響應是一個字符串就需要繼承Request。可以查看類StringRequest和ImageRequest作為繼承Request的例子。
- 實現parseNetworkResponse() 和 deliverResponse()抽象方法,下面有更多細節。
parseNetworkResponse方法
Response封裝了給定類型解析后的響應,(比如String,image或者JSON)。下面是一個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 ... }注意:
- NetworkResponse作為parseNetworkResponse()的參數,NetworkResponse包含響應作為byte[]類型,HTTP 狀態碼,和響應頭。
- 實現必須返回一個Response,它包含了實現的響應對象的類型和緩存原始數據或者在解析失敗的情況下的錯誤。
如果你的協議包含非標準的語義,你可以構建自己的Cache.Entry,但是大多數請求都是這樣的:
return Response.success(myDecodedObject,HttpHeaderParser.parseCacheHeaders(response));Volley調用parseNetworkResponse()方法是在工作線程中。這個確保了耗時的操作不會阻塞主線程,比如解析JPEG成Bitmap。
deliverResponse方法
Volley返回parseNetworkResponse()的對象傳遞到主線程。大多數的請求會在這里調用回調接口,例如:
protected void deliverResponse(T response) {listener.onResponse(response); }例如GsonRequest
Gson是一個使用反射技術把java對象轉化成JSON,或者JSON轉化為Java對象的庫。你可以定義java對象與它對應的JSON的key有相同的名字。傳遞Gson對象,并且Gson將會填充對象的屬性。下面是完整的Volley的Request實現。
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;}@Overridepublic Map<String, String> getHeaders() throws AuthFailureError {return headers != null ? headers : super.getHeaders();}@Overrideprotected void deliverResponse(T response) {listener.onResponse(response);}@Overrideprotected 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));}} }Volley提供了方便的類JsonArrayReques和JsonArrayObject。可以看標準請求一節來查看更多信息。
總結
以上是生活随笔為你收集整理的Volley传输网络数据的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 物联网之窄带物联网(NB-IOT)
- 下一篇: 【STM32+ESP8266连接腾讯云物