Android应用开发第7讲内容向导7.1本地服务7.2隐式和显式启动7.3线程启动挂起和停止Service简介Service是Android系统的服务组件,适用于开发没有用户界面且长时间在后台运行的应用功能因为手机硬件性能和屏幕尺寸的限制,通常Android系统仅允许一个应用程序处于激活状态并显示在手机屏幕上,而暂停其他处于未激活状态的程序因此,Android系统需要一种后台服务机制,允许在没有用户界面的情况下,使程序能够长时间在后台运行,实现应用程序的后台服务功能,并能够处理事件或数据更新7.1本地服务7.1.1Service生命周期Service生命周期包括完全生命周期活动生命周期onCreate()事件回调函数:Service的生命周期开始,完成Service的初始化工作onStart()事件回调函数:活动生命周期开始,但没有与之对应的“停止”函数,因此可以近似认为活动生命周期也是以onDestroy()标志结束onDestroy()事件回调函数:Service的生命周期结束,释放Service所有占用的资源调用startService()启动ServiceonCreate()onStart()Service正在运行Service被停止onDestroy()关闭Service调用startService()启动ServiceonCreate()onStart()Service正在运行Service被停止onDestroy()关闭ServiceService的使用方式一般有两种启动方式绑定方式启动方式通过调用Context.startService()启动Service,通过调用Context.stopService()或Service.stopSelf()停止Service。因此,Service一定是由其它的组件启动的,但停止过程可以通过其它组件或自身完成在启动方式中,启动Service的组件不能够获取到Service的对象实例,因此无法调用Service中的任何函数,也不能够获取到Service中的任何状态和数据信息能够以启动方式使用的Service,需要具备自管理的能力,而且不需要从通过函数调用获取Service的功能和数据绑定方式Service的使用是通过服务链接(Connection)实现的,服务链接能够获取Service的对象实例,因此绑定Service的组件可以调用Service中实现的函数,或直接获取Service中的状态和数据信息使用Service的组件通过Context.bindService()建立服务链接,通过Context.unbindService()停止服务链接如果在绑定过程中Service没有启动,Context.bindService()会自动启动Service,而且同一个Service可以绑定多个服务链接,这样可以同时为多个不同的组件提供服务启动方式和绑定方式的结合这两种使用方法并不是完全独立的,在某些情况下可以混合使用以MP3播放器为例,在后台的工作的Service通过Context.startService()启动某个音乐播放,但在播放过程中如果用户需要暂停音乐播放,则需要通过Context.bindService()获取服务链接和Service对象实例,进而通过调用Service对象实例中的函数,暂停音乐播放过程,并保存相关信息7.1.2本地服务本地服务的调用者和服务都在同一个程序中,是不需要跨进程就可以实现服务的调用本地服务涉及服务的建立、启动和停止,服务的绑定和取消绑定,以及如何在线程中实现服务服务管理服务管理主要指服务的启动和停止首先说明如何在代码中实现Service。Service是一段在后台运行、没有用户界面的代码,其最小代码集如下服务管理实例importandroid.app.Service;importandroid.content.Intent;importandroid.os.IBinder;publicclassRandomServiceextendsService{@OverridepublicIBinderonBind(Intentintent){returnnull;}}服务管理实例publicclassRandomServiceextendsService{@OverridepublicvoidonCreate(){super.onCreate();}@OverridepublicvoidonStart(Intentintent,intstartId){super.onStart(intent,startId);}@OverridepublicvoidonDestroy(){super.onDestroy();}}完成Service类后,需要在AndroidManifest.xml文件中注册这个Service注册Service非常重要,如果开发人员不对Service进行注册,则Service根本无法启动AndroidManifest.xml文件中注册Service的代码如下serviceandroid:name=.RandomService/使用service标签声明服务,其中的android:name表示Service类的名称,一定要与建立的Service类名称一致7.2.1显示启动在完成Service代码和在AndroidManifest.xml文件中注册后,下面来说明如何启动和停止Service。有两种方法启动Service,显式启动和隐式启动显式启动需要在Intent中指明Service所在的类,并调用startService(Intent)启动Service,示例代码如下finalIntentserviceIntent=newIntent(this,RandomService.class);startService(serviceIntent);7.2隐式和显式启动7.2.2隐式启动需要在注册Service时,声明Intent-filter的action属性serviceandroid:name=.RandomServiceintent-filteractionandroid:name=edu.hrbeu.RandomService//intent-filter/service在隐式启动Service时,需要设置Intent的action属性,这样则可以在不声明Service所在类的情况下启动服务。隐式启动的代码如下finalIntentserviceIntent=newIntent();serviceIntent.setAction(edu.hrbeu.RandomService);如果Service和调用服务的组件在同一个应用程序中,可以使用显式启动或隐式启动,显式启动更加易于使用,且代码简洁。但如果服务和调用服务的组件在不同的应用程序中,则只能使用隐式启动无论是显式启动还是隐式启动,停止Service的方法都是相同的,将启动Service的Intent传递给stopService(Intent)函数即可,示例代码如下stopService(serviceIntent);在首次调用startService(Intent)函数启动Service后,系统会先后调用onCreate()和onStart()如果是第二次调用startService(Intent)函数,系统则仅调用onStart(),而不再调用onCreate()在调用stopService(Intent)函数停止Service时,系统会调用onDestroy()无论调用过多少次startService(Intent),在调用stopService(Intent)函数时,系统仅调用一次onDestroy()SimpleRandomServiceDemo是在应用程序中使用Service的示例7.3.1线程简介在Android系统中,Activity、Service和BroadcastReceiver都是工作在主线程上,因此任何耗时的处理过程都会降低用户界面的响应速度,甚至导致用户界面失去响应当用户界面失去响应超过5秒后,Android系统会允许用户强行关闭应用程序,提示如下图所示7.3线程启动挂起和停止7.3.2使用线程无论线程是否真的并行工作,在宏观上可以认为子线程是独立于主线程的,且能与主线程并行工作的程序单元在Java语言中,建立和使用线程比较简单,首先需要实现Java的Runnable接口,并重载run()函数,在run()中放置代码的主体部分privateRunnablebackgroudWork=newRunnable(){@Overridepublicvoidrun(){//过程代码}};然后创建Thread对象,并将Runnable对象作为参数传递给Thread对象在Thread的构造函数中,第1个参数用来表示线程组,第2个参数是需要执行的Runnable对象,第3个参数是线程的名称privateThreadworkThread;workThread=newThread(null,backgroudWork,WorkThread);最后,调用start()方法启动线程workThread.start();当线程在run()方法返回后,线程就自动终止了当然,也可以调用stop()在外部终止线程,但这种方法并不推荐使用,因为这方法并不安全,有一定可能性会产生异常最好的方法是通知线程自行终止,一般调用interrupt()方法通告线程准备终止,线程会释放它正在使用的资源,在完成所有的清理工作后自行关闭workThread.interrupt();其实interrupt()方法并不能直接终止线程,仅是改变了线程内部的一个布尔值,run()方法能够检测到这个布尔值的改变,从而在适当的时候释放资源和终止线程在run()中的代码一般通过Thread.interrupted()方法查询线程是否被中断一般情况下,子线程需要无限运行,除非外部调用interrupt()方法中断线程,所以通常会将程序主体放置在while()函数内,并调用Thread.interrupted()方法判断线程是否应被中断下面的代码中,以1秒为间隔循环检测线程是否应被中断第4行代码使线程休眠1000毫秒当线程在休眠过程中线程被中断,则会产生InterruptedException异常因此代码中需要捕获InterruptedException异常,保证安全终止线程1.publicvoidrun(){2.while(!Thread.interrupted()){3.//过程代码4.Thread.sleep(1000);5.}6.}使用Handler更新用户界面Handler允许将Runnable对象发送到线程的消息队列中,每个Handler实例绑定到一个单独的线程和消息队列上当用户建立一个新的Handler实例,通过post()方法将Runnable对象从后台线程发送给GUI线程的消息队列,当Runnable对象通过消息队列后,这个Runnable对象将被运行使用线程privatestaticHandlerhandler=newHandler();publicstaticvoidUpdateGUI(doublerefreshDouble){handler.post(RefreshLable);}privatestaticRunnableRefreshLable=newRunnable(){@Overridepublicvoidrun(){//过程代码}};7.3.3服务绑定以绑定方式使用Service,能够获取到Service实例,不仅能够正常启动Service,还能够调用Service中的公有方法和属性为了使Service支持绑