Volley是google官方推出的一個開源網絡訪問庫,在項目中直接使用它,而不需要寫一大堆的重復的代碼;
項目主頁:
https://android.googlesource.com/platform/frameworks/volley
Volley中一個重要的類就是RequestQueue,不用多說,只看命名就知道它是一個請求隊列,用於存放我們添加進去的網絡請求;
我們可以通過Volley.newRequestQueue(Context context);獲取一個RequestQueue對象,通常一個應用程序中使用一個RequestQueue實例即可,因此可以把RequestQueue實例存放在Application中,當做一個全局變量;
下面直接通過例子來學習此開源庫的使用;
一、字符串請求
protected void stringRequest() { String url = "http://www.baidu.com"; StringRequest sr = new StringRequest(Method.GET, url, new Listener<String>() { @Override public void onResponse(String response) { Log.e(tag, "" + response); } }, new ErrorListener() { @Override public void onErrorResponse(VolleyError error) { } }); sr.setTag("tag"); reqQueue.add(sr); }
其中reqQueue即為RequestQueue實例對象(同下文);
我們只需要產生一個StringRequest實例,然後添加進RequestQueue隊列當中即可,在創建實例的時候,會分別設置請求成功和請求失敗時的監聽回調,在監聽器裡面我們可以做相應的處理;
二、Json請求
Json請求分別兩種,分別是JsonObjectRequest和JsonArrayRequest,下邊我們來看一個JsonObjectRequest請求;
protected void jsonObjectRequest() { String url = "http://m.weather.com.cn/data/101010100.html"; JsonObjectRequest jor = new JsonObjectRequest(Request.Method.GET, url, null, new Listener<JSONObject>() { @Override public void onResponse(JSONObject response) { Log.e(tag, "" + response); } }, new ErrorListener() { @Override public void onErrorResponse(VolleyError error) { } }); jor.setTag("tag"); reqQueue.add(jor); }
和StringRequest相仿,不再表述,同理JsonArrayRequest也是一樣;
三、圖片請求
protected void imageRequest() { iv = (ImageView) findViewById(R.id.iv); String url = "http://img0.bdstatic.com/img/image/shouye/hjxnzz04.jpg"; ImageRequest ir = new ImageRequest(url, new Listener<Bitmap>() { @Override public void onResponse(Bitmap response) { iv.setImageBitmap(response); } }, 0, 0, Config.RGB_565, new ErrorListener() { @Override public void onErrorResponse(VolleyError error) { iv.setImageResource(R.drawable.ic_launcher); } }); ir.setTag("tag"); reqQueue.add(ir); // ImageRequest的構造函數接收六個參數 // url:圖片的URL地址 // listener:圖片請求成功的回調 // maxWidth:允許圖片最大的寬度,指定的網絡圖片的寬度或高度大於這裡的最大值,則會對圖片進行壓縮,指定成0的話就表示不管圖片有多大,都不會進行壓縮. // maxHeight:允許圖片最大的高度 // decodeConfig:圖片的顏色屬性 // errorListener:圖片請求失敗的回調 }
ImageRequest和StringRequest差不多,不過可以通過設置maxWidth和maxHeight對圖片進行壓縮;
在上面這個請求中,我們只把請求回來的圖片直接設置給了ImageView,並沒有做其它處理,是否有點不嚴謹,內存溢出了咋辦?請看下面!
四、ImageLoader
protected void imageLoader() { iv = (ImageView) findViewById(R.id.iv); String url = "http://img0.bdstatic.com/img/image/shouye/hjxnzz04.jpg"; ImageLoader loader = new ImageLoader(reqQueue, new BitmapCache()); ImageListener listener = new ImageListener() { @Override public void onErrorResponse(VolleyError error) { } @Override public void onResponse(ImageContainer response, boolean isImmediate) { } }; // listener = ImageLoader.getImageListener(iv, R.drawable.ic_launcher, // R.drawable.ic_launcher); loader.get(url, listener, 0, 0); // ImageLoader明顯要比ImageRequest更加高效,它不僅可以幫我們對圖片進行緩存,還可以過濾掉重復的鏈接,避免重復發送請求. // 它的內部也是使用ImageRequest來實現的; }
ImageLoader功能就要強大一些,它可以對圖片進行緩存,其實它內部還是通過ImageRequest來實現的;通過ImageLoader.get(url,listener,maxWidth,maxHeight)自動獲取圖片;如果需要詳細的監聽圖片的獲取過程,則new一個ImageListener實例,如果不需要,則通過ImageLoader.getImageListener(int defaultResouceId,int errorResourceId);來設置默認的圖片和請求失敗時的圖片;
在創建ImageLoader實例的時候,需要一個ImageCache對象,我在這裡簡單自定義了一個ImageCache對象用於緩存圖片;
public class BitmapCache implements ImageCache { public static int getDefaultLruCacheSize() { final int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024); final int cacheSize = maxMemory / 8; return cacheSize; } private final String tag = "BitmapCache"; private LruCache<String, Bitmap> mCache; public BitmapCache() { int maxSize = getDefaultLruCacheSize(); 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) { Log.e(tag, "getBitmap"); return mCache.get(url); } @Override public void putBitmap(String url, Bitmap bitmap) { Log.e(tag, "putBitmap"); mCache.put(url, bitmap); } }
五、NetworkImageView
protected void networkImageView() { String url = "http://img0.bdstatic.com/img/image/shouye/hjxnzz04.jpg"; NetworkImageView niv = ...; niv.setErrorImageResId(R.drawable.ic_launcher); niv.setDefaultImageResId(R.drawable.ic_launcher); ImageLoader loader = new ImageLoader(reqQueue, new BitmapCache()); niv.setImageUrl(url, loader); // NetworkImageView是一個自定義控制,它是繼承自ImageView的,在原生的基礎之上加入了加載網絡圖片的功能 // 使用ImageRequest和ImageLoader這兩種方式來加載網絡圖片,都可以傳入一個最大寬度和高度的參數來對圖片進行壓縮, // 但NetworkImageView並不需要提供任何設置最大寬高的方法也能夠對加載的圖片進行壓縮. // 這是由於NetworkImageView是一個控件,在加載圖片的時候它會自動獲取自身的寬高,然後對比網絡圖片的寬度,再決定是否需要對圖片進行壓縮. // 也就是說,壓縮過程是在內部完全自動化的,並不需要我們關心,NetworkImageView會始終呈現給我們一張大小剛剛好的網絡圖片. }
NetworkImageView繼承處ImageView,它的使用更加簡單,它的一些特性上面已經列出,使用的時候直接把原始的ImageView替換即可;
六、補充
上面講了幾個常用的類的使用,下面對一些設置進行補充記錄一下;
上面的網絡請求都是最基本的請求,並沒有帶特定的參數,那麼問題來了,如果我需要設定一些請求參數怎麼辦?請看下面!
Request在發出請求的時候,會調用兩個方法getParams和getHeaders,分別讀取請求時附帶的參數和請求頭,我們重寫這兩方法,把需要設置的參數給寫進去即可,下面是一個簡單的例子;
StringRequest sr = new StringRequest(Method.GET, url, new Listener<String>() { @Override public void onResponse(String response) { Log.e(tag, "" + response); } }, new ErrorListener() { @Override public void onErrorResponse(VolleyError error) { } }) { @Override protected Map<String, String> getParams() throws AuthFailureError { Map<String, String> map = new HashMap<String, String>(); map.put("param1", "value1"); map.put("param2", "value2"); return map; } @Override public Map<String, String> getHeaders() throws AuthFailureError { Map<String, String> map = new HashMap<String, String>(); map.put("Content-Type", "application/json"); return map; } };
七、管理緩存
<1>.RequestQueue默認會把數據緩存到本地,比如一張圖片,一些相關方法參考DiskBasedCache類;但有時我們不需要對數據進行緩存,只需要在添加請求的時候通過Request.setShouldCache(false)即可;
<2>.查看緩存:
RequestQueue reqQueue = ...;
Cache cache = reqQueue.getCache(); Entry entry = cache.get(url); if(entry != null){ try { String data = new String(entry.data, "UTF-8"); // handle data, like converting it to xml, json, bitmap etc., } catch (UnsupportedEncodingException e) { e.printStackTrace(); } }else{ // Cached response doesn't exists. Make network call here }
Cache還提供了其它相關的方法,比如remove{刪除},invalidate{不會刪除緩存的數據,而是使其失效,當網絡請求成功獲取到新數據時,會覆蓋掉之前的數據},clear{清空};
除了使用Cache.clear()方法清空緩存,還有另外一種方法來請空緩存:
protected void clearCache() { ClearCacheRequest ccr = new ClearCacheRequest(reqQueue.getCache(), new Runnable() { @Override public void run() { } }); ccr.setTag(this); reqQueue.add(ccr); }
八、取消請求
有時候,我們可能需要手動取消請求,比如,在一個請求還未完成時,程序即出了,如果再繼續請求沒有太大的意義,還有可能造成程序崩潰,通常這種情況我們在onStop方法中取消請求,下面就來看看如何取消請求;
RequestQueue reqQueue ...;
reqQueue.cancelAll(this); // reqQueue.cancelAll(new RequestFilter() { // @Override // public boolean apply(Request<?> request) { // return false; // } // });
九、自定義Request
先來看StringRequest的源碼實現
public class StringRequest extends Request<String> { private final 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); } @Override protected void deliverResponse(String response) { mListener.onResponse(response); } @Override protected Response<String> parseNetworkResponse(NetworkResponse response) { String parsed; try { parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers)); } catch (UnsupportedEncodingException e) { parsed = new String(response.data); } return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response)); } }
通過上面StringRequest的源碼,相信實現一個自定義Request對大家來說都沒有太大的難度,也就是重寫兩個方法,deliverResponse(僅僅一行代碼,有什麼難的?)和parseNetworkResponse,主要就是實現parseNetworkResponse,下面參照StringRequest來實現一個XmlRequest;
public class XmlRequest extends Request<XmlPullParser> { private Listener<XmlPullParser> listener; public XmlRequest(int method, String url, Listener<XmlPullParser> listener, ErrorListener errorlistener) { super(method, url, errorlistener); this.listener = listener; } public XmlRequest(String url, Listener<XmlPullParser> listener, ErrorListener errorlistener) { this(Method.GET, url, listener, errorlistener); } @Override protected Response<XmlPullParser> parseNetworkResponse( NetworkResponse response) { try { String str = new String(response.data, HttpHeaderParser.parseCharset(response.headers)); XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); XmlPullParser parser = factory.newPullParser(); parser.setInput(new StringReader(str)); return Response.success(parser, HttpHeaderParser.parseCacheHeaders(response)); } catch (Exception e) { return Response.error(new ParseError()); } } @Override protected void deliverResponse(XmlPullParser response) { listener.onResponse(response); } }
十、自定義Request2(GsonRequest)
對於上面的JsonObjectRequest,我們只是得到了JSONObject,��想Gson,我們是否也可以直接把獲取到的Json數據轉換為一個實體對象呢?當然可以,而且也非常簡單;
public class GsonRequest<T> extends Request<T> { private Listener<T> listener; private Gson gson; private Class<T> clazz; public GsonRequest(int method, String url, Class<T> clazz, Listener<T> listener, ErrorListener errorlistener) { super(method, url, errorlistener); this.listener = listener; this.clazz = clazz; gson = new Gson(); } public GsonRequest(String url, Class<T> clazz, Listener<T> listener, ErrorListener errorlistener) { this(Method.GET, url, clazz, listener, errorlistener); } @Override protected Response<T> parseNetworkResponse(NetworkResponse response) { try { String str = new String(response.data, HttpHeaderParser.parseCharset(response.headers)); return Response.success(gson.fromJson(str, clazz), HttpHeaderParser.parseCacheHeaders(response)); } catch (UnsupportedEncodingException e) { return Response.error(new ParseError(e)); } } @Override protected void deliverResponse(T response) { listener.onResponse(response); } }
完!
更多Android相關信息見Android 專題頁面 http://www.linuxidc.com/topicnews.aspx?tid=11