Android网络与数据存储
第二章学习
在线请求天气API,并解析其中的json数据予以显示####
概要:
请求互联网信息提供商并取得返回的数据使用到HttpURLConnection,等待数据下载成功得到的Json,把它 解析成程序可利用的数据,使用到JSONObject
使用和风天气的API作为范例,只要注册就可免费用的还凑合的天气预报平台
http://www.heweather.com/
1.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文件,看不清,没关系,复制到http://www.runoob.com/jsontool 网页上就可以清楚看见文件的结构
{"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进行重命名,数据库不接受数字开头的字符串当列名……
将数据缓存到数据库,而页面显示时,直接从数据库提取数据,最终效果就是这样了
-完-