介绍
对于应用开发团队来说,天气数据无法自行生产,需要从专门的数据提供者那里申请使用权并通过一定的通信手段获取。
精准详实的天气数据和天气预测需要调动国家级的资源采集和整理——这是一项专业的工作,由专业的机构来执行。好比几十年如一日的央视新闻联播之后的天气预报,其数据并非由中央电视台测量产生,而是由中央气象台或者国家气象局(其实是同一个单位,地点在北京市海淀区白石桥路46号)提供。
基于互联网提供天气数据的站点常见的有:
各类数据提供方的服务的申请和使用方式大同小异,营销策略、收费情况各有差异,实际商业开发中要根据自身产品情况酌情选择。
本项目使用和风天气提供的天气数据服务。服务的形式是Web API,已经为我们的iWeather
应用申请了响应的API Key:
下面我们来了解一下和风天气提供的天气数据API如何使用。
API的使用
通常的互联网开放平台API通过URL的形式提供给开发者。后者在自己的应用程序中访问该URL并附带上所需要的参数,便可获取所需要的数据。
返回的数据通常具有一定的格式(如XML
或JSON
等)。开发者编写程序解析这种格式并得到最终数据。
关于API的URL及其所需参数、返回数据的具体格式,开放平台通常会提供详细的说明文档以供参考,并提供示例代码以供开发者学习。
URL
我们获取天气数据主要通过访问以下的URL:
https://free-api.heweather.net/s6/weather?parameters
本接口包含了3-7天天气预报、实况天气、逐小时天气预报以及生活指数,有对应权限的用户可通过访问此接口一次性获取某一地区的上述所有天气数据。
其中,parameters
代表请求参数,包括必选和可选参数。所有请求参数均使用&进行分隔,参数值存在中文或特殊字符的情况,需要对参数进行 url encode。
请求参数
例如,我们通过这个API查询北京市近期天气情况,则需要确定以下参数:
-
location:我们可以查询城市列表中北京对应的城市ID为
CN101010100
-
key:这个在申请服务时由平台提供。我们应用的key为
d2ae781d61744d65a2ef2156eef2cb64
那么可以组成以下的URL:
https://free-api.heweather.net/s6/weather?location=CN101010100&key= d2ae781d61744d65a2ef2156eef2cb64
可以在浏览器中直接访问这个URL查看结果:
这就是服务平台根据我们请求和参数返回的JSON
格式的天气数据。
我们构造App无非就是像浏览器一样去访问一个WWW地址,然后处理结果。反过来说,浏览器本身就是这类应用中的一种。
BeJSON.com
关于JSON,详见百度百科 - JSON
JSON
是文本格式的数据,易于人类阅读。但是为了更加易于阅读,我们对页面中显示的内容进行格式化。将JSON数据全部选中并拷贝到剪贴板。在浏览器中访问以下地址:
http://www.bejson.com
该网站提供强大的JSON相关功能。我们随着开发进展逐步的来探索这些功能。默认的,我们看到以下页面内容:
将我们刚才得到的JSON数据粘贴到红框中的编辑区,并点击左下角的“格式化校验”按钮进行检查并格式化,得到重新排列的数据如下:
{
"HeWeather6": [{
"basic": {
"cid": "CN101010100",
"location": "北京",
"parent_city": "北京",
"admin_area": "北京",
"cnty": "中国",
"lat": "39.90498734",
"lon": "116.4052887",
"tz": "+8.00"
},
"update": {
"loc": "2019-03-24 10:55",
"utc": "2019-03-24 02:55"
},
"status": "ok",
"now": {
"cloud": "0",
"cond_code": "100",
"cond_txt": "晴",
"fl": "9",
"hum": "16",
"pcpn": "0.0",
"pres": "1016",
"tmp": "12",
"vis": "15",
"wind_deg": "271",
"wind_dir": "西风",
"wind_sc": "2",
"wind_spd": "8"
},
"daily_forecast": [{
"cond_code_d": "100",
"cond_code_n": "100",
"cond_txt_d": "晴",
"cond_txt_n": "晴",
"date": "2019-03-24",
"hum": "18",
"mr": "22:14",
"ms": "08:18",
"pcpn": "0.0",
"pop": "0",
"pres": "1006",
"sr": "06:10",
"ss": "18:31",
"tmp_max": "19",
"tmp_min": "4",
"uv_index": "5",
"vis": "25",
"wind_deg": "204",
"wind_dir": "西南风",
"wind_sc": "3-4",
"wind_spd": "14"
}, {
"cond_code_d": "100",
"cond_code_n": "100",
"cond_txt_d": "晴",
"cond_txt_n": "晴",
"date": "2019-03-25",
"hum": "27",
"mr": "23:20",
"ms": "08:54",
"pcpn": "0.0",
"pop": "0",
"pres": "1012",
"sr": "06:08",
"ss": "18:32",
"tmp_max": "21",
"tmp_min": "5",
"uv_index": "5",
"vis": "25",
"wind_deg": "231",
"wind_dir": "西南风",
"wind_sc": "1-2",
"wind_spd": "4"
}, {
"cond_code_d": "100",
"cond_code_n": "100",
"cond_txt_d": "晴",
"cond_txt_n": "晴",
"date": "2019-03-26",
"hum": "41",
"mr": "00:00",
"ms": "09:33",
"pcpn": "0.0",
"pop": "0",
"pres": "1007",
"sr": "06:06",
"ss": "18:33",
"tmp_max": "19",
"tmp_min": "4",
"uv_index": "5",
"vis": "25",
"wind_deg": "104",
"wind_dir": "东南风",
"wind_sc": "1-2",
"wind_spd": "2"
}, {
"cond_code_d": "100",
"cond_code_n": "100",
"cond_txt_d": "晴",
"cond_txt_n": "晴",
"date": "2019-03-27",
"hum": "20",
"mr": "00:22",
"ms": "10:16",
"pcpn": "0.0",
"pop": "0",
"pres": "1018",
"sr": "06:05",
"ss": "18:34",
"tmp_max": "22",
"tmp_min": "4",
"uv_index": "6",
"vis": "25",
"wind_deg": "298",
"wind_dir": "西北风",
"wind_sc": "3-4",
"wind_spd": "15"
}, {
"cond_code_d": "100",
"cond_code_n": "101",
"cond_txt_d": "晴",
"cond_txt_n": "多云",
"date": "2019-03-28",
"hum": "16",
"mr": "01:20",
"ms": "11:03",
"pcpn": "0.0",
"pop": "25",
"pres": "1006",
"sr": "06:03",
"ss": "18:35",
"tmp_max": "15",
"tmp_min": "4",
"uv_index": "6",
"vis": "25",
"wind_deg": "205",
"wind_dir": "西南风",
"wind_sc": "1-2",
"wind_spd": "2"
}, {
"cond_code_d": "101",
"cond_code_n": "101",
"cond_txt_d": "多云",
"cond_txt_n": "多云",
"date": "2019-03-29",
"hum": "34",
"mr": "02:11",
"ms": "11:53",
"pcpn": "0.0",
"pop": "25",
"pres": "1005",
"sr": "06:02",
"ss": "18:36",
"tmp_max": "15",
"tmp_min": "3",
"uv_index": "5",
"vis": "16",
"wind_deg": "179",
"wind_dir": "南风",
"wind_sc": "1-2",
"wind_spd": "8"
}, {
"cond_code_d": "100",
"cond_code_n": "101",
"cond_txt_d": "晴",
"cond_txt_n": "多云",
"date": "2019-03-30",
"hum": "18",
"mr": "02:57",
"ms": "12:46",
"pcpn": "0.0",
"pop": "0",
"pres": "1018",
"sr": "06:00",
"ss": "18:37",
"tmp_max": "16",
"tmp_min": "4",
"uv_index": "6",
"vis": "25",
"wind_deg": "177",
"wind_dir": "南风",
"wind_sc": "1-2",
"wind_spd": "3"
}],
"hourly": [{
"cloud": "0",
"cond_code": "100",
"cond_txt": "晴",
"dew": "-13",
"hum": "23",
"pop": "0",
"pres": "1007",
"time": "2019-03-24 13:00",
"tmp": "16",
"wind_deg": "230",
"wind_dir": "西南风",
"wind_sc": "3-4",
"wind_spd": "14"
}, {
"cloud": "0",
"cond_code": "100",
"cond_txt": "晴",
"dew": "-11",
"hum": "18",
"pop": "0",
"pres": "1008",
"time": "2019-03-24 16:00",
"tmp": "19",
"wind_deg": "263",
"wind_dir": "西风",
"wind_sc": "3-4",
"wind_spd": "24"
}, {
"cloud": "0",
"cond_code": "100",
"cond_txt": "晴",
"dew": "-13",
"hum": "23",
"pop": "0",
"pres": "1008",
"time": "2019-03-24 19:00",
"tmp": "13",
"wind_deg": "268",
"wind_dir": "西风",
"wind_sc": "1-2",
"wind_spd": "7"
}, {
"cloud": "0",
"cond_code": "100",
"cond_txt": "晴",
"dew": "-9",
"hum": "36",
"pop": "0",
"pres": "1006",
"time": "2019-03-24 22:00",
"tmp": "11",
"wind_deg": "260",
"wind_dir": "西风",
"wind_sc": "1-2",
"wind_spd": "6"
}, {
"cloud": "0",
"cond_code": "100",
"cond_txt": "晴",
"dew": "-11",
"hum": "48",
"pop": "0",
"pres": "1007",
"time": "2019-03-25 01:00",
"tmp": "9",
"wind_deg": "264",
"wind_dir": "西风",
"wind_sc": "1-2",
"wind_spd": "5"
}, {
"cloud": "0",
"cond_code": "100",
"cond_txt": "晴",
"dew": "-14",
"hum": "54",
"pop": "0",
"pres": "1010",
"time": "2019-03-25 04:00",
"tmp": "5",
"wind_deg": "267",
"wind_dir": "西风",
"wind_sc": "1-2",
"wind_spd": "9"
}, {
"cloud": "0",
"cond_code": "100",
"cond_txt": "晴",
"dew": "-11",
"hum": "46",
"pop": "0",
"pres": "1009",
"time": "2019-03-25 07:00",
"tmp": "6",
"wind_deg": "279",
"wind_dir": "西风",
"wind_sc": "1-2",
"wind_spd": "8"
}, {
"cloud": "0",
"cond_code": "100",
"cond_txt": "晴",
"dew": "-12",
"hum": "31",
"pop": "0",
"pres": "1005",
"time": "2019-03-25 10:00",
"tmp": "12",
"wind_deg": "242",
"wind_dir": "西南风",
"wind_sc": "1-2",
"wind_spd": "4"
}],
"lifestyle": [{
"type": "comf",
"brf": "舒适",
"txt": "白天不太热也不太冷,风力不大,相信您在这样的天气条件下,应会感到比较清爽和舒适。"
}, {
"type": "drsg",
"brf": "较舒适",
"txt": "建议着薄外套、开衫牛仔衫裤等服装。年老体弱者应适当添加衣物,宜着夹克衫、薄毛衣等。"
}, {
"type": "flu",
"brf": "较易发",
"txt": "昼夜温差较大,较易发生感冒,请适当增减衣服。体质较弱的朋友请注意防护。"
}, {
"type": "sport",
"brf": "较适宜",
"txt": "天气较好,但因风力稍强,户外可选择对风力要求不高的运动,推荐您进行室内运动。"
}, {
"type": "trav",
"brf": "适宜",
"txt": "天气较好,风稍大,但温度适宜,是个好天气哦。适宜旅游,您可以尽情地享受大自然的无限风光。"
}, {
"type": "uv",
"brf": "中等",
"txt": "属中等强度紫外线辐射天气,外出时建议涂擦SPF高于15、PA+的防晒护肤品,戴帽子、太阳镜。"
}, {
"type": "cw",
"brf": "较适宜",
"txt": "较适宜洗车,未来一天无雨,风力较小,擦洗一新的汽车至少能保持一天。"
}, {
"type": "air",
"brf": "良",
"txt": "气象条件有利于空气污染物稀释、扩散和清除,可在室外正常活动。"
}]
}]
}
数据中各部分具体含义参考和风天气文档 - 常规天气数据集合
JSON to Java
仔细研究JSON数据,可以整理出一个树形结构:
天气数据结构此树形结构很清晰的反映了天气数据内部的逻辑结构:
- 总的天气数据实体
HeWeather6
包含了basic
、update
、status
、now
、daily_forecast
、hourly
和lifestyle
等7项实体,各自描述了不同维度的天气信息 - 其中
daily_forecast
、hourly
和lifestyle
这3个实体分别对应一个数组,内部包含多项同类数据,顶层实体HeWeather6
分别与它们形成了一对多的关系 - 其余各项也含有各自内部复杂的数据项
这样的实体结构反映到Java语言程序中,通常定义一组相关的类来进行描述。由于实体众多,各自定义也需要付出比较大的工作量。由于其中的逻辑本身比较简单,因此目前已经有了自动化的代码生成工具来进行代劳。上一节提到的bejson.com也提供这样的功能。
首先在我们的源码结构中定义一个子包com.dmtech.iw.entity
——选中App包名com.dmtech.iw
,右键单击选择“New -> Package”,在弹出的对话框中填写子包名entity
:
确认后子包 com.dmtech.iw.entity
即出现在代码结构视图中:
进入BeJSON.com主页,在导航栏中选择“JSON相关 -> JSON生成Java实体类”跳转到类似下图的页面:
删掉编辑区中的样例代码,将我们之前通过浏览器访问API后返回的JSON数据粘贴到编辑区,并填写以下内容:
-
Class:填写根实体名称即
HeWeather6
- *** Package***:填写刚才创建的包名,即
com.dmtech.iw.entity
具体如图:
屏幕快照 2019-03-24 15.24.35.png点击“生成JavaBean”按钮可以再下方查看生成的Java代码,点击紧邻的“下载代码”按钮即可下载生成的Java文件。
下载下来的是一个名为bejson_gen_beans.zip
的压缩包,将其解压,查看其文件结构:
观察目录结构,恰好是在我们提供的包名之下生成了一组类,类名与JSON中各实体名称一一对应。将红色方框标出的全部文件拷贝到Android Studio中com.dmtech.iw.entity
包下(直接选中后拷贝粘贴)。打开源文件HeWeather6.java
查看:
/**
* Copyright 2019 bejson.com
*/
package com.dmtech.iw.entity;
import java.util.List;
/**
* Auto-generated: 2019-03-24 15:25:14
*
* @author bejson.com (i@bejson.com)
* @website http://www.bejson.com/java2pojo/
*/
public class HeWeather6 {
private Basic basic;
private Update update;
private String status;
private Now now;
private List<Daily_forecast> daily_forecast;
private List<Hourly> hourly;
private List<Lifestyle> lifestyle;
public void setBasic(Basic basic) {
this.basic = basic;
}
public Basic getBasic() {
return basic;
}
public void setUpdate(Update update) {
this.update = update;
}
public Update getUpdate() {
return update;
}
public void setStatus(String status) {
this.status = status;
}
public String getStatus() {
return status;
}
public void setNow(Now now) {
this.now = now;
}
public Now getNow() {
return now;
}
public void setDaily_forecast(List<Daily_forecast> daily_forecast) {
this.daily_forecast = daily_forecast;
}
public List<Daily_forecast> getDaily_forecast() {
return daily_forecast;
}
public void setHourly(List<Hourly> hourly) {
this.hourly = hourly;
}
public List<Hourly> getHourly() {
return hourly;
}
public void setLifestyle(List<Lifestyle> lifestyle) {
this.lifestyle = lifestyle;
}
public List<Lifestyle> getLifestyle() {
return lifestyle;
}
}
这是一个典型的Java Bean类(就是用来描述实体的类),包含一组属性,以及读取和设置这组属性的getter
和setter
方法。可以看到,这组属性种包含了前面提到的全部7个实体。其余各个实体类在结构上类似。
用术语来说,模型(Model)定义完毕,接下来编写程序实现天气数据的获取。
本次新增或修改的文件有:
new file: app/src/main/java/com/dmtech/iw/entity/Basic.java
new file: app/src/main/java/com/dmtech/iw/entity/Daily_forecast.java
new file: app/src/main/java/com/dmtech/iw/entity/HeWeather6.java
new file: app/src/main/java/com/dmtech/iw/entity/Hourly.java
new file: app/src/main/java/com/dmtech/iw/entity/Lifestyle.java
new file: app/src/main/java/com/dmtech/iw/entity/Now.java
new file: app/src/main/java/com/dmtech/iw/entity/Update.java
网友评论