从源码分析Volly的JSONObjectRequest

今天在做客户端与服务器之间通过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,nullnew 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.希望自己能够继续这样热爱学习。

你可能感兴趣的:(开发中遇到的问题)