Volley StringRequest和JSONObjectRequest使用几个细节

一、Volley StringRequest

        下面是百度api提供的免费的接口‘天气查询-查看可用城市列表’,在百度apistore中有提供(天气查询)。

看下官方提供的代码:

[java]  view plain copy print ?
  1. String httpUrl = "http://apis.baidu.com/apistore/weatherservice/citylist";  
  2. String httpArg = "cityname=%E6%9C%9D%E9%98%B3";  
  3. String jsonResult = request(httpUrl, httpArg);  
  4. System.out.println(jsonResult);  
  5.   
  6. /** 
  7.  * @param urlAll 
  8.  *            :请求接口 
  9.  * @param httpArg 
  10.  *            :参数 
  11.  * @return 返回结果 
  12.  */  
  13. public static String request(String httpUrl, String httpArg) {  
  14.     BufferedReader reader = null;  
  15.     String result = null;  
  16.     StringBuffer sbf = new StringBuffer();  
  17.     httpUrl = httpUrl + "?" + httpArg;  
  18.   
  19.     try {  
  20.         URL url = new URL(httpUrl);  
  21.         HttpURLConnection connection = (HttpURLConnection) url  
  22.                 .openConnection();  
  23.         connection.setRequestMethod("GET");  
  24.         // 填入apikey到HTTP header  
  25.         connection.setRequestProperty("apikey",  "您自己的apikey");  
  26.         connection.connect();  
  27.         InputStream is = connection.getInputStream();  
  28.         reader = new BufferedReader(new InputStreamReader(is, "UTF-8"));  
  29.         String strRead = null;  
  30.         while ((strRead = reader.readLine()) != null) {  
  31.             sbf.append(strRead);  
  32.             sbf.append("\r\n");  
  33.         }  
  34.         reader.close();  
  35.         result = sbf.toString();  
  36.     } catch (Exception e) {  
  37.         e.printStackTrace();  
  38.     }  
  39.     return result;  
  40. }  

现在用Volley 的StringRequest实现它的接口。需要关注的几个地方:

URL:

[java]  view plain copy print ?
  1. httpUrl = httpUrl + "?" + httpArg;//http://apis.baidu.com/apistore/weatherservice/citylist?cityname=%E6%9C%9D%E9%98%B3  

Request Method:

[java]  view plain copy print ?
  1. connection.setRequestMethod("GET");  

Header : 

[java]  view plain copy print ?
  1. connection.setRequestProperty("apikey",  "您自己的apikey");  

参数带在url后面,如果有多个参数用‘&’分割

如果是拼接参数需要注意最前面还有一个‘?’

GET请求

带了http header参数,对应connection.setRequestProperty("","");


下面使用Volley的StringRequest的两种方式来实现上面的请求。


第一种:StringRequest Method.GET 

把参数带在url后面,使用StringRequest的不带参构造。Header参数重写getHeaders方法,对应 connection.setRequestProperty("apikey",  "您自己的apikey");

[java]  view plain copy print ?
  1. private String url = "http://apis.baidu.com/apistore/weatherservice/citylist";  
  2. private String params = "cityname=朝阳";  

 
 

[java]  view plain copy print ?
  1. private void stringRequestWithGet(){  
  2.        url = url + "?" + params;  
  3.        StringRequest stringRequest = new StringRequest(Request.Method.GET, url, new Response.Listener<String>() {  
  4.            @Override  
  5.            public void onResponse(String response) {  
  6.                try {  
  7.                    //使用JSONObject给response转换编码  
  8.                    JSONObject jsonObject = new JSONObject(response);  
  9.                    responseText.setText(jsonObject.toString());  
  10.                } catch (JSONException e) {  
  11.                    e.printStackTrace();  
  12.                }  
  13.            }  
  14.        }, new Response.ErrorListener() {  
  15.            @Override  
  16.            public void onErrorResponse(VolleyError error) {  
  17.                responseText.setText(error.getMessage());  
  18.            }  
  19.        }){  
  20.            @Override  
  21.            public Map<String, String> getHeaders() throws AuthFailureError {  
  22.                Map<String,String> map = new HashMap<>();  
  23.                map.put("apikey","f71e5f1e08cd5a7e42a7e9aa70d22458");  
  24.                return map;  
  25.            }  
  26.        };  
  27.        mQueue.add(stringRequest);  
  28.    }  

第二种:StringRequest Method.POST

Method:改为POST

Params:参数不带在Url末尾,而是重写StringRequest的getParams()。

注意:我以这种方式请求百度apistore中 ‘天气查询-查看可用城市列表’接口,没有成功。但是访问其他提供商的API是成功的。可能是这个接口不支持Post请求,或者我少传了什么参数吧。

[java]  view plain copy print ?
  1. private void stringRequestWithPost(){  
  2.         StringRequest stringRequest = new StringRequest(Request.Method.POST, url, new Response.Listener<String>() {  
  3.             @Override  
  4.             public void onResponse(String response) {  
  5.                 try {  
  6.                     //使用JSONObject给response转换编码  
  7.                     JSONObject jsonObject = new JSONObject(response);  
  8.                     responseText.setText(jsonObject.toString());  
  9.                 } catch (JSONException e) {  
  10.                     e.printStackTrace();  
  11.                 }  
  12.             }  
  13.         }, new Response.ErrorListener() {  
  14.             @Override  
  15.             public void onErrorResponse(VolleyError error) {  
  16.                 responseText.setText(error.getMessage());  
  17.             }  
  18.         }){  
  19.             @Override  
  20.             protected Map<String, String> getParams() throws AuthFailureError {  
  21.                 Map<String,String> map = new HashMap<>();  
  22.                 map.put("cityname","朝阳");  
  23.                 return map;  
  24.             }  
  25.   
  26.             @Override  
  27.             public Map<String, String> getHeaders() throws AuthFailureError {  
  28.                 Map<String,String> map = new HashMap<>();  
  29.                 map.put("apikey","f71e5f1e08cd5a7e42a7e9aa70d22458");  
  30.                 return map;  
  31.             }  
  32.         };  
  33.         mQueue.add(stringRequest);  
  34.     }  


二、Volley JsonObjectRequest

下面介绍使用JsonObjectRequest,以及使用JsonObjectRequest会遇到的一些坑。

由于百度apistore中我以post请求一直没成功过,不知道是什么问题,下面我换了一家API提供商 showapi 的 'QQ音乐十八大排行榜’接口来进行GET请求和POST请求。

先说Method.POST,遇到的坑会多一点。

第一种:JsonObjectRequest  Method.POST

错误写法

[java]  view plain copy print ?
  1. /** 
  2.     * JsonOnjectRequest Post 错误方式 
  3.     * 这里使用了和StringRequest一样的方式传参 重写getParams()方法。 
  4.     */  
  5.    private void jsonObjectRequestError1(){  
  6.   
  7.        String url = "http://route.showapi.com/213-3";  
  8.        final Map<String,String> params = DummyData.getDummyData();  
  9.   
  10.        JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(Request.Method.POST,url,nullnew Response.Listener<JSONObject>() {  
  11.            @Override  
  12.            public void onResponse(JSONObject response) {  
  13.                responseText.setText(response.toString());  
  14.            }  
  15.        }, new Response.ErrorListener() {  
  16.            @Override  
  17.            public void onErrorResponse(VolleyError error) {  
  18.                responseText.setText(error.getMessage());  
  19.            }  
  20.        }){  
  21.            @Override  
  22.            protected Map<String, String> getParams() throws AuthFailureError {  
  23.                return params;  
  24.            }  
  25.        };  
  26.        mQueue.add(jsonObjectRequest);  
  27.    }  

JsonObjectRequest构造中的参数传null,和StringRequest一样去重写getParams()方法来传参。貌似是没问题的?

使用这段代码拿不到服务器返回的数据,反馈的结果是没有传参给服务器。那就看看参数最终是在哪里提交给服务器的。

Debug HurlStack.java中的addBodyIfExists()这个方法,发现request.getBody()==null,也就是没有接收到参数。

StringRequest可以重写getParams来传参是因为Request中的这个方法:

Request.java

[java]  view plain copy print ?
  1. public byte[] getBody() throws AuthFailureError {  
  2.     Map<String, String> params = getParams();  
  3.     if (params != null && params.size() > 0) {  
  4.         return encodeParameters(params, getParamsEncoding());  
  5.     }  
  6.     return null;  
  7. }  

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

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

JsonRequest.java

[java]  view plain copy print ?
  1. @Override  
  2. public byte[] getBody() {  
  3.     try {  
  4.         return mRequestBody == null ? null : mRequestBody.getBytes(PROTOCOL_CHARSET);  
  5.     } catch (UnsupportedEncodingException uee) {  
  6.         VolleyLog.wtf("Unsupported Encoding while trying to get the bytes of %s using %s",  
  7.                 mRequestBody, PROTOCOL_CHARSET);  
  8.         return null;  
  9.     }  
  10. }  

重写了Request.getBody(),这段代码说明 mRequestBody这个变量就是它的参数。这个变量就存在与JsonRequest的构造函数中。

说明,JsonObjectRequest的传参必须通过构造来传,重写getParams()是无法传递的。

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

[java]  view plain copy print ?
  1. /** 
  2.  * JsonOnjectRequest Post 可能会出错的方式 
  3.  * 
  4.  * 这里的思路是调用JsonObjectRequest带参构造,传参进入不就得了? 
  5.  * 但是这里还是请求不到数据。 
  6.  * 原因是使用JsonObjectRequest 如果服务器接受的参数类型是 http://....?key1=value1&key2=value2, 
  7.  * 而不是json串{key1 : value1,key2 : value2...},参数就会传不进去。 
  8.  * 可以在 HurlStack.java 的addBodyIfExists方法中看到你传入的参数信息。 
  9.  */  
  10. private void jsonObjectRequestError2(){  
  11.   
  12.     String url = "http://route.showapi.com/213-3";  
  13.     Map<String,String> map = DummyData.getDummyData();  
  14.     JSONObject params = new JSONObject(map);  
  15.   
  16.     JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(Request.Method.POST,url,params, new Response.Listener<JSONObject>() {  
  17.         @Override  
  18.         public void onResponse(JSONObject response) {  
  19.             responseText.setText(response.toString());  
  20.         }  
  21.     }, new Response.ErrorListener() {  
  22.         @Override  
  23.         public void onErrorResponse(VolleyError error) {  
  24.             responseText.setText(error.getMessage());  
  25.         }  
  26.     });  
  27.     mQueue.add(jsonObjectRequest);  
  28. }  

仍然获取不到数据,原因看上面的注释。反馈的结果是没有传参给服务器。那就看看参数最终是在哪里提交给服务器的。

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。

[java]  view plain copy print ?
  1. /** Content type for request. */  
  2. private static final String PROTOCOL_CONTENT_TYPE =  
  3. String.format("application/json; charset=%s", PROTOCOL_CHARSET);  


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

[java]  view plain copy print ?
  1. @Override  
  2. public String getBodyContentType() {  
  3.     return "application/x-www-form-urlencoded; charset=" + getParamsEncoding();  
  4. }  

这段代码从Request.java中Copy。


自定义的Request

[java]  view plain copy print ?
  1. public class MyJsonObjectRequest extends JsonRequest<JSONObject> {  
  2.   
  3.     String stringRequest;  
  4.   
  5.     /** 
  6.      * 这里的method必须是Method.POST,也就是必须带参数。 
  7.      * 如果不想带参数,可以用JsonObjectRequest,给它构造参数传null。GET方式请求。 
  8.      * @param stringRequest 格式应该是 "key1=value1&key2=value2" 
  9.      */  
  10.   
  11.     public MyJsonObjectRequest(String url, String stringRequest,  
  12.                              Response.Listener<JSONObject> listener, Response.ErrorListener errorListener) {  
  13.         super(Method.POST, url,stringRequest , listener, errorListener);  
  14.         this.stringRequest = stringRequest;  
  15.     }  
  16.   
  17.     @Override  
  18.     public String getBodyContentType() {  
  19.         return "application/x-www-form-urlencoded; charset=" + getParamsEncoding();  
  20.     }  
  21.   
  22.     @Override  
  23.     protected Response<JSONObject> parseNetworkResponse(NetworkResponse response) {  
  24.         try {  
  25.             String jsonString = new String(response.data,  
  26.                     HttpHeaderParser.parseCharset(response.headers, PROTOCOL_CHARSET));  
  27.             return Response.success(new JSONObject(jsonString),  
  28.                     HttpHeaderParser.parseCacheHeaders(response));  
  29.         } catch (UnsupportedEncodingException e) {  
  30.             return Response.error(new ParseError(e));  
  31.         } catch (JSONException je) {  
  32.             return Response.error(new ParseError(je));  
  33.         }  
  34.     }  

请求方法:

[java]  view plain copy print ?
  1. private void jsonObjectRequestPostSuccess1(){  
  2.         String url = "http://route.showapi.com/213-3";  
  3.         Map<String,String> map = DummyData.getDummyData();  
  4.         String params = appendParameter(url,map);  
  5.   
  6.         MyJsonObjectRequest jsonObjectRequest = new MyJsonObjectRequest(url,params, new Response.Listener<JSONObject>() {  
  7.             @Override  
  8.             public void onResponse(JSONObject response) {  
  9.                 responseText.setText(response.toString());  
  10.             }  
  11.         }, new Response.ErrorListener() {  
  12.             @Override  
  13.             public void onErrorResponse(VolleyError error) {  
  14.                 responseText.setText(error.getMessage());  
  15.             }  
  16.         });  
  17.         mQueue.add(jsonObjectRequest);  
  18.     }  
  19.   
  20.   
  21.   
  22.     private String appendParameter(String url,Map<String,String> params){  
  23.         Uri uri = Uri.parse(url);  
  24.         Uri.Builder builder = uri.buildUpon();  
  25.         for(Map.Entry<String,String> entry:params.entrySet()){  
  26.             builder.appendQueryParameter(entry.getKey(),entry.getValue());  
  27.         }  
  28.         return builder.build().getQuery();  
  29.     }  


当然,也并非一定要自定义一个JsonObjectRequest不可,如果了解Request的继承关系和参数的传递流程,直接使用JsonObjectRequest也是可以的。

[java]  view plain copy print ?
  1. /** 
  2.  * 不想自定义,非得用JsonObjectRequestPost不可,只要你不嫌麻烦。 
  3.  */  
  4. private void jsonObjectRequestPostSuccess2(){  
  5.     String url = "http://route.showapi.com/213-3";  
  6.     Map<String,String> params = DummyData.getDummyData();  
  7.     final String mRequestBody = appendParameter(url,params);  
  8.   
  9.     JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(Request.Method.POST,url,nullnew Response.Listener<JSONObject>() {  
  10.         @Override  
  11.         public void onResponse(JSONObject response) {  
  12.             responseText.setText(response.toString());  
  13.         }  
  14.     }, new Response.ErrorListener() {  
  15.         @Override  
  16.         public void onErrorResponse(VolleyError error) {  
  17.             responseText.setText(error.getMessage());  
  18.         }  
  19.     }){  
  20.         @Override  
  21.         public String getBodyContentType() {  
  22.             return "application/x-www-form-urlencoded; charset=" + getParamsEncoding();  
  23.         }  
  24.   
  25.         @Override  
  26.         public byte[] getBody() {  
  27.             try {  
  28.                 return mRequestBody == null ? null : mRequestBody.getBytes(PROTOCOL_CHARSET);  
  29.             } catch (UnsupportedEncodingException uee) {  
  30.                 VolleyLog.wtf("Unsupported Encoding while trying to get the bytes of %s using %s",  
  31.                         mRequestBody, PROTOCOL_CHARSET);  
  32.                 return null;  
  33.             }  
  34.         }  
  35.     };  
  36.     mQueue.add(jsonObjectRequest);  
  37. }  
  38.   
  39. private String appendParameter(String url,Map<String,String> params){  
  40.     Uri uri = Uri.parse(url);  
  41.     Uri.Builder builder = uri.buildUpon();  
  42.     for(Map.Entry<String,String> entry:params.entrySet()){  
  43.         builder.appendQueryParameter(entry.getKey(),entry.getValue());  
  44.     }  
  45.     return builder.build().getQuery();  
  46. }  

总结:

使用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这个类。


[java]  view plain copy print ?
  1.   
源码

你可能感兴趣的:(Volley StringRequest和JSONObjectRequest使用几个细节)