Android网络与数据存储

第二章学习


在线请求天气API,并解析其中的json数据予以显示

概要:

请求互联网信息提供商并取得返回的数据使用到HttpURLConnection,等待数据下载成功得到的Json,把它 解析成程序可利用的数据,使用到JSONObject


使用和风天气的API作为范例,只要注册就可免费用的还凑合的天气预报平台

HttpURLConnection类的使用

本来Android发送Http请求拥有两种方式,分别是HttpURLConnection和HttpClient,但在Android 6.0时,HttpClient已经彻底从SDK里消失了,虽然是个重要的类,包括如今的阿里云服务中,也依然给我们提供了基于HttpClient的API请求SDK,由于版本问题,我也难以使用。(此处花费一整天用于折腾阿里云市场里购买的天气预报API,卒。)

所以,本着追赶潮流的思想,这次的App只使用HttpURLConnection进行网络请求。

0.生成HttpURLConnection对象:

API接口: https://api.heweather.com/x3/weather?cityid=城市ID&key=你的认证key
复制代码

注册好以后,替换掉“城市ID”以及“你的认证key”字段即可使用

URL url = new URL("https://api.heweather.com/x3/weather?cityid=城市ID&key=你的认证key");
//将字符串转化为URL对象
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
复制代码

1.给HttpURLConnection对象设置请求方式: 接下来给这个connection设置一种向网络服务器请求的方式,实际开发中我们用得较多的方式是Get和Post:

connection.setRequestMethod("GET");
复制代码
  • Get:请求获取Request-URI所标识的资源
  • POST:在Request-URI所标识的资源后附加新的数据
  • HEAD 请求获取由Request-URI所标识的资源的响应信息报头
  • PUT:请求服务器存储一个资源,并用Request-URI作为其标识
  • DELETE:请求服务器删除Request-URI所标识的资源
  • TRACE:请求服务器回送收到的请求信息,主要用于测试或诊断
  • CONNECT:保留将来使用
  • OPTIONS:请求查询服务器的性能,或者查询与资源相关的选项

说得太难懂,用GET和POST举个例子:

  • GET:在请求的URL地址后以?的形式带上交给服务器的数据,多个数据之间以&进行分隔, 但数据容量通常不能超过2K,比如: “https://api.heweather.com/x3/weather?cityid=城市ID&key=你的认证key” 这种就是GET
  • POST: 这个则可以在请求的实体内容中向服务器发送数据,传输没有数量限制

2.定制HttpURLConnection并获取链接状态:

设置连接超过8000毫秒则为超时状态,同理还有读取超时

connection.setConnectTimeout(8000);
connection.setReadTimeout(8000);
复制代码

还可以获取当前连接的状态

int responseCode = connection.getResponseCode();
复制代码

此时responseCode将会有非常多种数字有可能被返回,如“404”

  • 100~199 : 成功接受请求,客户端需提交下一次请求才能完成整个处理过程
  • 200: OK,客户端请求成功
  • 300~399:请求资源已移到新的地址(302,307,304)
  • 401:请求未授权,改状态代码需与WWW-Authenticate报头域一起使用
  • 403:Forbidden,服务器收到请求,但是拒绝提供服务
  • 404:Not Found,请求资源不存在,这个就不用说啦
  • 500:Internal Server Error,服务器发生不可预期的错误
  • 503:Server Unavailable,服务器当前不能处理客户端请求,一段时间后可能恢复正常

当然最好我们的返回代码是200,此时就成功了

3.获取输入流并转换为String类:

try {
    InputStream in = connection.getInputStream();
    BufferedReader reader = new BufferedReader(new InputStreamReader(in, "UTF-8"));
    StringBuilder response = new StringBuilder();
    String line;
    while ((line = reader.readLine()) != null) {
            response.append(line).append("\r\n");
        }
    reader.close();
        } catch (Exception e) {
         e.printStackTrace();
     } finally {
     if (connection != null) {
     connection.disconnect();
     }
}
复制代码

从网络请求的返回中获取输入流,并进行IO操作,结束后记得关掉BufferedReader和HttpURLConnection实例。此时,完成了一系列的操作后,我们取得了从网络返回的数据。

咳咳,插句题外话,当然也可以将返回数据解析为比特流返回

public class ToByteUtil {
    //从流中读取数据
    public static byte[] read(InputStream in) throws Exception{
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int len = 0;
        while((len = in.read(buffer)) != -1)
        //一行行读取,只要不为-1则表示没读完
        {
            out.write(buffer,0,len);
        }
        in.close();
        return out.toByteArray();
    }
}
复制代码

以上成功获得了一个String,当前的API商提供给我们的是Json文件,Json文件并不会直接被系统识别,需要解析出其中的每一项,然后利用起来;

我们可以将对数据库进行的操作封装为一系列方法,如下:

4.看看Json的格式:

{"HeWeather data service 3.0":[{"basic":{"city":"大连","cnty":"中国","id":"CN101070201","lat":"38.944000","lon":"121.576000","update":{"loc":"2015-07-15 10:43","utc":"2015-07-15 02:46:14"}},"status":"ok","aqi":{"city":{"aqi":"71","co":"1","no2":"75","o3":"101","pm10":"89","pm25":"44","qlty":"良","so2":"27"}},"alarms":[{"level":"橙色","stat":"预警中","title":"辽宁省大连市气象台发布高温橙色预警","txt":"大连市气象台2015年07月14日13时31分发布高温橙色预警信号:预计14日下午至傍晚,旅顺口区局部最高气温将达到37℃以上,请注意防范。","type":"高温"}],"now":{"cond":{"code":"100","txt":"晴"},"fl":"33","hum":"28","pcpn":"0","pres":"1005","tmp":"32","vis":"10","wind":{"deg":"350","dir":"东北风","sc":"4-5","spd":"11"}},"daily_forecast":[{"date":"2015-07-15","astro":{"sr":"04:40","ss":"19:19"},"cond":{"code_d":"100","code_n":"101","txt_d":"晴","txt_n":"多云"},"hum":"48","pcpn":"0.0","pop":"0","pres":"1005","tmp":{"max":"33","min":"24"},"vis":"10","wind":{"deg":"192","dir":"东南风","sc":"4-5","spd":"11"}},{"date":"2015-07-16","astro":{"sr":"04:40","ss":"19:18"},"cond":{"code_d":"104","code_n":"104","txt_d":"阴","txt_n":"阴"},"hum":"82","pcpn":"2.7","pop":"82","pres":"1008","tmp":{"max":"27","min":"23"},"vis":"10","wind":{"deg":"116","dir":"东南风","sc":"4-5","spd":"11"}},{"date":"2015-07-17","astro":{"sr":"04:41","ss":"19:17"},"cond":{"code_d":"101","code_n":"100","txt_d":"多云","txt_n":"晴"},"hum":"70","pcpn":"0.1","pop":"11","pres":"1006","tmp":{"max":"28","min":"22"},"vis":"10","wind":{"deg":"172","dir":"西风","sc":"4-5","spd":"11"}}],"hourly_forecast":[{"date":"2015-07-15 10:00","hum":"51","pop":"0","pres":"1006","tmp":"32","wind":{"deg":"127","dir":"东南风","sc":"微风","spd":"4"}},{"date":"2015-07-15 13:00","hum":"49","pop":"0","pres":"1005","tmp":"34","wind":{"deg":"179","dir":"南风","sc":"微风","spd":"7"}},{"date":"2015-07-15 16:00","hum":"54","pop":"0","pres":"1005","tmp":"31","wind":{"deg":"216","dir":"西南风","sc":"微风","spd":"6"}},{"date":"2015-07-15 19:00","hum":"62","pop":"0","pres":"1005","tmp":"29","wind":{"deg":"192","dir":"西南风","sc":"微风","spd":"4"}},{"date":"2015-07-15 22:00","hum":"62","pop":"0","pres":"1006","tmp":"26","wind":{"deg":"154","dir":"东南风","sc":"微风","spd":"10"}}],"suggestion":{"comf":{"brf":"较舒适","txt":"白天天气晴好,您在这种天气条件下,会感觉早晚凉爽、舒适,午后偏热。"},"cw":{"brf":"较不宜","txt":"较不宜洗车,未来一天无雨,风力较大,如果执意擦洗汽车,要做好蒙上污垢的心理准备。"},"drsg":{"brf":"炎热","txt":"天气炎热,建议着短衫、短裙、短裤、薄型T恤衫等清凉夏季服装。"},"flu":{"brf":"少发","txt":"各项气象条件适宜,发生感冒机率较低。但请避免长期处于空调房间中,以防感冒。"},"sport":{"brf":"较适宜","txt":"天气较好,但风力较大,推荐您进行室内运动,若在户外运动请注意防风。"},"trav":{"brf":"适宜","txt":"天气较好,是个好天气哦。稍热但是风大,能缓解炎热的感觉,适宜旅游,可不要错过机会呦!"},"uv":{"brf":"强","txt":"紫外线辐射强,建议涂擦SPF20左右、PA++的防晒护肤品。避免在10点至14点暴露于日光下。"}}}]}
复制代码

妈呀密密麻麻的,我有密集恐惧症啊…………为了减少长度,我压缩了json文件,看不清,没关系,复制到runoob.com 网页上就可以清楚看见文件的结构

{"now":{"cond":{"code":"100","txt":"晴"},"fl":"33","hum":"28","pcpn":"0","pres":"1005","tmp":"32","vis":"10","wind":{"deg":"350","dir":"东北风","sc":"4-5","spd":"11"}}}
复制代码



json文件都是以键值对进行保存“键:值”,而如果值是个数组,则按如下表示

{Key:["status":"ok","city":"大连"]}
复制代码

稍加观察,并不复杂

5.解析数据:

public class JsonUtil {
    public static ContentValues parseJSONToWeather(String jsonData) {
        ContentValues contentValues = new ContentValues();
        try {
            JSONObject jsonObject = new JSONObject(jsonData);
//此时将字符串转变为一个JSONObject实例,形象的说法就是{"key":"value"}这就是个object
            JSONArray jsonArray = jsonObject.getJSONArray("HeWeather data service 3.0");
//这里在此object中get了一个数组(JSONArray),输入这个数组的key,即可得到
            JSONObject allJsonObject = jsonArray.getJSONObject(0);
            String status = allJsonObject.getString("status");
            if (status.equals("ok")) {
                JSONObject basic = allJsonObject.getJSONObject("basic");
                contentValues.put("id", basic.getString("id"));
                contentValues.put("city", basic.getString("city"));
                JSONObject now = allJsonObject.getJSONObject("now");
                JSONObject now_cond = now.getJSONObject("cond");
                contentValues.put("now_cond_txt", now_cond.getString("txt"));
                contentValues.put("now_tmp", now.getString("tmp"));
                JSONArray daily_forecast = allJsonObject.getJSONArray("daily_forecast");
                for (int i = 0; i < 5; i++) {
                    String num = null;
                    switch (i){
                        case 0:
                            num = "first";
                            break;
                        case 1:
                            num = "second";
                            break;
                        case 2:
                            num = "third";
                            break;
                        case 3:
                            num = "fourth";
                            break;
                        case 4:
                            num = "fifth";
                            break;
                    }
                    JSONObject data = daily_forecast.getJSONObject(i);
                    contentValues.put(num + "_date", data.getString("date"));
                    JSONObject cond = data.getJSONObject("cond");
                    contentValues.put(num + "_txt_d", cond.getString("txt_d"));
                    contentValues.put(num + "_txt_n", cond.getString("txt_n"));
                    JSONObject tmp = data.getJSONObject("tmp");
                    contentValues.put(num + "_tmp_max", tmp.getString("max"));
                    contentValues.put(num + "_tmp_min", tmp.getString("min"));
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return contentValues;     //包含了所有的数据库信息
    }
}
复制代码

代码解析:

基本上就Object和Array这两种东西,慢慢按照结构把最终的值使用类似getString的方法取到。慢慢扣出了我需要提取的数据……

最后我将取得的键值组成了ContentValues,,其实本来用Map类型进行保存的,然后在外部在再次转换为ContentValues进行数据库操作,后来转念一想这两东西结构不是一样的嘛!!!于是省了一步……

因为json里的数据名有重名的,我稍微利用for循环和switch进行重命名,数据库不接受数字开头的字符串当列名……

将数据缓存到数据库,而页面显示时,直接从数据库提取数据,最终效果就是这样了

-完-