您的位置:首页 > 移动开发 > Android开发

Android访问中央气象台的天气预报API得到天气数据

2013-05-15 10:30 288 查看
http://blog.csdn.net/xianqiang1/article/details/7054045

在用Android获取天气预报数据时,大家一定会首先想到Google的天气预报API,其实除了Google的天气预报API,免费的天气预报接口还有http://www.webservicex.net/globalweather.asmx?op=GetWeather、http://webservice.webxml.com.cn/WebServices/WeatherWS.asmx和中央气象台的天气预报,这三个是我最近试过的都可以访问,网上其实还介绍的有www.ayandy.com,不过我没试过 ^_^

现在就来谈一谈这几个服务,google的就不说了,www.webservicex.net的这个是一个国外的,获取中国国内的有点麻烦,且只有当天的天气,所以果断放弃...呵呵

而webservice.webxml.com.cn的这个确实不错,访问的数据是来自中国气象局http://www.cma.gov.cn/ 数据的准确就不用说了,但他分为付费和免费的,免费的服务有点不稳定,我就曾遇到一回,所以也不是很好,最后就只剩下中央气象台的天气预报的API了,这个我不想说太多,虽然获取时你首先要知道对应的城市码,有点麻烦,其它的如稳定性与广阔性也是很一流的,它可以精确到县和区.下面就直奔主题:

这个服务的天气预报的请求地址是:http://m.weather.com.cn/data/101070201.html,这个文本就是城市天气URL,101070201代表的为对应地区的编码,执行URL,得到一个返回文本,是JSON格式的,如下(经过格式化):

[plain] view
plaincopy

{

"weatherinfo":{

"city":"成都",

"city_en":"chengdu",

"date_y":"2011年11月30日",

"date":"辛卯年",

"week":"星期三",

"fchh":"11", //预报发布时间

"cityid":"101270101",

"temp1":"13℃~10℃",

"temp2":"14℃~6℃",

"temp3":"13℃~5℃",

"temp4":"14℃~8℃",

"temp5":"10℃~8℃",

"temp6":"11℃~6℃",

"tempF1":"55.4℉~50℉",

"tempF2":"57.2℉~42.8℉",

"tempF3":"55.4℉~41℉",

"tempF4":"57.2℉~46.4℉",

"tempF5":"50℉~46.4℉",

"tempF6":"51.8℉~42.8℉",

"weather1":"阴转多云",

"weather2":"多云转晴",

"weather3":"多云转阴",

"weather4":"阵雨",

"weather5":"阵雨转小雨",

"weather6":"小雨转阴",

"img1":"2",

"img2":"1",

"img3":"1",

"img4":"0",

"img5":"1",

"img6":"2",

"img7":"3",

"img8":"99",

"img9":"3",

"img10":"7",

"img11":"7",

"img12":"2",

"img_single":"2",

"img_title1":"阴",

"img_title2":"多云",

"img_title3":"多云",

"img_title4":"晴",

"img_title5":"多云",

"img_title6":"阴",

"img_title7":"阵雨",

"img_title8":"阵雨",

"img_title9":"阵雨",

"img_title10":"小雨",

"img_title11":"小雨",

"img_title12":"阴",

"img_title_single":"阴",

"wind1":"北风小于3级",

"wind2":"北风小于3级",

"wind3":"北风小于3级",

"wind4":"南风转北风小于3级",

"wind5":"北风小于3级",

"wind6":"北风小于3级",

"fx1":"北风",

"fx2":"北风",

"fl1":"小于3级",

"fl2":"小于3级",

"fl3":"小于3级",

"fl4":"小于3级",

"fl5":"小于3级",

"fl6":"小于3级",

"index":"舒适",

"index_d":"建议着薄型套装或牛仔衫裤等春秋过渡装。年老体弱者宜着套装、夹克衫等。",

"index48":"舒适",

"index48_d":"建议着薄型套装或牛仔衫裤等春秋过渡装。年老体弱者宜着套装、夹克衫等。",

"index_uv":"最弱",

"index48_uv":"弱",

"index_xc":"适宜",

"index_tr":"很适宜",

"index_co":"较舒适",

"st1":"14",

"st2":"10",

"st3":"14",

"st4":"6",

"st5":"13",

"st6":"4",

"index_cl":"较适宜",

"index_ls":"不太适宜",

"index_ag":"较易发"

}

}

上面显示的信息一目了然,就不用我多说了(在得到天气数据的解析android2.2可用JSONObject对象参考:http://henzil.easymorse.com/?p=242),关键是获得对应地区的城市码了。

访问http://m.weather.com.cn/data5/city.xml得到一级列表(省、直辖市、自治区),结果用逗号隔开,id和城市名称使用竖线“|”隔开;结果示例如下:

[plain] view
plaincopy

01|北京,02|上海,03|天津,04|重庆,05|黑龙江,06|吉林,07|辽宁,08|内蒙古,09|河北,10|山西,11|陕西,.....

然后通过这一次访问得到的一级码得到二级码,如查找河北的:http://m.weather.com.cn/data5/city09.xml在city后加上一级码得到:

[plain] view
plaincopy

0901|石家庄,0902|保定,0903|张家口,0904|承德,0905|唐山,0906|廊坊,0907|沧州,0908|衡水,0909|邢台,0910|邯郸,0911|秦皇岛

然后再通过得到的二级码得到三级的县或区的编码:如唐山:http://m.weather.com.cn/data5/city0905.xml:

[plain] view
plaincopy

090501|唐山,090502|丰南,090503|丰润,090504|滦县,090505|滦南,090506|乐亭,090507|迁西,090508|玉田,090509|唐海,090510|遵化,090511|迁安

最后由得到了三级码就可能得到它对应的城市码了,如访问唐山.迁西:http://m.weather.com.cn/data5/city090507.xml得到:

[plain] view
plaincopy

090507|101090507

则可知河北.唐山.迁西的城市编码为:101090507,得到它的天气则通过访问http://m.weather.com.cn/data/101090507.html得到。

好了,相信通过上面的叙述,大家应该知道怎么得到一个城市的天气了;但接下来问题又来了,那我们怎样通过Android访问来得到全国任意一个地区的天气呢?难道每一次访问网络三次得到城市码,再用它访问m.weather.com.cn这个网址得到天气码?天啊?这怎么行!要知道我们手中的移动设备可不像电脑那样"富有",流量是要扣钱的!!

相信计算机算法好的同鞋已经想到了,就是通过程序将有的城市对应的天气码一次访问到本地用android上的数据库保存起来,这样在每一次需要时,只需查询一下数据库就可以了,那关键就是怎样遍历得到所有的城市码,现给出本人的遍历代码(来自于本人天气预报系统):

[java] view
plaincopy

//一个自定义的网络访问工具类

WebAccessTools webTools = new WebAccessTools(this); //这里的this为context对象

//得到访问网络的内容

String webContent=webTools.getWebContent("http://m.weather.com.cn/data5/city.xml");

//第一次解析得到的为省份或一级直辖市

String[][] provinces = WeaterInfoParser.parseCity(webContent); //WeatherInfoParser为自定义的一个解析字符串类

String[] groups = new String[provinces.length];

String[][] childs = new String[provinces.length][];

String[][] cityCode = new String[provinces.length][];

for(int i=0; i< provinces.length; i++) {

groups[i] = provinces[i][1];

//由省份码来得到城市码

StringBuffer urlBuilder= new StringBuffer("http://m.weather.com.cn/data5/city");

urlBuilder.append(provinces[i][0]);

urlBuilder.append(".xml");

webContent = webTools.getWebContent(urlBuilder.toString());

String[][] citys = WeaterInfoParser.parseCity(webContent);

//用于保存所的有二级市对应的towns县区

String[][][] towns = new String[citys.length][][];

//计算总的城镇数

int sum=0;

for(int j=0; j<citys.length; j++) {

//由城市码来得到地方码

urlBuilder= new StringBuffer("http://m.weather.com.cn/data5/city");

urlBuilder.append(citys[j][0]);

urlBuilder.append(".xml");

webContent = webTools.getWebContent(urlBuilder.toString());

towns[j] = WeaterInfoParser.parseCity(webContent);

sum = sum + towns[j].length;

}

childs[i] = new String[sum];

cityCode[i] = new String[sum];

sum=0;

for(int j=0; j<citys.length; j++) {

for(int n=0; n<towns[j].length; n++) {

if(n==0)

childs[i][sum] = towns[j]
[1];

else

childs[i][sum] = towns[j][0][1] + "." + towns[j]
[1];

urlBuilder= new StringBuffer("http://m.weather.com.cn/data5/city");

urlBuilder.append(towns[j]
[0]);

urlBuilder.append(".xml");

webContent = webTools.getWebContent(urlBuilder.toString());

String[][] code=WeaterInfoParser.parseCity(webContent);

cityCode[i][sum] = code[0][1];

sum = sum + 1;

}

}

urlBuilder=null;

}

//=======这里得到的groups数组记录的是得到的34个一级地区字符串 =====================

//=======childs记录的是与groups数组对应的一级地区对应的市级别或县区的字符串名,如:上海.闵行的形式(上海市没有.为上海)===

//=======cityCode和childs形式相同,只不过记录的为地区的城市码=====================

//=======下面就是将得到的城市码保存到数据库中,这里建了二张表,省份表和城市表,城市表通过province_id与省份表关联===========

//============================Create Database================================

//打开或创建一个数据库,数据库路径为:/data/data/包名/数据库文件

String path="/data"+ Environment.getDataDirectory().getAbsolutePath() + "/com.weather.app/db_weather.db";

SQLiteDatabase database = SQLiteDatabase.openOrCreateDatabase

(path, null);

//创建一个省份表

String sql="create table provinces (_id integer primary key autoincrement, name text)";

database.execSQL(sql);

//创建城市表

sql = "create table citys (_id integer primary key autoincrement, province_id integer, name text, city_num text)";

database.execSQL(sql);

//插入省份数据

ContentValues cv = null;

for(int i=0; i<provinces.length; i++) {

cv = new ContentValues();

cv.put("name", provinces[i][1]);

database.insert("provinces", null, cv);

}

//插入城市数据

for(int i=0; i<childs.length; i++) {

for(int j=0; j<childs[i].length; j++) {

cv = new ContentValues();

cv.put("province_id", i);

cv.put("name", childs[i][j]);

cv.put("city_num", cityCode[i][j]);

database.insert("citys", null, cv);

}

}

cv = null;

database.close();

下面给出WebAccessTools工具类:

[java] view
plaincopy

public class WebAccessTools {

/**

* 当前的Context上下文对象

*/

private Context context;

/**

* 构造一个网站访问工具类

* @param context 记录当前Activity中的Context上下文对象

*/

public WebAccessTools(Context context) {

this.context = context;

}

/**

* 根据给定的url地址访问网络,得到响应内容(这里为GET方式访问)

* @param url 指定的url地址

* @return web服务器响应的内容,为<code>String</code>类型,当访问失败时,返回为null

*/

public String getWebContent(String url) {

//创建一个http请求对象

HttpGet request = new HttpGet(url);

//创建HttpParams以用来设置HTTP参数

HttpParams params=new BasicHttpParams();

//设置连接超时或响应超时

HttpConnectionParams.setConnectionTimeout(params, 3000);

HttpConnectionParams.setSoTimeout(params, 5000);

//创建一个网络访问处理对象

HttpClient httpClient = new DefaultHttpClient(params);

try{

//执行请求参数项

HttpResponse response = httpClient.execute(request);

//判断是否请求成功

if(response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {

//获得响应信息

String content = EntityUtils.toString(response.getEntity());

return content;

} else {

//网连接失败,使用Toast显示提示信息

Toast.makeText(context, "网络访问失败,请检查您机器的联网设备!", Toast.LENGTH_LONG).show();

}

}catch(Exception e) {

e.printStackTrace();

} finally {

//释放网络连接资源

httpClient.getConnectionManager().shutdown();

}

return null;

}

}

就一个方法.

下面的解析工具类也只一个方法:

[java] view
plaincopy

public class WeaterInfoParser {

/**

* 通过解析content,得到一个一维为城市编号,二维为城市名的二维数组

* 解析的字符串的形式为: <code>编号|城市名,编号|城市名,.....</code>

* @param content 需要解析的字符串

* @return 封装有城市编码与名称的二维数组

*/

public static String[][] parseCity(String content) {

//判断content不为空

if(content!=null&&content.trim().length()!=0) {

StringTokenizer st=new StringTokenizer(content, ",");

int count = st.countTokens();

String[][] citys = new String[count][2];

int i=0, index=0;

while(st.hasMoreTokens()) {

String city = st.nextToken();

index = city.indexOf('|');

citys[i][0] = city.substring(0, index);

citys[i][1] = city.substring(index+1);

i = i+1;

}

return citys;

}

return null;

}

}

如上,这样就得到了国内所有地区的城市码数据库文件了,上面的方法在模拟器中运行时很慢,数分钟才能出来结果,要有耐心^_^!!

其通过上面的代码我们得到最重要的是db_weather.db这个文件,在Android中我们只要得到了它就可在程序第一次运行时直接导入到/data/data/包名/databases目录中就行了,已后直接调用,

关于数据库的导入,实质上就是文件的复制,我们只需在将数据库文件放在程序包中的res/raw目录中在运行时复制到databases目录是就行了,下面同样给出导入代码:

[java] view
plaincopy

//将res/raw中的城市数据库导入到安装的程序中的database目录下

public void importInitDatabase() {

//数据库的目录

String dirPath="/data/data/com.weather.app/databases";

File dir = new File(dirPath);

if(!dir.exists()) {

dir.mkdir();

}

//数据库文件

File dbfile = new File(dir, "db_weather.db");

try {

if(!dbfile.exists()) {

dbfile.createNewFile();

}

//加载欲导入的数据库

InputStream is = this.getApplicationContext().getResources().openRawResource(R.raw.db_weather);

FileOutputStream fos = new FileOutputStream(dbfile);

byte[] buffere=new byte[is.available()];

is.read(buffere);

fos.write(buffere);

is.close();

fos.close();

}catch(FileNotFoundException e){

e.printStackTrace();

}catch(IOException e) {

e.printStackTrace();

}

}

我得到的城市码数据库文件可到http://download.csdn.net/detail/xianqiang1/3896880下载

上面的是我从天气预报系统中截取的部分代码,如有错误,欢迎留言指出!!

参照文章:使用JACKSON解析JSON(HttpClient 3处理请求)/article/3762299.html

声明:感谢summerxzg和kpiao,指出了数据库文件的错误:我使用sqlitebrowser工具查看了一下我生成的db文件,显示出的的城市码数为2566个而summerxzg指出的为2583条,我的为不全。kpiao指出的citys表中的province_id与provinces表中的id不对应是由于代码中的是citys表创建时province_id是由0开始,而provinces表中的自动增长是从1开始故存在不匹配问题。为了准确起见,大家还是自己遍历一个数据库,小子我由于近期较忙暂时不会对上面内容作更改{^_^},再次感谢你们!

现在公布源码及文档下载地址:http://download.csdn.net/detail/xianqiang1/4535734

关于大家说的图片对不上,应该可以参考:http://www.weather.com.cn/static/html/legend.shtml



内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: