從前在學校用的最多的網絡請求框架就是AsyncHttpClient,用久了發現這樣一個問題,就是代碼複用太難,基本上作不到,因此有一段時間又迴歸了HttpURLConnection和HttpClient,再後來又學習了OKHttp的使用,雖然這幾種網絡請求各有各的優點,可是原理基本上都是同樣的,在android6.0中Google已經不提供HttpClient的API了,因此從長遠的角度來考慮,推薦你們多用OKHttp,關於OKHttp的使用能夠參見OKHttp的簡單使用。除了上面說的這幾種通訊方式,Google在2013年(好早呀)的I/O大會上還推出了一個網絡請求框架Volley,這和AsyncHttpClient的使用很是像,以前一直沒有總結過Volley的使用,週末有時間總結一下,與你們分享。java
Volley適用於交互頻繁可是數據量小的網絡請求,好比咱們在上一篇博客中介紹的新聞列表,這種狀況下使用Volley就是很是合適的,可是對於一些數據量大的網絡請求,好比下載,Volley就顯得略有力不從心。android
Volley是一個開源項目,咱們能夠在GitHub上得到它的源代碼,地址https://github.com/mcxiaoke/android-volley,拿到以後咱們能夠將之打包成jar包使用,也能夠直接將源碼拷貝到咱們的項目中使用,我的推薦第二種方式,這樣發生錯誤的時候方便咱們調試,同時也有利於咱們修改源碼,定製咱們本身的Volley。若是要拷貝源碼,咱們只須要將「android-volley-master\android-volley-master\src\main\java」這個文件夾下的com包拷貝到咱們的項目中便可。git
RequestQueue mQueue = Volley.newRequestQueue(this);得到一個請求隊列只須要一個參數,就是Context,這裏由於在MainActivity發起請求,因此直接用this。字符型數據的請求,咱們使用StringRequest:
StringRequest sr = new StringRequest("http://www.baidu.com", new Response.Listener<String>() { @Override public void onResponse(String response) { Log.i("lenve", response); } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { } });
mQueue.add(sr);嗯,就是這麼簡單。那咱們不由有疑問了,剛纔這個請求時get請求仍是post請求?咱們如何本身設置網絡請求方式?咱們看一下這個構造方法的源碼:
/** * 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); }
/** * 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; }
/** * Returns a Map of parameters to be used for a POST or PUT request. Can throw * {@link AuthFailureError} as authentication may be required to provide these values. * * <p>Note that you can directly override {@link #getBody()} for custom data.</p> * * @throws AuthFailureError in the event of auth failure */ protected Map<String, String> getParams() throws AuthFailureError { return null; }看註釋咱們大概就明白這個方法是幹什麼的了,它將POST請求或者PUT請求須要的參數封裝成一個Map對象,那麼咱們若是在POST請求中須要傳參的話,直接重寫這個方法就能夠了,代碼以下:
StringRequest sr = new StringRequest("http://www.baidu.com", new Response.Listener<String>() { @Override public void onResponse(String response) { Log.i("lenve", response); } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { } }) { /** * 重寫getParams(),能夠本身組裝post要提交的參數 */ @Override protected Map<String, String> getParams() throws AuthFailureError { Map<String, String> map = new HashMap<String, String>(); map.put("params1", "value1"); map.put("params1", "value1"); return map; } };好了,請求字符數據就是這麼簡單。
JsonObjectRequest jsonReq = new JsonObjectRequest(HTTPURL, new Response.Listener<JSONObject>() { @Override public void onResponse(JSONObject response) { try { JSONObject jo = response.getJSONObject("paramz"); JSONArray ja = jo.getJSONArray("feeds"); for (int i = 0; i < ja.length(); i++) { JSONObject jo1 = ja.getJSONObject(i) .getJSONObject("data"); Log.i("lenve", jo1.getString("subject")); } } catch (JSONException e) { e.printStackTrace(); } } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { } }); mQueue.add(jsonReq);
ImageLoader il = new ImageLoader(mQueue, new BitmapCache()); ImageListener listener = ImageLoader.getImageListener(iv, R.drawable.ic_launcher, R.drawable.ic_launcher); il.get(IMAGEURL, listener);
public ImageContainer get(String requestUrl, ImageListener imageListener, int maxWidth, int maxHeight, ScaleType scaleType) { // only fulfill requests that were initiated from the main thread. throwIfNotOnMainThread(); final String cacheKey = getCacheKey(requestUrl, maxWidth, maxHeight, scaleType); // Try to look up the request in the cache of remote images. Bitmap cachedBitmap = mCache.getBitmap(cacheKey); if (cachedBitmap != null) { // Return the cached bitmap. ImageContainer container = new ImageContainer(cachedBitmap, requestUrl, null, null); imageListener.onResponse(container, true); return container; } // The bitmap did not exist in the cache, fetch it! ImageContainer imageContainer = new ImageContainer(null, requestUrl, cacheKey, imageListener); // Update the caller to let them know that they should use the default bitmap. imageListener.onResponse(imageContainer, true); // Check to see if a request is already in-flight. BatchedImageRequest request = mInFlightRequests.get(cacheKey); if (request != null) { // If it is, add this request to the list of listeners. request.addContainer(imageContainer); return imageContainer; } // The request is not already in flight. Send the new request to the network and // track it. Request<Bitmap> newRequest = makeImageRequest(requestUrl, maxWidth, maxHeight, scaleType, cacheKey); mRequestQueue.add(newRequest); mInFlightRequests.put(cacheKey, new BatchedImageRequest(newRequest, imageContainer)); return imageContainer; }
<com.android.volley.toolbox.NetworkImageView android:id="@+id/iv2" android:layout_width="wrap_content" android:layout_height="wrap_content"/>
NetworkImageView niv = (NetworkImageView) this.findViewById(R.id.iv2);
niv.setDefaultImageResId(R.drawable.ic_launcher); ImageLoader il = new ImageLoader(mQueue, new BitmapCache()); niv.setImageUrl(IMAGEURL, il);
public ImageContainer get(String requestUrl, ImageListener imageListener, int maxWidth, int maxHeight, ScaleType scaleType) { .... }沒錯,NetworkImageView和ImageLoader最終都會到達這個方法,因此說這兩個東東其實很是像,那麼在實際開發中究竟用哪一個要視狀況而定。
ImageRequest ir = new ImageRequest(IMAGEURL, new Listener<Bitmap>() { @Override public void onResponse(Bitmap response) { iv3.setImageBitmap(response); } }, 200, 200, ScaleType.CENTER, Bitmap.Config.ARGB_8888, new ErrorListener() { @Override public void onErrorResponse(VolleyError error) { } }); mQueue.add(ir);
public class JsonObjectRequest extends JsonRequest<JSONObject> { /** * Creates a new request. * @param method the HTTP method to use * @param url URL to fetch the JSON from * @param requestBody A {@link String} to post with the request. Null is allowed and * indicates no parameters will be posted along with request. * @param listener Listener to receive the JSON response * @param errorListener Error listener, or null to ignore errors. */ public JsonObjectRequest(int method, String url, String requestBody, Listener<JSONObject> listener, ErrorListener errorListener) { super(method, url, requestBody, listener, errorListener); } /** * Creates a new request. * @param url URL to fetch the JSON from * @param listener Listener to receive the JSON response * @param errorListener Error listener, or null to ignore errors. */ public JsonObjectRequest(String url, Listener<JSONObject> listener, ErrorListener errorListener) { super(Method.GET, url, null, listener, errorListener); } /** * Creates a new request. * @param method the HTTP method to use * @param url URL to fetch the JSON from * @param listener Listener to receive the JSON response * @param errorListener Error listener, or null to ignore errors. */ public JsonObjectRequest(int method, String url, Listener<JSONObject> listener, ErrorListener errorListener) { super(method, url, null, listener, errorListener); } /** * Creates a new request. * @param method the HTTP method to use * @param url URL to fetch the JSON from * @param jsonRequest A {@link JSONObject} to post with the request. Null is allowed and * indicates no parameters will be posted along with request. * @param listener Listener to receive the JSON response * @param errorListener Error listener, or null to ignore errors. */ public JsonObjectRequest(int method, String url, JSONObject jsonRequest, Listener<JSONObject> listener, ErrorListener errorListener) { super(method, url, (jsonRequest == null) ? null : jsonRequest.toString(), listener, errorListener); } /** * Constructor which defaults to <code>GET</code> if <code>jsonRequest</code> is * <code>null</code>, <code>POST</code> otherwise. * * @see #JsonObjectRequest(int, String, JSONObject, Listener, ErrorListener) */ public JsonObjectRequest(String url, JSONObject jsonRequest, Listener<JSONObject> listener, ErrorListener errorListener) { this(jsonRequest == null ? Method.GET : Method.POST, url, jsonRequest, listener, errorListener); } @Override protected Response<JSONObject> parseNetworkResponse(NetworkResponse response) { try { String jsonString = new String(response.data, HttpHeaderParser.parseCharset(response.headers, PROTOCOL_CHARSET)); return Response.success(new JSONObject(jsonString), HttpHeaderParser.parseCacheHeaders(response)); } catch (UnsupportedEncodingException e) { return Response.error(new ParseError(e)); } catch (JSONException je) { return Response.error(new ParseError(je)); } } }哈,原來這麼簡單,光是構造方法就有五個,不過構造方法都很簡單,咱們就很少說,核心的功能在parseNetworkResponse方法中,這裏調用成功的時候返回一個Response泛型,泛型裏邊的東西是一個JSONObject,其實也很簡單,咱們若是要處理XML,那麼直接在重寫 parseNetworkResponse方法,在調用成功的時候直接返回一個Response泛型,這個泛型中是一個XmlPullParser對象,哈哈,很簡單吧,同時,結合StringRequest的實現方式,咱們實現了XMLRequest的代碼:
public class XMLRequest extends Request<XmlPullParser> { private Listener<XmlPullParser> mListener; public XMLRequest(String url, Listener<XmlPullParser> mListener, ErrorListener listener) { this(Method.GET, url, mListener, listener); } public XMLRequest(int method, String url, Listener<XmlPullParser> mListener, ErrorListener listener) { super(method, url, listener); this.mListener = mListener; } @Override protected Response<XmlPullParser> parseNetworkResponse( NetworkResponse response) { String parsed; try { parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers)); XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); XmlPullParser parser = factory.newPullParser(); parser.setInput(new StringReader(parsed)); return Response.success(parser, HttpHeaderParser.parseCacheHeaders(response)); } catch (UnsupportedEncodingException e) { parsed = new String(response.data); } catch (XmlPullParserException e) { e.printStackTrace(); } return null; } @Override protected void deliverResponse(XmlPullParser response) { if (mListener != null) { mListener.onResponse(response); } } @Override protected void onFinish() { super.onFinish(); mListener = null; } }
XMLRequest xr = new XMLRequest(XMLURL, new Response.Listener<XmlPullParser>() { @Override public void onResponse(XmlPullParser parser) { try { int eventType = parser.getEventType(); while (eventType != XmlPullParser.END_DOCUMENT) { switch (eventType) { case XmlPullParser.START_DOCUMENT: break; case XmlPullParser.START_TAG: String tagName = parser.getName(); if ("city".equals(tagName)) { Log.i("lenve", new String(parser.nextText())); } break; case XmlPullParser.END_TAG: break; } eventType = parser.next(); } } catch (XmlPullParserException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { } }); mQueue.add(xr);