构造RequestQueue实例
如果我们的应用需要经常使用网络,那么创建一个单例的RequestQueue会更加高效。
public class MyVolleyHelper {
private static MyVolleyHelper mInstance;
private RequestQueue mRequestQueue;
private static Context mContext;
private MyVolleyHelper(Context context) {
mContext = context;
}
public static synchronized MyVolleyHelper getInstance(Context context) {
if (mInstance == null) {
mInstance = new MyVolleyHelper(context);
}
return mInstance;
}
public RequestQueue getRequestQueue() {
if (mRequestQueue == null) {
// getApplicationContext() 是关键, 它避免了你
//传递进Activity或BroadcastReceiver导致的内存泄漏
mRequestQueue = Volley.newRequestQueue(mContext.getApplicationContext());
}
return mRequestQueue;
}
public <T> void addToRequestQueue(Request<T> req) {
getRequestQueue().add(req);
}
}
Volley get请求
- JsonObjectRequest 用来接收和发送JsonObject类型的数据
- JsonArrayRequest 用来接收和发送JsonArray类型的数据
- StringRequest 用来接收和发送响应主体为String的数据
下面以JsonArrayRequest为例,volley的onResponse方法是在主线程上,所以可以进行ui的更新,如果有耗时的操作,需要放到thread中操作。
private void getVolleyData() {
JsonArrayRequest jsonArrayRequest = new JsonArrayRequest(Request.Method.GET,
uri,null,
new Response.Listener<JSONArray>() {
@Override
public void onResponse(JSONArray response) {
/// 在UI thread上
///refresh ui
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.d(TAG, "error = "+error);
}
});
///设置tag,方便取消对应tag的request
jsonArrayRequest.setTag(requestTAG);
MyVolleyHelper.getInstance(getApplicationContext()).addToRequestQueue(jsonArrayRequest);
}
返回错误类型有以下几种:
- AuthFailureError:如果在做一个HTTP的身份验证,可能会发生这个错误。
- NetworkError:Socket关闭,服务器宕机,DNS错误都会产生这个错误。
- NoConnectionError:和NetworkError类似,这个是客户端没有网络连接。
- ParseError:在使用JsonObjectRequest或JsonArrayRequest时,如果接收到的JSON是畸形,会产生异常。
- SERVERERROR:服务器的响应的一个错误,最有可能的4xx或5xx HTTP状态代码。
- TimeoutError:Socket超时,服务器太忙或网络延迟会产生这个异常。默认情况下,Volley的超时时间为2.5秒。如果得到这个错误可以使用RetryPolicy。
另外还有ImageRequest和ImageLoader的使用。
- 通过Volley请求,在普通ImageView上显示图片。
- 通过Volley请求,在普通NetworkImageView上显示图片。
private void imageLoaderRequest() {
//实例化ImageLoader
ImageLoader imageLoader = new ImageLoader(MyVolleyHelper.getInstance(getApplicationContext()).getRequestQueue(),
new BitmapCache());
boolean debugNetImage = true;//Test code
if (debugNetImage) {
networkImageView.setDefaultImageResId(R.mipmap.ic_launcher);
networkImageView.setErrorImageResId(R.mipmap.ic_launcher);
networkImageView.setImageUrl(URI, imageLoader);
}else {
//设置监听器
ImageLoader.ImageListener listener =
ImageLoader.getImageListener(imageView, R.mipmap.ic_launcher, R.mipmap.ic_launcher);
//3.获取图片
imageLoader.get(URI, listener);
}
}
BitmapCache 主要用来设置图片缓存大小
public class BitmapCache implements ImageLoader.ImageCache {
private LruCache<String, Bitmap> mCache;
public BitmapCache() {
int maxSize = 10 * 1024 * 1024;
///缓存图片的大小设置为10M
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) {
return mCache.get(url);
}
@Override
public void putBitmap(String url, Bitmap bitmap) {
mCache.put(url, bitmap);
}
}
Vollet post请求
这个部分是我最想要说的,因为使用post请求的时候参数始终无法获取到正常的数据,用okhttp传递相同的参数是可以获取到返回的数据的,而volley怎么都不可以。所以查看了网上的一些介绍,在此特意记录一下。
以前是我测试的方法尝试:
服务器返回的是JsonObject,所以我们要用JsonObjectRequest来请求。
private void postDataFail1() {
JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(Request.Method.POST, uri,
null, new Response.Listener<JSONObject>() {
@Override
public void onResponse(JSONObject response) {
if (response != null) {
Log.d(TAG, "response = " + response.toString());
}
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.d(TAG, "error = " + error);
}
}) {
//将参数存储到map中然后返回,系统会自动调用这个方法,将参数传递出去
@Override
protected Map<String, String> getParams() {
Map<String, String> map = new HashMap<String, String>();
map.put(key1, "string1");
map.put(key2, "string2");
return map;
}
};
jsonObjectRequest.setTag("post");
MyVolleyHelper.getInstance(getApplicationContext()).addToRequestQueue(jsonObjectRequest);
}
此方法是重写Request的getParams方法,将参数传递给服务器。但是失败了,没有数据返回。
再试验另外一种写法:
private void postDataFail2() {
Map<String, String> map = new HashMap<String, String>();
map.put(key1, "string1");
map.put(key2, "string2");
JSONObject jsonObject = new JSONObject(map);
JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(Request.Method.POST, uri,
jsonObject, new Response.Listener<JSONObject>() {
@Override
public void onResponse(JSONObject response) {
if (response != null) {
Log.d(TAG, "response = " + response.toString());
}
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.d(TAG, "error = " + error);
}
}) ;
jsonObjectRequest.setTag("post");
MyVolleyHelper.getInstance(getApplicationContext()).addToRequestQueue(jsonObjectRequest);
}
此方法是通过传入JsonObjectRequest的构造方法,但是依然失败了。
上述2种方式实际上都没有将参数传递给服务器,所以服务器当然就不会返回数据。
我们先看下getParams方法是哪里使用的。
Request.java
/**
* Returns the raw POST or PUT body to be sent.
*
* <p>By default, the body consists of the request parameters in
* application/x-www-form-urlencoded format. When overriding this method, consider overriding
* {@link #getBodyContentType()} as well to match the new body format.
*
* @throws AuthFailureError in the event of auth failure
*/
public byte[] getBody() throws AuthFailureError {
Map<String, String> params = getParams();
if (params != null && params.size() > 0) {
return encodeParameters(params, getParamsEncoding());
}
return null;
}
getBody方法会将参数转成& = 形式传递过去。
而JsonObjectRequest继承JsonRequest,来看下JsonRequest的getBody方法
@Override
public byte[] getBody() {
try {
return mRequestBody == null ? null : mRequestBody.getBytes(PROTOCOL_CHARSET);
} catch (UnsupportedEncodingException uee) {
VolleyLog.wtf("Unsupported Encoding while trying to get the bytes of %s using %s",
mRequestBody, PROTOCOL_CHARSET);
return null;
}
}
mRequestBody是一个string类型,并且mRequestBody就是参数,必须通过构造方法传递
public JsonRequest(int method, String url, String requestBody, Listener<T> listener,
ErrorListener errorListener) {
super(method, url, errorListener);
mListener = listener;
mRequestBody = requestBody;
}
此时,应该就已经很明白了,第一种方式,JsonObjectRequest重写getParams方法根本就不会将参数传递上去,无效的。第二种方式传递的是JsonObject数据,并不是String,所以同样无法获取到数据。
解决方案:
- 自定义CustomRequestt继承Request,将参数通过构造方法,在getParams方法返回这参数。
这样就可以将参数传递上去。public class CustomRequest extends Request<JSONObject> { private Response.Listener<JSONObject> listener; private Map<String, String> params; public CustomRequest(String url, Map<String, String> params, Response.Listener<JSONObject> reponseListener, Response.ErrorListener errorListener) { super(Method.GET, url, errorListener); this.listener = reponseListener; this.params = params; } public CustomRequest(int method, String url, Map<String, String> params, Response.Listener<JSONObject> reponseListener, Response.ErrorListener errorListener) { super(method, url, errorListener); this.listener = reponseListener; this.params = params; } protected Map<String, String> getParams() throws com.android.volley.AuthFailureError { return params; }; @Override protected Response<JSONObject> parseNetworkResponse(NetworkResponse response) { try { String jsonString = new String(response.data, HttpHeaderParser.parseCharset(response.headers, "utf-8")); 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)); } } @Override protected void deliverResponse(JSONObject response) { // TODO Auto-generated method stub listener.onResponse(response); }
}
使用方法:
~~~java
private void postDataSuccess1() {
Map<String, String> map = new HashMap<String, String>();
map.put(key1, "string1");
map.put(key2, "string2");
CustomRequest jsonObjectRequest = new CustomRequest(Request.Method.POST, uri,
map, new Response.Listener<JSONObject>() {
@Override
public void onResponse(JSONObject response) {
Log.d(TAG, "postThemeData2 onResponse");
if (response != null) {
Log.d(TAG, "response = " + response.toString());
}
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.d(TAG, "error = " + error);
}
});
jsonObjectRequest.setTag("post");
MyVolleyHelper.getInstance(getApplicationContext()).addToRequestQueue(jsonObjectRequest);
}
-
自定义MyJsonObjectRequest继承JsonRequest,将参数直接以string的形式传递。
public class MyJsonObjectRequest extends JsonRequest<JSONObject> { String stringRequest; public MyJsonObjectRequest(String url, String stringRequest, Response.Listener<JSONObject> listener, Response.ErrorListener errorListener) { super(Method.POST, url, stringRequest , listener, errorListener); this.stringRequest = stringRequest; } @Override public String getBodyContentType() { return "application/x-www-form-urlencoded; charset=" + getParamsEncoding(); } @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)); } } }
这里注意的是必须要重写getBodyContentType方法。
使用方法:
private void postDataSuccess2() { Map<String, String> map = new HashMap<String, String>(); map.put(key1, "string1"); map.put(key2, "string2"); String params = appendParameter( uri,map); Log.d(TAG, "params = "+params); MyJsonObjectRequest jsonObjectRequest = new MyJsonObjectRequest( uri, params, new Response.Listener<JSONObject>() { @Override public void onResponse(JSONObject response) { if (response != null) { Log.d(TAG, "postDataSuccess2 response = " + response.toString()); } } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { } }); MyVolleyHelper.getInstance(getApplicationContext()).addToRequestQueue(jsonObjectRequest); } ///拼接下参数,转成string private String appendParameter(String url,Map<String,String> params){ Uri uri = Uri.parse(url); Uri.Builder builder = uri.buildUpon(); for(Map.Entry<String,String> entry:params.entrySet()){ builder.appendQueryParameter(entry.getKey(),entry.getValue()); } return builder.build().getQuery(); }
还是使用JsonObjectRequest,重写getBody 和getBodyContentType方法,保证传递的参数准确。
private void postDataSuccess3() {
Map<String, String> map = new HashMap<String, String>();
map.put(key1, "string1");
map.put(key2, "string2");
final String mRequestBody = appendParameter(
uri,map);
JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(Request.Method.POST, uri,
null, new Response.Listener<JSONObject>() {
@Override
public void onResponse(JSONObject response) {
Log.d(TAG, "postThemeData onResponse");
if (response != null) {
Log.d(TAG, "response = " + response.toString());
}
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
Log.d(TAG, "error = " + error);
}
}) {
@Override
public byte[] getBody() {
try {
return mRequestBody == null ? null : mRequestBody.getBytes(PROTOCOL_CHARSET);
} catch (UnsupportedEncodingException uee) {
VolleyLog.wtf("Unsupported Encoding while trying to get the bytes of %s using %s",
mRequestBody, PROTOCOL_CHARSET);
return null;
}
}
@Override
public String getBodyContentType() {
return "application/x-www-form-urlencoded; charset=" + getParamsEncoding();
}
};
jsonObjectRequest.setTag("post");
MyVolleyHelper.getInstance(getApplicationContext()).addToRequestQueue(jsonObjectRequest);
}
上述三种写法,都可以正确返回数据。以上做个记录。
总结
JsonObjectRequest 请求post数据不能直接通过重写getParams方法或者直接在构造方法里面传递,而是需要保证getBody方法能够真正的得到参数。
感谢
本文重点参考了http://blog.csdn.net/onlysnail/article/details/47905375和http://blog.csdn.net/Waydrow/article/details/51002721, 还有一些其他的volley文章。