一、Volley StringRequest
下面是百度api提供的免费的接口‘天气查询-查看可用城市列表’,在百度apistore中有提供(天气查询)。
看下官方提供的代码:
- String httpUrl = "http://apis.baidu.com/apistore/weatherservice/citylist";
- String httpArg = "cityname=%E6%9C%9D%E9%98%B3";
- String jsonResult = request(httpUrl, httpArg);
- System.out.println(jsonResult);
-
-
-
-
-
-
-
-
- public static String request(String httpUrl, String httpArg) {
- BufferedReader reader = null;
- String result = null;
- StringBuffer sbf = new StringBuffer();
- httpUrl = httpUrl + "?" + httpArg;
-
- try {
- URL url = new URL(httpUrl);
- HttpURLConnection connection = (HttpURLConnection) url
- .openConnection();
- connection.setRequestMethod("GET");
-
- connection.setRequestProperty("apikey", "您自己的apikey");
- connection.connect();
- InputStream is = connection.getInputStream();
- reader = new BufferedReader(new InputStreamReader(is, "UTF-8"));
- String strRead = null;
- while ((strRead = reader.readLine()) != null) {
- sbf.append(strRead);
- sbf.append("\r\n");
- }
- reader.close();
- result = sbf.toString();
- } catch (Exception e) {
- e.printStackTrace();
- }
- return result;
- }
现在用Volley 的StringRequest实现它的接口。需要关注的几个地方:
URL:
- httpUrl = httpUrl + "?" + httpArg;
Request Method:
- connection.setRequestMethod("GET");
Header :
- connection.setRequestProperty("apikey", "您自己的apikey");
参数带在url后面,如果有多个参数用‘&’分割
如果是拼接参数需要注意最前面还有一个‘?’
GET请求
带了http header参数,对应connection.setRequestProperty("","");
下面使用Volley的StringRequest的两种方式来实现上面的请求。
第一种:StringRequest Method.GET
把参数带在url后面,使用StringRequest的不带参构造。Header参数重写getHeaders方法,对应 connection.setRequestProperty("apikey", "您自己的apikey");
- private String url = "http://apis.baidu.com/apistore/weatherservice/citylist";
- private String params = "cityname=朝阳";
- private void stringRequestWithGet(){
- url = url + "?" + params;
- StringRequest stringRequest = new StringRequest(Request.Method.GET, url, new Response.Listener<String>() {
- @Override
- public void onResponse(String response) {
- try {
-
- JSONObject jsonObject = new JSONObject(response);
- responseText.setText(jsonObject.toString());
- } catch (JSONException e) {
- e.printStackTrace();
- }
- }
- }, new Response.ErrorListener() {
- @Override
- public void onErrorResponse(VolleyError error) {
- responseText.setText(error.getMessage());
- }
- }){
- @Override
- public Map<String, String> getHeaders() throws AuthFailureError {
- Map<String,String> map = new HashMap<>();
- map.put("apikey","f71e5f1e08cd5a7e42a7e9aa70d22458");
- return map;
- }
- };
- mQueue.add(stringRequest);
- }
第二种:StringRequest Method.POST
Method:改为POST
Params:参数不带在Url末尾,而是重写StringRequest的getParams()。
注意:我以这种方式请求百度apistore中 ‘天气查询-查看可用城市列表’接口,没有成功。但是访问其他提供商的API是成功的。可能是这个接口不支持Post请求,或者我少传了什么参数吧。
- private void stringRequestWithPost(){
- StringRequest stringRequest = new StringRequest(Request.Method.POST, url, new Response.Listener<String>() {
- @Override
- public void onResponse(String response) {
- try {
-
- JSONObject jsonObject = new JSONObject(response);
- responseText.setText(jsonObject.toString());
- } catch (JSONException e) {
- e.printStackTrace();
- }
- }
- }, new Response.ErrorListener() {
- @Override
- public void onErrorResponse(VolleyError error) {
- responseText.setText(error.getMessage());
- }
- }){
- @Override
- protected Map<String, String> getParams() throws AuthFailureError {
- Map<String,String> map = new HashMap<>();
- map.put("cityname","朝阳");
- return map;
- }
-
- @Override
- public Map<String, String> getHeaders() throws AuthFailureError {
- Map<String,String> map = new HashMap<>();
- map.put("apikey","f71e5f1e08cd5a7e42a7e9aa70d22458");
- return map;
- }
- };
- mQueue.add(stringRequest);
- }
二、Volley JsonObjectRequest
下面介绍使用JsonObjectRequest,以及使用JsonObjectRequest会遇到的一些坑。
由于百度apistore中我以post请求一直没成功过,不知道是什么问题,下面我换了一家API提供商 showapi 的 'QQ音乐十八大排行榜’接口来进行GET请求和POST请求。
先说Method.POST,遇到的坑会多一点。
第一种:JsonObjectRequest Method.POST
错误写法
-
-
-
-
- private void jsonObjectRequestError1(){
-
- String url = "http://route.showapi.com/213-3";
- final Map<String,String> params = DummyData.getDummyData();
-
- JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(Request.Method.POST,url,null, 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());
- }
- }){
- @Override
- protected Map<String, String> getParams() throws AuthFailureError {
- return params;
- }
- };
- mQueue.add(jsonObjectRequest);
- }
JsonObjectRequest构造中的参数传null,和StringRequest一样去重写getParams()方法来传参。貌似是没问题的?
使用这段代码拿不到服务器返回的数据,反馈的结果是没有传参给服务器。那就看看参数最终是在哪里提交给服务器的。
Debug HurlStack.java中的addBodyIfExists()这个方法,发现request.getBody()==null,也就是没有接收到参数。
StringRequest可以重写getParams来传参是因为Request中的这个方法:
Request.java
- public byte[] getBody() throws AuthFailureError {
- Map<String, String> 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的构造传递参数。又会出现什么问题?
-
-
-
-
-
-
-
-
-
- private void jsonObjectRequestError2(){
-
- String url = "http://route.showapi.com/213-3";
- Map<String,String> map = DummyData.getDummyData();
- JSONObject params = new JSONObject(map);
-
- JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(Request.Method.POST,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);
- }
仍然获取不到数据,原因看上面的注释。反馈的结果是没有传参给服务器。那就看看参数最终是在哪里提交给服务器的。
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。
-
- private static final String PROTOCOL_CONTENT_TYPE =
- String.format("application/json; charset=%s", PROTOCOL_CHARSET);
请求的是一个json串,但是这里自定义的Request传入的参数不是一个json串。所以还需要在自定义的类中重写一个方法:
- @Override
- public String getBodyContentType() {
- return "application/x-www-form-urlencoded; charset=" + getParamsEncoding();
- }
这段代码从Request.java中Copy。
自定义的Request
- 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));
- }
- }
请求方法:
- 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也是可以的。
-
-
-
- private void jsonObjectRequestPostSuccess2(){
- String url = "http://route.showapi.com/213-3";
- Map<String,String> params = DummyData.getDummyData();
- final String mRequestBody = appendParameter(url,params);
-
- JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(Request.Method.POST,url,null, 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());
- }
- }){
- @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<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 请求服务器,它的参数是以 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这个类。
源码