您好,欢迎访问三七文档
当前位置:首页 > 商业/管理/HR > 销售管理 > 第7章 Service后台服务
第七章Service后台服务本章目标:掌握Service组件应用掌握进程内服务掌握Handler消息传递机制7.1Service组件应用因为手机屏幕尺寸的限制,通常情况下在同一时刻仅有一个应用程序处于激活状态,并能够显示在手机屏幕上。因此,应用程序需要一种机制,在没有用户界面的情况下,使其能够长时间在后台运行,实现应用程序的特定功能,并能够处理事件或更新数据。Android系统提供的Service(服务)组件,不直接与用户时行交互,并能够长期在后台运行。有很多情况需要使用Service,经常提到的例子就是MP3播放器,用户需要在关闭播放器界面后,仍然能够保持音乐继续播放,这就需要将音乐回放的功能在Service组件实现。Service非常适用于无需用户干预,且需要长期运行的后台功能。Service没有用户界面,有利于降低系统资源的消耗,而且Service比Activity具有更高的优先级,因此在系统资源紧张时,Service不会轻易被Android系统终止。即使Service被系统终止,在系统资源恢复后,Service也将自动恢复运行状态,因此可以认为Service是在系统中永久运行的组件。Service除了可以实现后台服务功能,还可以用于进程间通信(InterProcessCommunication,IPC),解决两个不同Android应用程序之间的调用和通信问题。7.1Service组件应用Service的生命周期比较简单,仅包括全生命周期和活动生命周期,还有三个事件回调函数,分别是onCreate()、onStart()和onDestroy()。Service生命周期从onCreate()开始到onDestroy()结束,在onCreate()中完成Service初始化工作,在onDestroy()中释放所有占用的资源。活动生命周期从onStart()开始,但没有与之对应的“停止”函数,因此可以近似认为活动周期也是以onDestroy()标志结束。Service的使用方式一般有两种,一种是启动方式,另一种是绑定方式。在启动方式中,通过调用Context.startService()启动Service,通过调用Context.stopService()或Service.stopSefl()停止Service。因此,Service一定是由其他的组件启动的,但停止过程可以通过其他组件或自身完成。在启动方式中,启动Service的组件不能获取到Service对象,因此无法调用Service中实现的方法,也不能获取Service中任何状态和数据信息。因此,如果仅以启动方式使用Service,这个Service需要具备自管理的能力,且不需要通过函数调用向外部组件提供数据或功能。7.1Service组件应用在绑定方式中,Service的使用是通过服务链接(Connection)实现的,服务链接能获取Service对象,因此绑定Service组件可以调用Service中实现的函数,或直接获取Service中状态和数据信息。使用Service的组件通过Context.bindService()建立服务链接,通过Context.unbindService()停止服务链接。如果在绑定过程中Service没有启动,Context.bindService()会自动启动Service。而且同一个Service可以绑定多个服务链接,这样可以同时为多个不同的组件提供服务。当然,这两种使用方法并不是完全独立的,可以某些情况下混合使用启动方式和绑定方式。还是以MP3播放器为例,在后台工作的Service通过Context.startService()启动某个特定音乐播放,但在播放过程中如果用户需要暂停音乐播放,则需要通过Context.bindService()获取服务链接和Service对象,进而通过调用Service对象中的函数,暂停音乐播放过程,并保存相关信息。在这种情况下,如果调用Context.stopService()不能停止Service,需要在所有服务链接关闭后,Service才能真正停止。7.2.1服务管理服务管理主要指服务的启动和停止,在介绍如何启动和停止服务前,首先说明如何在代码中实现Service。Service是一段在后台运行、没有用户界面代码。这个Service最小代码集并不能完成任何实际的功能,为了使Service具有实际意义,一般需要重载onCreate()、onStart()和onDestroy()。Android系统在创建Service时,会自动调用onCreate(),用户一般在onCreate()方法中完成必要的初始化工作,例如创建线程、建立数据库链接等。在Service没有必要再存在时,系统会自动调用onDestroy(),用户在onDestroy()释放所有占用的资源。通过Context.startService(Intent)启动Service时,onStart()则会被系统调用,Intent会传递给Service一些重要的参数。当然,不是所有的Service都需要重载这三个函数,完全可以根据实际情况选择需要重载的某个函数。7.2.1服务管理重载onCreate()、onStart()和onDestroy()三个函数时,务必要在代码中调用父函数,如上代码所示。完成Service类后,需要在AndroidManifest.xml文件注册这个Service。这个注册过程非常重要,如果用户不注册,则这个Service根本无法启动。AndroidManifest.xml文件中注册Service的代码如下:serviceandroid:name=.RandomService/使用service标签声明服务,其中的android:name表示的是Service的类名称,一定要与用户建立的Service类名称一致。在完成Service代码和注册后,下一步来说明如何启动和停止Service。有两种方法启动Service,显式启动和隐式启动。显式启动需要在Intent中指明Service所在的类,并调用startService(Intent)函数启动Service,示例代码如下:finalIntentserviceIntent=newIntent(this,RandomService.class);startService(serviceIntent);7.2.1服务管理隐式启动则需要在注册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);startService(serviceIntent);7.2.1服务管理如果Service和调用服务的组件在同一个应用程序中,可以使用显式启动或隐式启动,显式启动更加易于使用,且代码简单。但如果服务和调用服务的组件在不同的应用程序中,则只能使用隐式启动。无论是显式启动还是隐式启动,停止Service的方法都是相同的,将启动Service的Intent传递给stopService(Intent)函数即可,示例代码如下:stopService(serviceIntent);在调用startService(Intent)函数首次启动Service后,系统会先后调用onCreate()和onStart(),如果再次调用调用startService(Intent)函数,系统则仅调用onStart()。在调用stopService(Intent)函数停止Service时,系统会调用onDestroy()。无论调用过多少次startService(Intent),在调用stopService(Intent)函数时,系统仅调用onDestroy()一次。7.2.2使用线程在Android系统中,Activity、Service和BroadcastReceiver都是工作在主线程上,因此任何耗时的处理都会降低用户界面的响应速度,甚至导致用户界面失去响应。当用户界面失去响应超过5秒钟,Android系统会允许用户强行关闭应用程序。因此,较好的解决方法是将耗时的处理过程转移到子线程上,这样可以避免负责界面更新的主线程无法处理界面事件,从而避免用户界面长时间失去响应。耗时的处理过程除了指运算量巨大的复杂运算外,还包括大量的文件操作、网络操作和数据库操作等等。线程是独立的程序单元,多个线程可以并行工作。在多处理器系统中,每个中央处理器(CPU)单独运行一个线程,因此线程是并行工作的。但在单处理器系统中,处理器会给每个线程一个小段时间,在这个时间内线程是被执行的,然后处理器执行下一个线程,这样就产生了线程并行运行的假象。无论线程是否真的并行工作,在宏观上可以认为子线程是独立于主线程,且能与主线程并行工作的程序单元。7.2.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();7.2.2使用线程当线程在run()方法返回后,线程就自动终止了。当然,也可以调用stop()方法在外部终止线程,但这种方法并不推荐使用,因为这种方法并不安全,有可能会产生异常。最好的方法是通知线程自行终止,一般调用interrupt()方法通告线程准备终止,线程会释放它正在使用的资源,在完成所有的清理工作后自行关闭。workThread.interrupt();其实interrupt()方法并不能直接终止线程,仅是改变了线程内部的一个布尔字段,run()方法能够检测到这个布尔字段,从而知道何时应该释放资源和终止线程。在run()方法的代码,一般通过Thread.interrupted()方法查询线程是否被中断。在很多情况下,子线程需要无限运行,除非外部调用Thread.interrupted()方法判断线程是否应被中断。7.2.2使用线程Handler允许将Runnable对象发送到线程的消息队列中,每个Handler对象绑定到一个单独的线程和消息队列上。当用户建立一个新的Handler对象,通过post()方法将Runnable对象从后台线程发送到GUI线程的消息队列中,当Runnable对象通过消息队列后,这个Runnable对象将被运行。privatestaticHandlerhandler=newHandler();//建立一个私有的静态的Handler对象publicstaticvoidUpdateGUI(doublerefreshDouble){//公有的界
本文标题:第7章 Service后台服务
链接地址:https://www.777doc.com/doc-3404744 .html