Android学习记录之Volley框架JSONObjectRequest

最近发现JSONObjectRequest通过getParams,不能像StringRequest一样获取到服务器的数据,反馈的结果是没有传参给服务器。
那就看看参数最终是在哪里提交给服务器的。通过调式发现HurlStack.java中的addBodyIfExists()这个方法,发现request.getBody()==null,也就是没有接收到参数。
StringRequest可以重写getParams来传参是因为Request中的这个方法:
Request.java

public byte[] getBody() throws AuthFailureError {  
    Map params = getParams();  
    if (params != null && params.size() > 0) {  
        return encodeParameters(params, getParamsEncoding());  
    }  
    return null;  
}  

重写的getParams的结果会传到这里。

JsonObjectRequest不能重写getParams来传参是因为JsonRequest中的这个方法:
JsonRequest.java

@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;  
    }  
} 

重写了Request.getBody(),这段代码说明 mRequestBody这个变量就是它的参数。这个变量就存在与JsonRequest的构造函数中。
说明,JsonObjectRequest的传参必须通过构造来传,重写getParams()是无法传递的。

下面按正常思路,来踩第二个坑。给JsonObjectRequest的构造传递参数。又会出现什么问题?

/** 
 * JsonOnjectRequest Post 可能会出错的方式 
 * 
 * 这里的思路是调用JsonObjectRequest带参构造,传参进入不就得了? 
 * 但是这里还是请求不到数据。 
 * 原因是使用JsonObjectRequest 如果服务器接受的参数类型是 http://....?key1=value1&key2=value2, 
 * 而不是json串{key1 : value1,key2 : value2...},参数就会传不进去。 
 * 可以在 HurlStack.java 的addBodyIfExists方法中看到你传入的参数信息。 
 */  
private void jsonObjectRequestError2(){  

    String url = "http://route.showapi.com/213-3";  
    Map map = DummyData.getDummyData();  
    JSONObject params = new JSONObject(map);  

    JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(Request.Method.POST,url,params, new Response.Listener() {  
        @Override  
        public void onResponse(JSONObject response) {  
            responseText.setText(response.toString());  
        }  
    }, new Response.ErrorListener() {  
        @Override  
        public void onErrorResponse(VolleyError error) {  
            responseText.setText(error.getMessage());  
        }  
    });  
    mQueue.add(jsonObjectRequest);  
}  

仍然获取不到数据,原因看上面的注释。反馈的结果是没有传参给服务器。那就看看参数最终是在哪里提交给服务器的。
Debug HurlStack.java中的addBodyIfExists()这个方法。

参数传递是正确的,只不过传递的参数格式是一个json串。这时考虑是不是服务器不接受这样的传参,那就换一种格式传递参数:
http://apis.baidu.com/apistore/weatherservice/citylist?key1=value1&key2=value2…
下面以这种思路把代码进行改造。
假设服务器不接受json串的参数,而是接受key=value这样的参数,这样我们就传递这种格式的参数给JsonObjectRequest。然后在试试能否成功。但是JsonObjectRequest的构造中,参数必须以JSONObject来定义,所以只能单独定义一个类继承JSONObject,参数以String的格式传入。

按正常思路,来踩第三个坑。定义一个类继承JsonObjectRequest,传入一个String参数,格式是 “key1=value1&key2=value2…”。
这里不贴代码了,这个类除了把构造的参数JSONObject改为String,其他完全一致。当然,结果还是获取不到数据。

原因很简单,看JsonRequest中的变量 PROTOCOL_CONTENT_TYPE。

/** Content type for request. */  
private static final String PROTOCOL_CONTENT_TYPE =  
String.format("application/json; charset=%s", PROTOCOL_CHARSET);  

请求的是一个json串,但是这里自定义的Request传入的参数不是一个json串。所以还需要在自定义的类中重写一个方法:

public String getBodyContentType() {  
    return "application/x-www-form-urlencoded; charset=" + getParamsEncoding();  
}  

这段代码从Request.java中Copy。
自定义的Request

public class MyJsonObjectRequest extends JsonRequest<JSONObject> {  

    String stringRequest;  

    /** 
     * 这里的method必须是Method.POST,也就是必须带参数。 
     * 如果不想带参数,可以用JsonObjectRequest,给它构造参数传null。GET方式请求。 
     * @param stringRequest 格式应该是 "key1=value1&key2=value2" 
     */  

    public MyJsonObjectRequest(String url, String stringRequest,  
                             Response.Listener 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 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));  
        }  
    }  

请求方法:

private void jsonObjectRequestPostSuccess1(){  
        String url = "http://route.showapi.com/213-3";  
        Map<String,String> map = DummyData.getDummyData();  
        String params = appendParameter(url,map);  

        MyJsonObjectRequest jsonObjectRequest = new MyJsonObjectRequest(url,params, new Response.Listener<JSONObject>() {  
            @Override  
            public void onResponse(JSONObject response) {  
                responseText.setText(response.toString());  
            }  
        }, new Response.ErrorListener() {  
            @Override  
            public void onErrorResponse(VolleyError error) {  
                responseText.setText(error.getMessage());  
            }  
        });  
        mQueue.add(jsonObjectRequest);  
    }  



    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不可,如果了解Request的继承关系和参数的传递流程,直接使用JsonObjectRequest也是可以的。

/** 
 * 不想自定义,非得用JsonObjectRequestPost不可,只要你不嫌麻烦。 
 */  
private void jsonObjectRequestPostSuccess2(){  
    String url = "http://route.showapi.com/213-3";  
    Map params = DummyData.getDummyData();  
    final String mRequestBody = appendParameter(url,params);  

    JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(Request.Method.POST,url,null, new Response.Listener() {  
        @Override  
        public void onResponse(JSONObject response) {  
            responseText.setText(response.toString());  
        }  
    }, new Response.ErrorListener() {  
        @Override  
        public void onErrorResponse(VolleyError error) {  
            responseText.setText(error.getMessage());  
        }  
    }){  
        @Override  
        public String getBodyContentType() {  
            return "application/x-www-form-urlencoded; charset=" + getParamsEncoding();  
        }  

        @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;  
            }  
        }  
    };  
    mQueue.add(jsonObjectRequest);  
}  

private String appendParameter(String url,Map params){  
    Uri uri = Uri.parse(url);  
    Uri.Builder builder = uri.buildUpon();  
    for(Map.Entry entry:params.entrySet()){  
        builder.appendQueryParameter(entry.getKey(),entry.getValue());  
    }  
    return builder.build().getQuery();  
}  

总结:
使用JsonObjectRequest 请求服务器,它的参数是以 json 串 的格式传输给服务器的,考虑服务器是否支持这种格式,可以在JsonRequest的PROTOCOL_CONTENT_TYPE中看到它传递参数的类型。如果服务器支持的是 http:…?key1=value1&key2=value2这种格式,也就是对应Request.java中的PROTOCOL_CONTENT_TYPE变量类型。就需要做一些改动,有两种方式来实现。第一种方式是定义一个类继承JsonObjectRequest,第二种直接使用JsonObjectRequest。原理是相同的,重点是重写匿名内部类的getBodyContentType这个方法。不能重写getParams这个方法类传参。

使用StringRequest 如果是带参数,或者Http Header,或者其他,可以重写它匿名内部类方法,如getParams(),getHeader()等等。

有必要关注HurlStack.java这个类。

你可能感兴趣的:(学习记录)