Android平台下的空气质量和天气情况查询应用设计与实现
2016-06-16龚世文朱忠敏
龚世文+朱忠敏
摘要:Android体系结构采用软件堆层(Software Stack)的架构,提供一系列的核心应用程序包括浏览器、电子邮件客户端、通讯录和日历等,其市场份额长年保持在50%以上,无论从技术层面还是受众范围来看都是非常合适的应用开发平台。
关键词:Android平台;系统设计;大气环境
中图分类号:TP311 文献标识码:A 文章编号:1009-3044(2016)10-0073-04
1 概述
中国经济在过去40年快速的发展极大地提高了人民生活水平,同时也引起了严重的环境问题。早在2007年Cyranoski便在《自然》杂志撰文指出大气污染已经成为困扰大多数中国城市的严重问题。2009年以来,在国内外舆论的引导下大气污染问题逐步从一个学术性议题演变为政治性的大众议题。2014年起,环境保护部开始通过其官方网站正式对公众发布实时PM2.5数据。数据的公开发布有助消除公众的疑虑,同时对公共健康起到了指导作用。随着“互联网+”时代的到来,大众获取信息的主要手段已经从电视广播报纸转变为网络,而智能手机的普及化进一步提升了大众获取信息的能力。相比于网页模式,手机端应用的最大优势在于可以智能定位,随时随地获取最新数据。Android体系结构采用软件堆层(Software Stack)的架构,提供一系列的核心应用程序包括浏览器、电子邮件客户端、通讯录和日历等,其市场份额长年保持在50%以上,无论从技术层面还是受众范围来看都是非常合适的应用开发平台。因此,在Android系统下,开发一款能够实时查询所处位置空气质量和天气情况的手机应用是一个具有明显用户需求的任务。
2 系统设计
一般的空气质量查询应用只能对大气颗粒物(PM2.5和PM10)和大气污染物(SO2、CO、NOX和O3)进行查询。考虑到气象因素与大气污染存在一定关联,而且这两个因素共同决定了用户进行室外运动的意愿。因此,本文所提应用程序划分为空气质量查询和天气查询两大功能。空气质量查询功能部分划分为用户界面、网上数据获取与解析、数据适配器和相关知识查询四个模块;天气查询功能部分用户界面、数据获取与解析、数据库适配器三个模块。各个模块之间的关系如下图1所示。
图 1 应用总体设计思想
在空气质量查询功能模块中,当用户通过界面发送请求时,空气质量数据获取子模块负责从中国环境监测总站城市空气质量实时发布平台加载数据信息,然后进行数据解析显示在用户界面上。气象数据查询模块的流程与前者相似,唯一不同在于使用了不同的解析技术。JSON相对于XML,速率快,数据的体积小,JSON与JavaScript的交互更加方便,但JSON对数据的描述性比XML较差。因而结合两者解析的优缺点以及服务端返回数据格式,在解析空气质量数据时使用JSON解析,在解析天气数据时使用XML的Pull解析。
3功能实现
3.1程序设计
通过JSON方式实现PM2.5数据解析,通过定义JSONArray和JSONObject获取大气环境的相关数据,包括AQI、城市名、PM2.5、PM10、首要污染物、污染级别等,主要代码如下所示:
protected void onPostExecute(String result) {
ArrayList
if (result != null && !result.contains("error")) {
Log.d(PM25Provider.LogTag, result);
try { JSONArray localJSONArray = new JSONArray(result);
pm25List = new ArrayList
for (int i = 0; i < localJSONArray.length(); i++) {
JSONObject localJSONObject = localJSONArray.getJSONObject(i);
PM25Provider.PM25 pm25 = new PM25Provider.PM25();
pm25.aqi = localJSONObject.optString("aqi");
pm25.area = localJSONObject.optString("area");
pm25.pm2_5 = localJSONObject.optString("pm2_5");
pm25.pm10 = localJSONObject.optString("pm10");
pm25.position_name = localJSONObject.optString("position_name");
pm25.primary_pollutant = localJSONObject.optString("primary_pollutant");
pm25.quality = localJSONObject.optString("quality");
pm25.time_point = localJSONObject.optString("time_point");
pm25List.add(pm25); }
} catch (JSONException e) { e.printStackTrace();
} pm25Listener.onInfo(pm25List); } }
在此基础上,利用API.java访问网络接口并设置城市名字符串;利用PM25Adapter.java将数据加载于界面布局;PM25Citysetting.java实现城市选择功能;PM25Activity.java完成主界面显示响应功能,首先设置分享部分响应,其次设置初次显示界面数据,之后设置动态打印报告数据加载显示以及用户反馈动态效果响应问题,最后通过调用其他Activity完成数据成功显示。空气质量报告显示主要代码如下所示:
new PM25Provider().request(new PM25Provider.PM25Info() {
public void onInfo(List
if (pm25List == null) {
Toast.makeText(PM25Activity.this, R.string.get_pm25_fail,
Toast.LENGTH_SHORT).show();
return; }
PM25Provider.PM25 pm25 = (PM25Provider.PM25) pm25List.get(-1
+ pm25List.size());
vAQI = wrapFont(pm25.aqi);
vPM25 = wrapFont(pm25.pm2_5);
mPaperAQI.setText(vAQI);
try {
Date date = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'")
.parse(pm25.time_point);
SimpleDateFormat sdf = new SimpleDateFormat(
"yyyy年MM月dd日 HH:mm");
vTime = sdf.format(date);
mPaperTime.setText("发布时间:" + vTime);
vCity = pm25.area;
mPaperArea.setText(vCity);
vQuality = pm25.quality;
mPaperQuality.setText("等级:" + vQuality);
mPaperProposal.setText("建议:" + (String) maps.get(vQuality));
mValue.setText("88888 ");
mListView.setAdapter(new PM25Adapter(PM25Activity.this,
pm25List));
startInfoShow();
} catch (ParseException e) {
e.printStackTrace();
}
}
}, city);
}
气象功能实现代码包:com.weaher,其中Config.java配置信息类;DBAdapter.java实现数据库配置;Forecast.java未来天气类;Weather.java当前天气类;WeatherAdapter.java数据获取模块,使用XML Pull解析数据;WeatherDemo.java该功能的程序启动默认界面响应。
在WeatherAdapter.java数据获取模块中首先设置静态变量getWeather,通过HttpClient的get或post方式请求服务器,然后建立XmlPullParser解析器解析服务器返回的数据,使用while if语句获取四天内weather的时间、地点、温度、气候、风力,其在Forecast.java和Weather.java中定义好需要获取的变量数据名。
Pull解析代码如下面所示:
private static void getWeatherData(String city) throws IOException,Throwable{
String requestUrl = "http://api.map.baidu.com/telematics/v3/weather?location="
+city +"&output=xml&ak=A72e372de05e63c8740b2622d0ed8ab1";
String result = request("POST",requestUrl);
InputStream streamTemp = new ByteArrayInputStream(result.getBytes());
XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
factory.setNamespaceAware(true);
XmlPullParser parser = factory.newPullParser();
parser.setInput(streamTemp,"UTF-8");
while(parser.next()!=XmlPullParser.END_DOCUMENT){
String element = parser.getName();
if(element != null && element.equals("weather_data")){
for(int dayCount = 0;dayCount<4;++dayCount){
if(dayCount == 0){
Weather.city = city;
System.out.println(city); }
while(parser.next()!=XmlPullParser.END_DOCUMENT){
element = parser.getName();
if((element != null)){
if (element.equals("date")) {
if(dayCount == 0)
Weather.current_date_time = parser.nextText();
else
Weather.day[dayCount-1].day_of_week = parser.nextText(); }
else if (element.equals("dayPictureUrl")) {
if(dayCount == 0)
Weather.current_dayPictureUrl = parser.nextText();
else
Weather.day[dayCount-1].dayPictureUrl = parser.nextText(); }
else if (element.equals("nightPictureUrl")) {
if(dayCount == 0)
Weather.current_nightPictureUrl = parser.nextText();
else
Weather.day[dayCount-1].nightPictureUrl = parser.nextText(); }
else if (element.equals("weather")) {
if(dayCount == 0)
Weather.current_weather = parser.nextText(); else
Weather.day[dayCount-1].weather = parser.nextText(); }
else if (element.equals("wind")) {
if(dayCount == 0)
Weather.current_wind = parser.nextText(); else
Weather.day[dayCount-1].wind = parser.nextText(); }
else if (element.equals("temperature")) {
if(dayCount == 0)
Weather.current_temp = parser.nextText(); else
Weather.day[dayCount-1].temperature = parser.nextText(); break;
}
服务器请求代码如下所示:
private static String request(String method, String url) {
HttpResponse httpResponse = null;
StringBuffer result = new StringBuffer();
try {
if (method.equals("GET")) {
HttpGet httpGet = new HttpGet(url);
HttpClient httpClient = new DefaultHttpClient();
httpResponse = httpClient.execute(httpGet);
HttpEntity httpEntity = httpResponse.getEntity();
result.append(EntityUtils.toString(httpEntity, "utf-8"));
StatusLine statusLine = httpResponse.getStatusLine();
result.append(":" + statusLine.getProtocolVersion() + "\r\n");
int statusCode = statusLine.getStatusCode();
result.append(":" + statusCode + "\r\n");
} else if (method.equals("POST")) {
HttpPost httpPost = new HttpPost(url);
HttpClient httpClient = new DefaultHttpClient();
httpResponse = httpClient.execute(httpPost);
HttpEntity httpEntity = httpResponse.getEntity();
result.append(EntityUtils.toString(httpEntity, "utf-8"));
}
} catch (Exception e)
3.2 UI展示
空气质量查询界面设有城市选择和查询两个按钮,选择按钮可以选择城市、实现PM2.5与AQI切换,查询按钮可以实现动态效果。在连网的状态下,可以自动定位到当前城市,显示其AQI值,通过选择按键可以切换PM2.5和AQI值,如图2左1所示即为切换到PM2.5的状态。当点击查询会打印一份空气质量检测报告,报告上显示有标题、发布时间、当前城市名、AQI值、等级、建议措施、各监测点PM2.5、PM10的值以及分享标志,用户可以分享到QQ、微信、微博等等一系列软件中,效果如图2左2至右1所示。
在天气查询界面中,主要显示天气实时数据(包括地区、时间、温度、气候、风力)、未来三天天气数据(包括温度、气候)、城市选择和城市保存设置。其整体布局是LinearLayout线性布局,局部运用表格布局TableLayout和TableRow、TextView显示未来三天数据信息,使用RelativeLayout布局和Button、EditText、TextView控件显示城市设置信息。当用户选择城市后,点击保存设置按钮,界面会显示“城市设置成功”字样,在网络通畅的情况下,即可显示城市天气相关信息。而当无网状态下,全部显示为null。如图3所示。
4 结束语
通过虚拟机和移动终端的测试表明,此应用可以正常运行。整体界面以蓝色系为主,设计美观,给人一种清新舒畅的视觉享受,操作简单,易于掌握。需要指出的是该应用的空气质量数据和天气数据全部来源于网络,因此在运行时需确保系统环境的网络流畅性。
参考文献:
[1] Cyranoski D. Satellite view alerts china to soaring pollution[J]. Nature 2005, 437: 12.
[2] Cyranoski D. Monitoring air pollution[J]. Nature 2007, 448: 518.
[3] 董冠洋. 2014年中国190个城市将实时发布PM2.5数据[N]. 中国新闻社,2013-12-31
[4] 关立勋.Android 应用开发深入学习实录[M].北京:电子工业出版社,2013.
[5] 李斌.空气质量实时发布系统数据上传问题的探讨[J].环境科学与技术,2011,34(12H):242- 244.
[6] 赵启明.Android典型技术模块开发详解[M].北京:中国铁道出版社,2012.
[7] 隆益民.Android应用开发[M].广州:中山大学出版社,2010.
[8] 王灏,马军.Java完全自学手册[M].北京:机械工业出版社,2006:20-55.
[9] 王国辉,李伟.Android开发宝典[M].北京:机械工业出版社,2008.