今天在做客户端与服务器之间通过Volly的POST传递数据时遇到了一个问题,现在将他记载下来,加深自己的印象,希望自己以后遇到问题可以这样去分析问题。
我们知道Volly提供了很简单的方法就能够去网络获取数据,我们这里只讨论POST方法,下面上代码:
/**
* post网络请求
*/
public static void sendHttprequest(Context context, String url,final Map mapmap) {
//首先获取请求队列
RequestQueue requestQueue = Volley.newRequestQueue(context);
//new出一个StringRequest的普通POST请求
StringRequest stringRequest = new StringRequest(Request.Method.POST,url, new Response.Listener() {
//成功方法以及失败方法
@Override
public void onResponse(String s) {
Log.d("tgp",s.toString());
}
},new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError volleyError) {
Log.d("haha", volleyError.toString());
}
})
//通过重写父类的getParams()来给拿到请求体,也就是我们要提交给父类的数据
{
protected Map getParams() throws AuthFailureError{
return mapmap;
}
};
//将请求添加到请求队列中
requestQueue.add(stringRequest);
}
以上代码十分简单,基本所有其他工具的用法也差不多。因为服务器返回给我的是json格式的数据,所以我就想试试JSONObjectRequest去服务器上获取数据。也跟上面的方法类似:
//跟上面基本相同的注释我就不写了
public static void sendJsonprequest(Context context, String url,final Map mapmap) {
RequestQueue requestQueue = Volley.newRequestQueue(context);
//此处的第三个参数就是请求体,需要post提交的数据,但是我们先置为空,我们先通过和StringRequest方法一样重写getParams()能否成功
JSONObjectRequest objectRequest = new JSONObjectRequest(Request.Method.POST,url,null,new Response.Listener() {
@Override
public void onResponse(JSONObject s) {
Log.d("tgp",s.getString("Submit_status").toString());
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError volleyError) {
Log.d("haha", volleyError.toString());
}
})
//重写getParams()
{
protected Map getParams() throws AuthFailureError{
return mapmap;
}
};
requestQueue.add(stringRequest);
}
我满心欢喜的运行,想见证奇迹的时刻,但是服务器端并没有收到我发的数据,和上面的StringRequest却可以,而且还收到了返回的消息。这是为什么呢?我们先来看看StringRequest的源码:
public class StringRequest extends Request<String> {
private final Listener mListener;
//我们new的就是这个构造方法,这个方法需要把请求方法POST,url,和失败的监听提交给父类Request,因为这些都是在父类中处理的,所有的请求都是基于Request这个类的。
public StringRequest(int method, String url, Listener listener,
ErrorListener errorListener) {
super(method, url, errorListener);
//把成功的监听给全局变量mListener
mListener = listener;
}
public StringRequest(String url, Listener listener, ErrorListener errorListener) {
this(Method.GET, url, listener, errorListener);
}
//这个方法必须要重写,这个是成功的回调
@Override
protected void deliverResponse(String response) {
mListener.onResponse(response);
}
//这个方法也要重写,用来解析服务器回来的数据
@Override
protected Response parseNetworkResponse(NetworkResponse response) {
String parsed;
try {
//response.data就是服务器的数据,这里把他用请求头里编码方式封装成了一个字符串parsed ,默认是utf-8
parsed = new String(response.data, HttpHeaderParser.parseCharset(response.headers));
} catch (UnsupportedEncodingException e) {
parsed = new String(response.data);
}
//最终的数据parsed
return Response.success(parsed, HttpHeaderParser.parseCacheHeaders(response));
}
}
这样似乎还是没有得到我们想要的答案,接着看超类Request里面一段源码:
protected Map getPostParams() throws AuthFailureError {
return getParams();
}
//子类可以重写这个方法
protected Map getParams() throws AuthFailureError {
return null;
}
//关键在这里
public byte[] getPostBody() throws AuthFailureError {
//这里postParams为空,看上面两个方法
Map postParams = getPostParams();
//if为假,不执行语句,最后返回空
if (postParams != null && postParams.size() > 0) {
return encodeParameters(postParams, getPostParamsEncoding());
}
return null;
}
如果我们在用这个方法的时候不重写getParams()这个方法,也就是返回空,
但是如果我们重写了这个方法并返回了一个map就可以让if里面语句得到执行,这样也就是StringRequest为什么可以写的原因,再来看为什么JSONObject不可以,先来看他的源码:
public class JsonObjectRequest extends JsonRequest<JSONObject> {
//其他的基本都一样,只不过第三个参数是指定了要一个JSONObjec对象
public JsonObjectRequest(int method, String url, JSONObject jsonRequest,
Listener listener, ErrorListener errorListener) {
//这里第三个参数我们一开始new的时候是给的空,因为我们想知道能不能重写getParams来获取请求体
super(method, url, (jsonRequest == null) ? null : jsonRequest.toString(), listener,
errorListener);
}
public JsonObjectRequest(String url, JSONObject jsonRequest, Listener listener,
ErrorListener errorListener) {
this(jsonRequest == null ? Method.GET : Method.POST, url, jsonRequest,
listener, errorListener);
}
//解析也是一样,只不过最后的结果是一个JSONObjec对象
@Override
protected Response parseNetworkResponse(NetworkResponse response) {
try {
String jsonString =
new String(response.data, HttpHeaderParser.parseCharset(response.headers));
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));
}
}
}
基本没什么可以看的,我们接着看他的父类JsonRequest类,
public JsonRequest(int method, String url, String requestBody, Listener listener,
ErrorListener errorListener) {
super(method, url, errorListener);
mListener = listener;
//第三个参数也就是请求体给了mRequestBody
mRequestBody = requestBody;
}
@Override
public byte[] getPostBody() {
return getBody();
}
@Override
//关键在这里
public byte[] getBody() {
try {
//从这里可以看出mRequestBody就是我们的请求体,不管我们重不重写getParams()这个方法都没有用,mRequestBody只能通过第三个参数直接赋值
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;
}
}
那就简单了,我们就来试一试这个思路到底能不能行呢?
//跟上面基本相同的注释我就不写了
public static void sendJsonprequest(Context context, String url,final Map mapmap) {
//因为第三个参数是指定要JSONObjicet类型的
JSONObject jsonobject=new JSONObject(mapmap);
RequestQueue requestQueue = Volley.newRequestQueue(context);
//此处的第三个参数就是请求体,需要post提交的数据,我们给他赋值。
JSONObjectRequest objectRequest = new JSONObjectRequest(Request.Method.POST,url,jsonobject,new Response.Listener() {
@Override
public void onResponse(JSONObject s) {
Log.d("tgp",s.getString("Submit_status").toString());
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError volleyError) {
Log.d("haha", volleyError.toString());
}
});
requestQueue.add(stringRequest);
}
结果还是不行,服务器仍然接受不到数据,但是debug跟着走,数据其实是传到了某一个地方的,我不记得是哪里了,数据格式是json字符串形式的。再来看JsonRequest类中还有一个方法:
public String getBodyContentType() {
return PROTOCOL_CONTENT_TYPE;
}
再看PROTOCOL_CONTENT_TYPE是什么:
private static final String PROTOCOL_CONTENT_TYPE =
String.format("application/json; charset=%s", PROTOCOL_CHARSET);
所以传过去的是json字符串形式的数据,这时候我恍然大悟,因为服务器是我的上司写的,他并没有支持json格式的数据,也就是{“key1”:“value1”,“key2”:“value2”}这种形式的。他只能接受
key1=value1&key2=value2&…这种形式的。
但是JSONObjectRequest构造函数中指定了JSONobject类型,所以我们只能自己再自定义一个Request去做我想做的事情,也就是发送数据时是用普通的key1=value1&key2=value2&…这种形式的,而接受时就接受JSONObject对象的。下面直接上代码:
//唯一的不同就是我直接继承了Request类
public class JsonStringRequest extends Request<JSONObject> {
private Response.Listener mListener;
public JsonStringRequest(String url, Response.Listener listener, Response.ErrorListener errorListener) {
super(Request.Method.POST, url, errorListener);
mListener = listener;
mMap = map;
}
//此处因为response返回值需要json数据,和JsonObjectRequest类一样即可
@Override
protected Response parseNetworkResponse(NetworkResponse response) {
try {
String jsonString = new String(response.data,HttpHeaderParser.parseCharset(response.headers));
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) {
mListener.onResponse(response);
}
}
来调用试试看:
public static void sendJsonprequest(Context context, String url,final Map mapmap) {
RequestQueue requestQueue = Volley.newRequestQueue(context);
JsonStringRequest stringRequest = new JsonStringRequest(url, new Response.Listener() {
@Override
public void onResponse(JSONObject s) {
try{
Log.d("tgp",s.getString("Submit_status").toString());
}catch (Exception e){
e.printStackTrace();
}
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError volleyError) {
Log.d("haha", volleyError.toString());
}
})
//基本都一样,但是我这里用的是重写getParams()来设置请求体,为什么可以呢?还记得前面分析的么?如果不记得,返回前面看一看。
{
protected Map getParams() throws AuthFailureError{
return mapmap;
}
};
requestQueue.add(stringRequest);
}
最后结果是可以的,服务器终于收到了我的数据,而且我也能收到他返回的数据给我。最后来总结一下:
1.StringRequest可以重写getParams()来设置请求体,而JSONObjectRequest不可以。
2.Volly是开源的,你可以自定义自己想要的请求方式和解析方式。
3.希望自己能够继续这样热爱学习。