版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
《Android手机开发》第20-22课
Android天气预报短信服务软件
——项目设计与开发
讲授:刘发久本章学习目标:掌握Android应用项目的开发过程掌握Android应用项目的需求分析掌握Android应用项目的系统设计掌握Android应用项目的实现方法掌握Android应用项目的测试方法20.1
需求分析
设计本章的初衷希望能够根据实际项目的需求,准确的分析出Android应用程序开发所可能涉及到的知识点,并学会如何通过分析软件的需求,快速的设计出应用程序的用户界面和模块结构,并最终完成应用程序的开发和调试20.1
需求分析功能需求本章提供的“天气预报短信服务软件”综合示例中,有一个显示天气情况的用户界面,可以通过图片和文字显示当前和未来几天的天气状况,包括温度、湿度、风向和雨雪情况等。这些天气数据是通过后台服务获取的,这个后台服务可以按照一定时间间隔,从Google上获取天气预报信息,并将天气信息保存在后台服务中。示例还需要提供基于SMS短信的天气数据服务,其他手机用户可以向本示例所在的手机上发送SMS短信,并在短信中包含用户指定的关键字,则可以将保存在后台服务中的天气情况,再通过SMS短信回复给用户。最后,每个被发送的SMS短信都要被记录下来,用户可以浏览或删除这些回复信息20.1
需求分析界面需求本示例包含三个主要的用户界面显示天气预报的用户界面显示已发送SMS短信的用户界面浏览和设置配置信息的用户界面20.1
需求分析内部功能隐藏在用户界面后面的内部功能,是用户界面能够正确实现的基础显示天气预报的用户界面获取Google的天气数据显示SMS短信的用户界面根据关键字监视SMS短信发送包含天气信息的SMS短信将发送的SMS短信写入数据库浏览和设置配置信息的用户界面将用户设置的配置信息保存到数据库启动时读取数据库中的配置信息恢复缺省设置20.2
项目设计20.2.1用户界面设计详细分析应用程序中三个主要用户界面包含的显示内容在“显示天气预报的用户界面”中,根据Google可以提供的数据,在界面上可以显示当前的天气状况,包括城市名称、温度、湿度、风向、雨雪情况和获取数据时间等信息;还可以显示未来四天的天气状况,但仅包括温度和雨雪情况在“显示已发送SMS短信的用户界面”中,应显示每个回复短信的时间、目标手机号码、城市名称、当天的天气状况和未来一天的天气状况在“浏览和设置配置信息的用户界面”中,应显示获取天气预报的目标城市名称、获取数据的频率和短信的关键字,并允许用户设置是否提供短信服务以及是否记录回复的短信信息20.2项目设计20.2.1用户界面设计用户界面的草图20.2项目设计20.2.2数据库设计本示例主要存储两种数据配置信息:因为配置信息的数据量很小,从Android支持的存储方式上分析,可以保存在SharePreference、文件或SQLite数据库中SMS短信服务信息:
SMS短信服务信息是一个随着时间推移而不断增加的数据,属于文本信息,而且有固定的格式,因此适合使用SQLite数据库进行存储综合分析这两种需要存储的数据,选择SQLite数据库作为存储数据的方法20.2项目设计20.2.2数据库设计配置信息配置信息中主要保存天气信息查询的城市名称,访问Google更新天气信息的频率,请求天气信息SMS短信的关键字,以及是否提供短信服务和是否记录短信服务内容配置信息的数据库表结构属性数据类型说明_idinteger自动增加的主键city_nametext进行天气信息查询的城市名refresh_speedtext进行天气信息查询的频率,单位为秒/次sms_servicetext是否提供短信服务,即接收到请求短信后是否回复包含天气信息的短信sms_infotext是否记录发出的SMS短信的信息key_wordtext短信服务的关键字,用以确定哪条短信是请求天气服务的短信20.2项目设计20.2.2数据库设计SMS短信服务信息SMS短信服务信息主要保存请求服务短信的发送者、短信内容、接收时间和回复信息的内容SMS短信服务信息的数据库表结构属性数据类型说明_idinteger自动增加的主键sms_sendertext请求服务短信的发送者sms_bodytext请求服务短信的内容信息sms_receive_timetext接收到请求服务短信的时间return_resulttext回复短信的内容20.2项目设计20.2.3程序模块设计从功能需求上分析,可以将整个应用程序划分为4个模块,分别是用户界面、后台服务、数据库适配器和短信监听器。下图是模块结构图20.2项目设计20.2.3程序模块设计由模块结构图中可知,后台服务是整个应用程序的核心,主要包含两个子模块,一个是“数据获取模块”,负责周期性的从Google获取天气信息;另一个是“短信服务模块”,负责处理接收到的服务请求短信,并发送包含天气信息的短信后台服务由用户界面通过Intent启动,启动后的后台服务可以在用户界面关闭后仍然保持运行状态,直到用户通过用户界面发送Intent停止服务,或系统因资源不足而强行关闭服务20.2项目设计20.2.3程序模块设计用户界面从后台服务获取天气信息,而没有直接通过网络访问Google的天气数据一方面是因为后台服务使用了工作线程,通过后台服务获取天气数据可以避免因网络通信不畅造成界面失去响应另一方面,在用户关闭界面后,后台服务仍然需要更新天气信息,以保证短信服务数据的准确性。用户界面还会调用数据库适配器,向SQLite数据库中写入、读取配置信息,或对SMS短信服务信息进行操作20.2项目设计20.2.3程序模块设计短信监听器是一个BroadcastReceiver,监视所有接收到的短信如果短信中包含用户自定义的关键字,短信监听器则会认为这条短信是天气服务请求短信,将短信的相关信息写入后台服务的短信服务队列如果用户在配置信息中选择无需提供短信服务,短信监听器仍然继续监听所有短信,只是后台服务不再允许将服务请求短信写入服务队列数据库适配器封装了所有对SQLite数据库操作的方法,用户界面和后台服务会调用它实现数据库操作20.3
程序开发
20.3.1文件结构与用途在程序开发阶段,首先确定“天气预报短信服务软件”的工程名称为WeatherDemo,包名称为edu.hrbeu.WeatherDemo,据程序模块设计的内容,建立WeatherDemo示例20.3
程序开发20.3.1文件结构与用途WeatherDemo示例源代码的文件结构20.3
程序开发20.3.1文件结构与用途WeatherDemo示例设置了多个命名空间,分别用来保存用户界面、数据库、后台服务、SMS短信和天气数据的源代码文件WeatherDemo示例的命名空间命名空间说明edu.hrbeu.WeatherDemo存放与用户界面相关的源代码文件edu.hrbeu.WeatherDemo.DB存放与SQLite数据库相关的源代码文件edu.hrbeu.WeatherDemo.Service存放与后台服务相关的源代码文件edu.hrbeu.WeatherDemo.SMS
存放与SMS短信相关的源代码文件edu.hrbeu.WeatherDemo.Weather
存放与天气数据有关的源代码文件20.3
程序开发20.3.1文件结构与用途WeatherDemo示例将不同用途的源代码文件放置在不同的命名空间中包名称文件名说明.WeatherDemoHistoryActivity.java“历史数据”页的ActivitySetupActivity.java“系统设置”页的ActivityWeatherActivity.java“天气预报”页的ActivityWeatherDemo.java程序启动缺省的Activity.WeatherDemo.DBConfig.java保存配置信息的类DBAdapter.java数据库适配器.WeatherDemo.ServiceSmsReceiver.java短信监听器WeatherAdapter.java数据获取模块WeatherService.java后台服务.WeatherDemo.SMS
SimpleSms.java简化的SMS短信类SmsAdapter.java短信发送模块.WeatherDemo.WeatherForecast.java未来天气信息的类Weather.java当前天气信息的类20.3
程序开发20.3.1文件结构与用途Android的资源文件保存在/res的子目录中/res/drawable目录中保存的是图像文件/res/layout目录中保存的是布局文件/res/values目录中保存的是用来定义字符串和颜色的文件/res/xml目录保存的是XML格式的数据文件所有在程序开发阶段可以被调用的资源都保存在这些目录中20.3
程序开发20.3.1文件结构与用途资源文件名称与用途资源目录文件说明drawableicon.png图标文件sunny.png调试用的天气图片tab_history.pngTabHost中“历史数据”页的图片tab_setup.pngTabHost中“系统设置”页的图片tab_weather.pngTabHost中“天气预报”页的图片layoutdata_row.xml“历史数据”页ListActivity的每行数据的布局tab_history.xmlTabHost中“历史数据”页的布局tab_setup.xmlTabHost中“系统设置”页的布局tab_weather.xmlTabHost中“天气预报”页的布局valuescolor.xml保存颜色的XML文件string.xml保存字符串的XML文件xmlapi.xml从Google下载的天气数据文件。在程序运行时没有实际作用,但在开发过程中可以让读者了解数据格式20.3
程序开发20.3.2数据库适配器数据库适配器是最底层的模块,主要用于封装用户界面和后台服务对SQLite数据库的操作,数据库适配器的核心代码主要在DBAdapter.java文件中用户保存配置信息的类文件Config.javaConfig.java文件的全部代码如下packageedu.hrbeu.WeatherDemo.DB;
publicclassConfig{ publicstaticStringCityName; publicstaticStringRefreshSpeed; publicstaticStringProvideSmsService; publicstaticStringSaveSmsInfo; publicstaticStringKeyWord;
20.3
程序开发20.3.2数据库适配器从代码中不难看出,公有静态属性CityName、RefreshSpeed、ProvideSmsService、SaveSmsInfo和KeyWord,完全对应数据库中保存配置信息表的属性。在程序启动后,保存在数据库中的城市名称、更新频率、是否提供短信服务、是否保存短信信息和关键字等内容,将被写入这个Config类中,供其他模块在做逻辑判断时使用publicstaticvoidLoadDefaultConfig(){
CityName="";
RefreshSpeed="60";
ProvideSmsService="true";
SaveSmsInfo="true";
KeyWord="NY"; }}20.3
程序开发20.3.2数据库适配器代码第10行的LoadDefaultConfig()函数,保存了程序内置的配置参数此函数会在两个情况下被调用用户主动选择“恢复缺省设置”首次启动程序时,用来初始化保存配置参数的数据库DBAdapter类与以往介绍过的数据库适配器类相似,都具有继承SQLiteOpenHelper的帮助类DBOpenHelperDBOpenHelper在建立数据库时,同时建立两个数据库表,并对保存配置信息的表进行了初始化,初始化的相关代码在第42~49行20.3
程序开发20.3.2数据库适配器privatestaticfinalStringDB_NAME="weather_app.db";privatestaticfinalStringDB_TABLE_CONFIG="setup_config";privatestaticfinalStringDB_CONFIG_ID="1";privatestaticfinalintDB_VERSION=1;
publicstaticfinalStringKEY_ID="_id";publicstaticfinalStringKEY_CITY_NAME="city_name";publicstaticfinalStringKEY_REFRESH_SPEED="refresh_speed";publicstaticfinalStringKEY_SMS_SERVICE="sms_service";publicstaticfinalStringKEY_SMS_INFO="sms_info";publicstaticfinalStringKEY_KEY_WORD="key_word";
privatestaticfinalStringDB_TABLE_SMS="sms_data";publicstaticfinalStringKEY_SENDER="sms_sender";publicstaticfinalStringKEY_BODY="sms_body";publicstaticfinalStringKEY_RECEIVE_TIME="sms_receive_time";publicstaticfinalStringKEY_RETURN_RESULT="return_result";
20.3
程序开发20.3.2数据库适配器/**静态Helper类,用于建立、更新和打开数据库*/privatestaticclassDBOpenHelperextendsSQLiteOpenHelper{publicDBOpenHelper(Contextcontext,Stringname,CursorFactoryfactory,intversion){
super(context,name,factory,version);}
privatestaticfinalStringDB_CREATE_CONFIG="createtable"+DB_TABLE_CONFIG+"("+KEY_ID+"integerprimarykeyautoincrement,"+KEY_CITY_NAME+"textnotnull,"+KEY_REFRESH_SPEED+"text,"+KEY_SMS_SERVICE+"text,"+KEY_SMS_INFO+"text,"+KEY_KEY_WORD+"text);";
privatestaticfinalStringDB_CREATE_SMS="createtable"+DB_TABLE_SMS+"("+KEY_ID+"integerprimarykeyautoincrement,"+KEY_SENDER+"textnotnull,"+KEY_BODY+"text,"+KEY_RECEIVE_TIME+"text,"+KEY_RETURN_RESULT+"text);";
20.3
程序开发20.3.2数据库适配器@Override publicvoidonCreate(SQLiteDatabase_db){ _db.execSQL(DB_CREATE_CONFIG); _db.execSQL(DB_CREATE_SMS);
//初始化系统配置的数据表
Config.LoadDefaultConfig();
ContentValues
newValues=newContentValues();
newValues.put(KEY_CITY_NAME,Config.CityName);
newValues.put(KEY_REFRESH_SPEED,Config.RefreshSpeed);
newValues.put(KEY_SMS_SERVICE,Config.ProvideSmsService);
newValues.put(KEY_SMS_INFO,Config.SaveSmsInfo);
newValues.put(KEY_KEY_WORD,Config.KeyWord); _db.insert(DB_TABLE_CONFIG,null,newValues); }
20.3
程序开发20.3.2数据库适配器@Override publicvoidonUpgrade(SQLiteDatabase_db,int_oldVersion,int_newVersion){ _db.execSQL("DROPTABLEIFEXISTS"+DB_TABLE_CONFIG); _db.execSQL("DROPTABLEIFEXISTS"+DB_CREATE_SMS);
onCreate(_db); }}20.3
程序开发20.3.2数据库适配器在DBAdapter类中,用户界面会调用SaveConfig()和LoadConfig(),从SQLite数据库中保存和读取配置信息。保存配置信息时,SaveConfig()函数会将Config类中的公有静态属性写入数据库;反之,LoadConfig()会将数据库中的配置信息写入Config类中的公有静态属性SaveConfig()和LoadConfig()的代码如下publicvoidSaveConfig(){
ContentValues
updateValues=newContentValues();
updateValues.put(KEY_CITY_NAME,Config.CityName);
updateValues.put(KEY_REFRESH_SPEED,Config.RefreshSpeed);
updateValues.put(KEY_SMS_SERVICE,Config.ProvideSmsService);
updateValues.put(KEY_SMS_INFO,Config.SaveSmsInfo);
updateValues.put(KEY_KEY_WORD,Config.KeyWord); 20.3
程序开发20.3.2数据库适配器
db.update(DB_TABLE_CONFIG,updateValues,KEY_ID+"="+DB_CONFIG_ID,null);
Toast.makeText(context,"系统设置保存成功",Toast.LENGTH_SHORT).show();}
publicvoidLoadConfig(){ Cursorresult=db.query(DB_TABLE_CONFIG,newString[]{KEY_ID,KEY_CITY_NAME, KEY_REFRESH_SPEED,KEY_SMS_SERVICE,KEY_SMS_INFO,KEY_KEY_WORD}, KEY_ID+"="+DB_CONFIG_ID,null,null,null,null); if(result.getCount()==0||!result.moveToFirst()){ return; }20.3
程序开发20.3.2数据库适配器
Config.CityName=result.getString(result.getColumnIndex(KEY_CITY_NAME));
Config.RefreshSpeed=result.getString(result.getColumnIndex(KEY_REFRESH_SPEED));
Config.ProvideSmsService=result.getString(result.getColumnIndex(KEY_SMS_SERVICE));
Config.SaveSmsInfo=result.getString(result.getColumnIndex(KEY_SMS_INFO));
Config.KeyWord=result.getString(result.getColumnIndex(KEY_KEY_WORD));
Toast.makeText(context,"系统设置读取成功",Toast.LENGTH_SHORT).show();}20.3
程序开发20.3.2数据库适配器另一个会调用DBAdapter类的是后台服务,即WeatherService类后台服务主要调用SaveOneSms(SimpleSms
sms)、DeleteAllSms()和GetAllSms()函数,分别用来保存SMS短信记录、删除所有SMS数据记录和获取所有SMS数据记录在GetAllSms()函数中,调用了一个私有函数ToSimpleSms(Cursorcursor),用来将从数据库获取的数据转换为SimpleSms对象数组SimpleSms类将在下一小节进行介绍下面是SaveOneSms(SimpleSms
sms)、DeleteAllSms()和GetAllSms()函数的代码20.3
程序开发20.3.2数据库适配器publicvoidSaveOneSms(SimpleSms
sms){
ContentValues
newValues=newContentValues();
newValues.put(KEY_SENDER,sms.Sender);
newValues.put(KEY_BODY,sms.Body);
newValues.put(KEY_RECEIVE_TIME,sms.ReceiveTime);
newValues.put(KEY_RETURN_RESULT,sms.ReturnResult);
db.insert(DB_TABLE_SMS,null,newValues);}publiclongDeleteAllSms(){ returndb.delete(DB_TABLE_SMS,null,null);}publicSimpleSms[]GetAllSms(){ Cursorresults=db.query(DB_TABLE_SMS,newString[]{KEY_ID,KEY_SENDER, KEY_BODY,KEY_RECEIVE_TIME,KEY_RETURN_RESULT}, null,null,null,null,null); returnToSimpleSms(results);}privateSimpleSms[]ToSimpleSms(Cursorcursor){20.3
程序开发20.3.2数据库适配器int
resultCounts=cursor.getCount(); if(resultCounts==0||!cursor.moveToFirst()){ returnnull; }
SimpleSms[]sms=newSimpleSms[resultCounts]; for(inti=0;i<resultCounts;i++){
sms[i]=newSimpleSms();
sms[i].Sender=cursor.getString(cursor.getColumnIndex(KEY_SENDER));
sms[i].Body=cursor.getString(cursor.getColumnIndex(KEY_BODY));
sms[i].ReceiveTime=cursor.getString(cursor.getColumnIndex(KEY_RECEIVE_TIME));
sms[i].ReturnResult=cursor.getString(cursor.getColumnIndex(KEY_RETURN_RESULT));
cursor.moveToNext(); } returnsms;}20.3
程序开发20.3.3短信监听器短信监听器本质上是BroadcastReceiver,用于监听Android系统所接收到的所有SMS短消息,可以在应用程序关闭后仍然继续运行,核心代码在SmsReceiver.java文件中在介绍SmsReceiver类前,先说明用来保存SMS短信内容和相关信息的SimpleSms类。android.telephony.gsm.SmsMessage是Android提供的短信类,但这里需要一个更精简、小巧的类,保存少量的信息,因此构造了SimpleSms类,仅用来保存短信的发送者、内容、接收时间和返回结果。这里的“返回结果”指的是返回包含天气信息的短信内容20.3
程序开发20.3.3短信监听器SimpleSms.java文件完整代码:packageedu.hrbeu.WeatherDemo.SMS;importjava.text.SimpleDateFormat;
publicclassSimpleSms{ publicStringSender; publicStringBody; publicStringReceiveTime; publicStringReturnResult;
publicSimpleSms(){ }20.3
程序开发20.3.3短信监听器第5行到第8行代码的属性Sender、Body、ReceiveTime和ReturnResult,分别表示SMS短信的发送者、内容、接收时间和返回结果第15行和第16行的代码在SimpleSms类的构造函数中,直接将系统时间以“年-月-日小时:分:秒”的格式保存在ReceiveTime属性中publicSimpleSms(Stringsender,Stringbody){
this.Sender=sender;
this.Body=body;
SimpleDateFormat
tempDate=newSimpleDateFormat("yyyy-MM-dd"+""+"hh:mm:ss");
this.ReceiveTime=tempDate.format(new
java.util.Date());
this.ReturnResult=""; }}20.3
程序开发20.3.3短信监听器SmsReceiver类继承BroadcastReceiver,重载了onReceive()函数系统消息的识别和关键字的识别并不复杂,只要接收vider.Telephony.SMS_RECEIVED类型的系统消息,则表明是Android系统接收到了短信;将短信的内容拆分后,判断消息内容是否是配置信息中定义的关键字,即可判断该短信是否为天气服务请求短信20.3
程序开发20.3.3短信监听器SmsReceiver.java文件的核心代码publicclassSmsReceiverextendsBroadcastReceiver{ privatestaticfinalStringSMS_ACTION="vider.Telephony.SMS_RECEIVED";
@Override publicvoidonReceive(Contextcontext,Intentintent){ if(intent.getAction().equals(SMS_ACTION)){ Bundlebundle=intent.getExtras(); if(bundle!=null){ Object[]objs=(Object[])bundle.get("pdus");
SmsMessage[]messages=newSmsMessage[objs.length]; for(inti=0;i<objs.length;i++){
messages[i]=SmsMessage.createFromPdu((byte[])objs[i]); }20.3
程序开发20.3.3短信监听器StringsmsBody=messages[0].getDisplayMessageBody(); StringsmsSender=messages[0].getDisplayOriginatingAddress(); if(smsBody.trim().equals(Config.KeyWord)&&Config.ProvideSmsService.equals("true")){
SimpleSms
simpleSms=newSimpleSms(smsSender,smsBody);
WeatherService.RequerSMSService(simpleSms);
Toast.makeText(context,"接收到服务请求短信",Toast.LENGTH_SHORT).show(); }
} }}20.3
程序开发20.3.3短信监听器第10行代码将带有pdus字符串特征的对象,通过Bundle.get()函数提取出来在第12行代码使用SmsMessage.CreateFromPdu()函数构造SmsMessage对象在第20行代码使用循环语句是因为接收到的短信可能不止一条从第14行和第15行代码上看,这里只处理第1条短信第17行代码构造SimpleSms对象在代码第18行调用WeatherService类的RequerSMSService()函数,将SimpleSms对象添加到短信队列中20.3
程序开发20.3.3短信监听器在AndroidManifest.xml文件中注册短信监听器SmsReceiver,并声明可以接收短信的用户许可android.permission.RECEIVE_SMS如果注册的组件不在根命名空间中,则需要将子命名空间写在类的前面20.3
程序开发20.3.3短信监听器例如下面在代码第1行中,因为SmsReceiver.java文件在edu.hrbeu.WeatherDemo.Service命名空间下,而不在根命名空间edu.hrbeu.WeatherDemo下,因此注册组件时需要在类名SmsReceiver前添加.Service<receiverandroid:name=".Service.SmsReceiver"> <intent-filter> <actionandroid:name="vider.Telephony.SMS_RECEIVED"/> </intent-filter></receiver><uses-permissionandroid:name="android.permission.RECEIVE_SMS"/>20.3
程序开发20.3.4后台服务后台服务是WeatherDemo示例的核心模块,在用户启动后持续在后台运行,直到用户手动停止服务后台服务功能一是发送包含天气信息的SMS短信(短信发送模块)二是周期性的获取Google的天气数据(数据获取模块)
20.3
程序开发20.3.4后台服务短信发送模块:后台服务在单独的线程上运行首先调用ProcessSmsList()函数,检查短信队列中是否有需要回复的短信然后调用GetGoogleWeatherData()函数获取天气数据最后线程暂停1秒,以释放CPU资源WeatherDemo示例后台服务的核心代码在WeatherService.java文件中20.3
程序开发20.3.4后台服务下面是线程调用函数的部分代码privatestaticArrayList<SimpleSms>smsList=newArrayList<SimpleSms>();
privateRunnable
backgroudWork=newRunnable(){ @Override publicvoidrun(){ try{
while(!Terrupted()){
ProcessSmsList();
GetGoogleWeatherData(); Thread.sleep(1000); } }catch(InterruptedExceptione){
e.printStackTrace(); } }};20.3
程序开发20.3.4后台服务ProcessSmsList()函数用来检查短信列表smsList,并根据Weather类中保存的天气数据,向请求者的发送回复短信WeatherService.java文件的ProcessSmsList()函数代码如下20.3
程序开发20.3.4后台服务privatevoidProcessSmsList(){ if(smsList.size()==0){ return; }
SmsManager
smsManager=SmsManager.getDefault();
PendingIntent
mPi=PendingIntent.getBroadcast(this,0,newIntent(),0);
while(smsList.size()>0){
SimpleSms
sms=smsList.get(0); smsList.remove(0);
smsManager.sendTextMessage(sms.Sender,null,Weather.GetSmsMsg(),mPi,null);
sms.ReturnResult=Weather.GetSmsMsg();
SaveSmsData(sms); }}20.3
程序开发20.3.4后台服务发送短信是使用SmsManager对象的sendTextMessage()方法,该方法一共需要5个参数第1个参数是收件人地址第2个参数是发件人地址第3个参数是短信正文第4个参数是发送服务第5个参数是送达服务sendTextMessage()方法的收件人地址和短信正文是不可为空的参数,而且一般GSM规范要求短信内容要控制在70个汉字以内代码第8行的Weather.GetSmsMsg(),用来获得供回复短信使用的天气信息,因为考虑到短信的字数限制,仅返回当天和未来一天的天气状况20.3
程序开发20.3.4后台服务Weather.java文件的代码如下packageedu.hrbeu.WeatherDemo.Weather;importandroid.graphics.Bitmap;
publicclassWeather{ publicstaticStringcity; publicstaticStringforecase_date; publicstaticStringcurrent_date_time; publicstaticStringcurrent_condition; publicstaticStringcurrent_temp; publicstaticStringcurrent_humidity; publicstaticStringcurrent_image_url; publicstaticBitmapcurrent_image; publicstaticStringcurrent_wind;
20.3
程序开发20.3.4后台服务publicstaticForecast[]day=newForecast[4];
static{ for(inti=0;i<day.length;i++){
day[i]=newForecast(); } }
publicstaticStringGetSmsMsg(){ Stringmsg="";
msg+=city+",";
msg+=current_condition+","+current_temp+".";
msg+=day[0].day_of_week+“,”+day[0].condition+“,”+、
day[0].high+"/"+day[0].low; returnmsg; }}20.3
程序开发20.3.4后台服务Forecast.java文件的代码如下packageedu.hrbeu.WeatherDemo.Weather;importandroid.graphics.Bitmap;
publicclassForecast{ publicStringday_of_week; publicStringlow; publicStringhigh; publicStringimage_url; publicBitmapimage; publicStringcondition;}20.3
程序开发20.3.4后台服务数据获取模块:天气数据是从Google提供的WebService中获取的,数据的获取地址是http:///ig/api?hl=en&weather=New%20YorkNew%20York表示获取纽约(NewYork)的天气数据,%20表示一个空格读者可以替换New%20York,并将新的地址输入Web浏览器,在浏览器中可以直接看到XML格式的天气数据在资源目录中的/res/xml/api.xml文件,就是2009年9月22日获取的纽约天气数据在程序资源中保留api.xml文件,主要是用来帮助读者分析XML数据格式,在程序运行期间并不访问该文件20.3
程序开发20.3.4后台服务api.xml文件的内容如下<?xmlversion="1.0"encoding="UTF-8"?><xml_api_replyversion="1"> <weathermodule_id="0"tab_id="0"mobile_row="0"mobile_zipped="1"row="0"section="0"> <forecast_information> <citydata=""/> <postal_codedata=""/> <latitude_e6data=""/> <longitude_e6data=""/> <forecast_datedata=""/> <current_date_timedata="2009-09-2216:51:00+0000"/> <unit_systemdata="US"/> </forecast_information>
20.3
程序开发20.3.4后台服务<current_conditions> <conditiondata="MostlyCloudy"/> <temp_fdata="72"/> <temp_cdata="22"/> <humiditydata="Humidity:71%"/> <icondata="/ig/images/weather/mostly_cloudy.gif"/> <wind_conditiondata="Wind:Nat"/> </current_conditions>
<forecast_conditions> <day_of_weekdata="Tue"/> <lowdata="65"/> <highdata="76"/> <icondata="/ig/images/weather/mostly_sunny.gif"/> <conditiondata="PartlySunny"/> </forecast_conditions>
20.3
程序开发20.3.4后台服务<forecast_conditions> <day_of_weekdata="Wed"/> <lowdata="68"/> <highdata="79"/> <icondata="/ig/images/weather/chance_of_storm.gif"/> <conditiondata="ChanceofStorm"/> </forecast_conditions>
<forecast_conditions> <day_of_weekdata="Thu"/> <lowdata="61"/> <highdata="83"/> <icondata="/ig/images/weather/chance_of_storm.gif"/> <conditiondata="ChanceofStorm"/> </forecast_conditions>\
20.3
程序开发20.3.4后台服务<forecast_information>标签内的数据是天气预报的城市和时间等基本信息,<current_conditions>标签内的是当时的天气状况,4个<forecast_conditions>标签是未来四天的天气情况在api.xml文件中,还提供了能够反映天气情况的图标地址,例如第19行、第27行和第35行等<forecast_conditions> <day_of_weekdata="Fri"/> <lowdata="54"/> <highdata="72"/> <icondata="/ig/images/weather/sunny.gif"/> <conditiondata="Clear"/> </forecast_conditions> </weather></xml_api_reply>20.3
程序开发20.3.4后台服务WeatherAdapter类实现了利用URL获取位图的私有函数GetURLBitmap(),以及用来下载和解析XML数据的公有函数GetWeatherData()后台服务在调用GetWeatherData()函数解析Google提供的天气数据时,会不断调用GetURLBitmap()函数,将XML数据中的天气图标根据图标地址下载到本地保存20.3
程序开发20.3.4后台服务GetURLBitmap()函数的代码如下privatestaticBitmapGetURLBitmap(String
urlString){ URLurl=null; Bitmapbitmap=null; try{
url=newURL(""+urlString); } catch(MalformedURLExceptione){
e.printStackTrace(); }
try{
HttpURLConnection
conn=(HttpURLConnection)url.openConnection();
conn.connect();
InputStreamis=conn.getInputStream(); bitmap=BitmapFactory.decodeStream(is);
is.close();20.3
程序开发20.3.4后台服务第12行代码构造了支持HTTP功能的URLConnection第14行代码返回字节流第15行代码使用字节流产生位图第16行代码关闭字节流}catch(IOExceptione){
e.printStackTrace(); } returnbitmap;}20.3
程序开发20.3.4后台服务GetWeatherData()函数首先根据指定的URL地址,从网络获取字节流数据,然后调用轻量级XML解析器XmlPullParser对天气数据进行解析,并将解析结果保存在Weather类的公有静态属性中GetWeatherData()函数的代码如下publicstaticvoidGetWeatherData()throwsIOException,Throwable{StringqueryString="http:///ig/api?weather="+Config.CityName; URLaURL=newURL(queryString.replace("","%20"));
URLConnection
conn=aURL.openConnection();
conn.connect();
InputStreamis=conn.getInputStream();
XmlPullParserFactoryfactory=XmlPullParserFactory.newInstance()
factory.setNamespaceAware(true);
XmlPullParserparser=factory.newPullParser(); parser.setInput(is,"UTF-8");20.3
程序开发20.3.4后台服务12.
int
dayCounter=0;
while(parser.next()!=XmlPullParser.END_DOCUMENT){ Stringelement=parser.getName(); if(element!=null&&element.equals("forecast_information")){
while(true){
int
eventCode=parser.next(); element=parser.getName(); if(eventCode==XmlPullParser.START_TAG){ if(element.equals("city")){
Weather.city=parser.getAttributeValue(0);23.}elseif(element.equals("current_date_time")){
Weather.current_date_time=parser.getAttributeValue(0);25.}}
20.3
程序开发20.3.4后台服务28. if(element.equals("forecast_information")&&
eventCode==XmlPullParser.END_TAG){ break; }} }20.3
程序开发20.3.4后台服务if(element!=null&&element.equals("current_conditions")){
while(true){
int
eventCode=parser.next(); element=parser.getName(); if(eventCode==XmlPullParser.START_TAG){ if(element.equals("condition")){
Weather.current_condition=parser.getAttributeValue(0);\ }elseif(element.equals("temp_f")){
Weather.current_temp=parser.getAttributeValue(0); }elseif(element.equals("humidity")){
Weather.current_humidity=parser.getAttributeValue(0); }elseif(element.equals("wind_condition")){
Weather.current_wind=parser.getAttributeValue(0); }elseif(element.equals("icon")){
Weather.current_image_url=parser.getAttributeValue(0);
Weather.current_image=GetURLBitmap(Weather.current_image_url);}}
20.3
程序开发20.3.4后台服务if(element.equals("current_conditions")&&
eventCode==XmlPullParser.END_TAG){ break; } } }20.3
程序开发20.3.4后台服务if(element!=null&&element.equals("forecast_conditions")){
while(true){
int
eventCode=parser.next(); element=parser.getName(); if(eventCode==XmlPullParser.START_TAG){ if(element.equals("day_of_week")){
Weather.day[dayCounter].day_of_week=parser.getAttributeValue(0); }elseif(element.equals("low")){
Weather.day[dayCounter].low=parser.getAttributeValue(0); }elseif(element.equals("high")){
Weather.day[dayCounter].high=parser.getAttributeValue(0); }elseif(element.equals("icon")){
Weather.day[dayCounter].image_url=parser.getAttributeValue(0);
Weather.day[dayCounter].image=GetURLBitmap(Weather.day[dayCounter].image_url); }elseif(element.equals("condition")){
Weather.day[dayCounter].condition=parser.getAttributeValue(0); }20.3
程序开发20.3.4后台服务}
if(element.equals("forecast_conditions")&&
eventCode==XmlPullParser.END_TAG){
dayCounter++; break; } } } }
is.close();}20.3
程序开发20.3.4后台服务最后,在AndroidManifest.xml文件中注册WeatherService,并声明连接互联网和发送SMS短信的两个用户许可<serviceandroid:name=".Service.WeatherService"/><uses-permissionandroid:name="android.permission.INTERNET"/><uses-permissionandroid:name="android.permission.SEND_SMS"/>20.3
程序开发20.3.5用户界面在用户界面设计上,采用可以在多个分页上快速切换的Tab标签页WeatherDemo示例的Tab标签页将每个标签页与一个Activity关联在一起,这样做的好处就是可以将不同标签页的代码放在不同的文件中,而且每个标签页都可以有独立的选项菜单20.3
程序开发20.3.5用户界面WeatherDemo类是继承TabActivity的Tab标签页,共设置3个标签页TAB1标签页的标题为“天气预报”,关联的Activity为WeatherActivityTAB2标签页的标题为“历史数据”,关联Activity为HistoryActivityTAB3标签页的标题为“系统设置”,关联Activity为SetupActivity20.3
程序开发20.3.5用户界面WeatherDemo.java文件的完整代码如下packageedu.hrbeu.WeatherDemo;
importandroid.app.TabActivity;importandroid.content.Intent;importandroid.os.Bundle;importandroid.widget.TabHost;
publicclassWeatherDemoextendsTabActivity{ @Override 10.publicvoidonCreate(Bundle
savedInstanceState){
super.onCreate(savedInstanceState);12.
TabHost
tabHost=getTabHost(); tabHost.addTab(tabHost.newTabSpec("TAB1") .setIndicator("天气预报",getResources().getDrawable(R.drawable.tab_weather)) .setContent(new
Intent(this,WeatherActivity.class)));20.3
程序开发20.3.5用户界面WeatherDemo.java中的代码只是
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
评论
0/150
提交评论