到目前为止,我们已经实现了RSS Reader的基本功能,在这个OPhone应用程序中,我们使用Activity作为UI界面,使用SQLite数据库并封装为ContentProvider实现数据存储和查询。为了更进一步地优化RSS Reader应用程序的设计,我们将使用OPhone系统提供的另一种重要的组件--Service来封装RSS Reader的逻辑,使应用程序的结构更加清晰。
使用Service组件
Service组件是OPhone系统中定义的一类没有界面,在后台运行并提供服务的组件。例如,音乐播放器就使用了Service组件在后台播放音乐,这样,即使用户关闭了前台的Activity,也可以继续播放音乐。
使用Service组件的另一个好处是将应用程序的逻辑全部移到Service组件中,这样,Activity只需要把注意力放在UI逻辑上,通过调用Service组件,Activity不必关心业务逻辑。
下面,我们就把RSS Reader的联网、XML解析、数据存取等复杂逻辑从Activity移到Service里。
要编写一个Service组件相当容易,从android.app.Service派生一个实现类即可:
01.public class ReadingService extends Service { 02. ... 03.} Service组件是由系统或Activity启动的,其生命周期主要对应onCreate()、onStart()和onDestroy()三个方法。Service组件被创建时,onCreate()方法被调用,这里可以编写初始化代码,每当Activity请求启动一个Service组件时,onStart()方法被调用,最后,当系统销毁Service组件时,onDestroy()方法被调用,这里可以编写清理资源的代码。
需要注意的是,onStart()方法可能被多次调用,因此,只需初始化一次的代码需要放到onCreate()而不是onStart()方法。然后,我们就可以向ReadingService中添加若干公共方法: 01.public BriefSubscription addSubscription(String url) { ... } 02.public int getPreferenceOfExpires() { ... } 03.public int getPreferenceOfFreq() { ... } 04.public boolean getPreferenceOfUnreadOnly() { ... } 05.public void markRead(long item_id) { ... } 06.public void markUnread(long item_id) { ... } 07.public List queryBriefItems(long sub_id, boolean unreadOnly) { ... } 08.public List queryBriefSubscriptions() { ... } 09.public void removeSubscription(String sub_id) { ... } 10.public void storePreferences(boolean unreadOnly, int freq, int expires) { ... } 把Activity中的相关逻辑代码移至相应的方法中即可。
此外,Service组件也支持消息处理,因此,多线程和任务调度相关的逻辑也从MainActivity中移至ReadingService中,并添加删除过期Item的逻辑: 01.private final Handler handler = new Handler() { 02. @Override 03. public void handleMessage(Message msg) { 04. switch (msg.what) { 05. case MSG_TIMER: 06. log.info("Message: MSG_TIMER"); 07. removeExpires(); 08. refreshFeeds(); 09. break; 10. } 11. } 12.}; 现在,应用程序的逻辑已经完全移至ReadingService中。下一步,我们需要在AndroidManifest.xml中添加ReadingService的声明: 注意:Service的Class全名由AndroidManifest.xml中声明的package名称"org.expressme.wireless.reader"和Service的android:name组合而成。
启动Service
Service的启动是通过Activity的startService()方法实现的,同样需要一个Intent实例: 01.@Override 02.public void onCreate(Bundle savedInstanceState) { 03. super.onCreate(savedInstanceState); 04. ... 05. Intent intent = new Intent(this, ReadingService.class); 06. ComponentName service = startService(intent); 07.} Activity无需知道Service当前是否已经启动。如果Service还没有启动,OPhone系统会创建Service,调用其onCreate()方法,再调用其onStart()方法。如果Service已经正在运行,OPhone系统会调用其onStart()方法,由于onStart()方法可能被多次调用,因此,Service组件要维护自己的内部状态,防止在onStart()方法中多次初始化。
停止Service
停止Service与启动Service类似,也需要构造一个Intent实例,然后,通过stopService()方法停止Service: 01.@Override 02.protected void onDestroy() { 03. super.onDestroy(); 04. Intent intent = new Intent(this, service.getClass()); 05. stopService(intent); 06.} 停止Service的方法一般由生命周期最长的Activity在其onDestroy()方法中调用,这样,Activity被销毁时,Service就停止了,能够及时释放系统资源。
与Activity通信
仅仅是启动和停止Service还远远不够。细心的读者可能发现了,启动ReadingService时,返回的不是ReadingService类的引用,而是ComponentName的实例。那么,我们在ReadingService中定义了若干个public方法,如何才能在Activity中调用呢?
在OPhone系统中,要调用Service的public方法,需要通过Binder机制来实现,首先,Service组件本身要实现Binder机制,然后,Activity才能通过Binder连接到Service组件,并调用其public方法。
因此,第一步是给ReadingService添加Binder支持。在ReadingService内部添加一个ReadingBinder的内部类声明,添加getService()方法并返回ReadingService的当前实例,然后,实例化并持有一个Binder的引用:
01.public class ReadingBinder extends Binder { 02. public ReadingService getService() { 03. return ReadingService.this; 04. } 05.} 06.private final IBinder binder = new ReadingBinder(); 下一步,覆写onBind()方法,返回binder实例: 现在,ReadingService组件就实现了Binder机制,下面,我们需要在Activity中添加一点代码,通过bindService()方法来绑定ReadingService的实例: // bind service:
Intent bindIntent = new Intent(this, ReadingService.class);
bindService(bindIntent, serviceConnection, Context.BIND_AUTO_CREATE);
// bind service: Intent bindIntent = new Intent(this, ReadingService.class); bindService(bindIntent, serviceConnection, Context.BIND_AUTO_CREATE);
没错!绑定一个Service也是通过Intent完成的,同时需要提供一个ServiceConnection回调接口,用于接收Bind事件: 01.private ServiceConnection serviceConnection = new ServiceConnection() { 02. public void onServiceConnected(ComponentName className, IBinder service) { 03. serviceBinder = ((ReadingService.ReadingBinder)service).getService (); 04. init(); 05.; 06. } 07. public void onServiceDisconnected(ComponentName className) { 08. serviceBinder = null; 09. } 10.}; ServiceConnection回调接口用于接收Connected和Disconnected事件,请注意,bindService()方法是异步执行的,即bindService()返回后,并不能立刻获取到Service的实例,必须响应onServiceConnected()事件,在这个事件中获取Service的实例,然后执行一些初始化方法。
绑定Service后,Activity就获得了Service实例的引用,我们将其保存在成员变量中,然后,在Activity的生命周期中,就可以随时调用Service的public业务方法了。
Activity结束时,还必须及时取消对Service的绑定,通过unbindService()方法实现: 对Service的绑定和取消应该分别对应Activity的onCreate()和onDestroy()事件,这样,能够保证Activity正确释放引用的资源。
使用Broadcast广播
在Activity中调用Service的public方法很容易,例如,我们通过调用refresh()方法就可以请求ReadingService组件在后台开启新的异步任务来获取最新的RSS。当ReadingService获得了最新的RSS内容并写入数据库后,如何通知前台的MainActivity刷新当前显示的ListView呢?
直接调用MainActivity的某个notifyChanged()方法可不好,因为ReadingService很难获得MainActivity的引用,即使获得了,ReadingService不是运行在系统API层的,无法掌控MainActivity的状态,如果MainActivity已经处于销毁状态,则刷新UI可能引发应用程序崩溃。
此外,直接调用还导致两个组件的紧密耦合,将来如果有其他Activity也需要得到该通知的话,则还需添加更多的代码,导致更紧密的耦合。
理想状态下,ReadingService应该只负责发出通知,不知道也不关心谁会接收到该消息,而MainActivity则应该只负责接收该通知,不知道也不关心谁发出的消息,这样,通过典型的Observer模式实现的广播,就可以让各个组件保持松耦合,还可以动态地加入接收者。
发送广播
OPhone系统已经提供了Observer模式的实现,即使用Broadcast广播一个Intent。下面,我们通过Broadcast机制来实现ReadingService和MainActivity之间的异步消息发送和接收的功能。
首先,我们需要定义ReadingService能够发出的消息类型,目前,RSS Reader应用一共支持以下3种消息类型: 01.// 有新的RSS项: 02.public static final String NOTIFY_NEW_ITEMS = ReadingService.class.getName() + ".NOTIFY_NEW_ITEMS"; 03.// 用户设置已更改: 04.public static final String NOTIFY_PREF_CHANGED = ReadingService.class.getName() + ".NOTIFY_PREF_CHANGED"; 05.// 用户删除了一个订阅: 06.public static final String NOTIFY_SUB_REMOVED = ReadingService.class.getName() + ".NOTIFY_SUB_REMOVED"; 注意到消息类型是String类型,因此,为了确保全局唯一,我们使用ReadingService的完整类名+自定义消息名称。
现在,ReadingService可以在合适的时候发出通知消息。例如,当用户修改了设置后,ReadingService将首先保存用户设置,然后,发出NOTIFY_PERF_CHANGED消息:
使用sendBroadcast()方法就可以发出广播消息,该方法定义在android.content.Context接口中,Service和Activity均继承并实现了该方法。
如果我们希望能在消息中再附带一点数据,则需要将需要携带的数据放入Intent中,通过Intent的putExtra()方法可以放入String、int、boolean等常见数据类型,例如,当发现新的RSS项后,ReadingService将发送NOTIFY_NEW_ITEMS消息,并同时附上Subscription的ID值: 接收广播
现在,ReadingService已经能够发出广播了,下一步需要做的,就是让MainActivity能够接收广播。
要接收一个广播,首先需要创建一个BroadcastReceiver的实例,并覆写onReceive()方法用于处理广播: 建议将BroadcastReceiver的实例定义为final类型。
然后,在Activity的onCreate()方法中注册BroadcastReceiver的实例,以便能够接收到广播消息: 01.@Override 02.public void onCreate(Bundle savedInstanceState) { 03. super.onCreate(savedInstanceState); 04. ... 05. // 注册: 06. IntentFilter filter = new IntentFilter(ReadingService.NOTIFY_NEW_ITEMS); 07. registerReceiver(this.newItemsReceiver, filter); 08.} 注意到registerReceiver方法除了传入BroadcastReceiver的实例外,还需要一个IntentFilter。顾名思义,IntentFilter就是根据消息类型来过滤接收到的Intent的。例如,上述代码指定的IntentFilter将过滤掉除NOTIFY_NEW_ITEMS之外的其他所有Intent,这样,该BroadcastReceiver接收到的广播消息就全部是NOTIFY_NEW_ITEMS,没有必要再根据Intent.getAction()来判断了。
最后,不要忘记在Activity的onDestroy()方法中取消已注册的BroadcaseReceiver:
发表评论
-
常见的字符串和数的转换(C)
2012-07-06 09:51 7341. 字符串转换为数字:头文件 #include at ... -
数据验证--正则表达式验证RegularExpressionValidator
2012-07-06 09:45 645RegularExpressionValidator控件用 ... -
Oracle 正则表达式
2012-07-06 09:37 674Oracle正则表达式 周末学习正则表达式,将搜集的资 ... -
DevExpress皮肤引用的办法
2012-07-06 09:30 946procedure TFrmMain.SetSkin(sk ... -
JS操作iframe里的dom,js iframe
2012-07-05 20:45 647直接赋值如下代码测试即可明白: 1.html: ... -
Flex事件机制二
2012-07-02 10:36 665三: dispatcherEvent EventDispa ... -
as与js相互通信(flex中调用js函数)
2012-07-02 10:36 658Flex中As调用Js的方法是: 1、导入包 (i ... -
flex 实现的多点切割+mask蒙版+自动识别非透明区域+自适应图片大小
2012-07-02 10:36 706先上代码: width="650" ... -
flex的数据验证!
2012-07-02 10:36 57219.3.2 如何使用数 ... -
flex使用翻译串流程
2012-07-01 00:13 658== 翻译串流程 == 如果在本地进行翻译串编译, ... -
[ActionScript3.0][My flex]读书笔记三之 模块化编程
2012-07-01 00:13 5821.加载mxml模块 第一步: 新建mxml模块:Data ... -
flex与C# Socket通信
2012-07-01 00:13 583Socket 通信没什么好说,一个服务端,多个客户端,很容 ... -
Flex组件自动获取输入焦点
2012-07-01 00:12 616浏览器加载swf后,Flex组件自动获取输入焦点。 1.在 ... -
Flex中使用HttpService和WebService方式通信
2012-07-01 00:12 834HttpService、WebService、Remote ...
相关推荐
受到无线网络的速度限制,RSSReader需要把抓取的内容存储到本地,以便快速显示给用户,这个功能利用数据库存储最容易实现。此外,RSSReader还需要保存用户的设置。下面,我们分别来编写这两个功能。使用SQLite...
Ultimate Reader——Our Own Rss Reader 随着越来越多的Web站点对RSS的支持,RSS已经成为目前最成功的XML应用。RSS搭建了一个信息迅速传播的技术平台,使得每个人都成为潜在的信息提供和获取者。在知识爆炸的当今...
RssReader项目设计手册,以及相关PPT
RSSReader和Awasu、FeedDemon一样,也是一个单机版新闻阅读器。一旦下载完毕,这款免费的RSSReader安装所花时间不到1分钟。但和NewsGator相同的是,RSSReader也需要另外安装Microsoft .Net Framework 1.1
基于iPhone 设计的RSS阅读应用(RssReader)仅用于学习和研究
ivandroid rss reader
一个较为完美的RSSReader,加入了数据库。有详细的设计报告和帮助文档。
rssReader 阅读器 C#版 源码 不错的阅读器
Android 手机系统的阅读器rss reader源代码,一个很棒的程序,RSS 阅读器相信大家都清楚吧,专用于RSS定阅,在电脑上已是一个成熟的应用了,本RSS Reader是运行在Google的Android手机操作系统中,对此有研究的专业...
rss reader rss阅读
Atom-Rss-Reader.zip,示例rssreader ios应用程序,atom是一个用web技术构建的开源文本编辑器。
优点: 速度超快,特别针对大量频道同步更新而设计。 下载资料保存到本地,以前的新闻不会因网站更新而丢失。 支持所有主流RSS协议,对国内各种非标准的中文rss feed兼容性特...绿色软件800KB,免安装,展开直接使用
RssReader Rss阅读器 C#程序
一个读取RSS资源并展示的实例,觉得挺实用就传上来分享了。一共两个界面,一个是读取并展示列表,一个是点击列表进入阅读页
java使用Rome解析Rss的实例
Rss feed reader green software
本书以Android应用程序的开发为主题,并结合真实的案例向读者详细介绍了Android的基本组件的使用及应用程序开发的整个流程。本书的讲述由浅入深,实例全面并典型,几乎囊括了所有和Android应用相关的项目。全书分为...
javascript 解析 rss 实例 订阅 xml
Java使用SAX的rss解析实例 按照说明配好rss连接地址后即可使用 本实例用的Struts架构
android下开发的rss reader,工程源码,适合初学者熟悉开发流程