Service简介
Service是可以在后台长时间运行的Android应用程序组件,由其它组件启动,不提供用户界面。Service启动后,即使用户切换到另一个APP,它仍然在后台运行。另外,其它组件可以绑定到一个Service并与之进行交互,或进行进程间通信(IPC)。例如,Service可以在后台进行网络传输、音乐播放、I / O操作或Content Provider操作。
Service的分类
Service按运行状态分为三种不同类型。
Foreground Service:此类Service执行一些用户可感知的操作。例如,音频APP使用Foreground Service来播放音乐。Foreground Service必须在状态栏显示图标。即使用户没有与APP交互,Foreground Service也会继续运行。
Background Service:此类Service执行用户不直接感知到的操作。例如,如果APP使用Background Service压缩其存储空间。
注意:如果您的APP使用26或更高版本的API,则当应用程序本身不在前台时,系统会对运行的Background Service施加限制,详见 restrictions on running background services。在大多数情况下,您的APP应该使用 scheduled job 来代替Background Service。
Bound Service:应用程序组件通过调用bindService()绑定到Service。Bound Service提供了client-server接口,允许组件与Service进行交互、发送请求、接收结果或进行进程间通信(IPC)。Bound Service只在有组件被绑定的情况下运行。多个组件可以同时绑定到一个Service,当所有组件都解除绑定时,Service将被销毁。
一个Service既可以被Start也可以被bind,这取决于你的Service是否实现这两个回调函数:
1、 实现onStartCommand(),允许其它组件start这个Service。
2、 实现onBind(),允许其它组件bind这个Service。
任何应用程序组件(包括其它APP的组件)都可以使用Intent,通过start、bind的方式来启动一个Service。您也可以在manifest文件中将Service声明为私有的,以阻止其他APP的访问。
注意:Service运行在APP进程的主线程中,所以不能再Service中执行耗CPU、耗时的操作(如播放音乐或访问网络),而应在Service中创建一个新的线程来完成阻塞操作,防止出现ANR问题,保证界面交互的流畅性。
Service常用回调函数使用说明
要创建一个Service,必须继承Service或其子类,并重写一些关键的回调方法。下面是需要重写的回调方法:
onStartCommand():其它组件调用startService()时,系统会调用此方法,然后Service会一直在后台运行,直到调用stopSelf()或stopService()才停止。如果Service只供其它组件bind,则不需要重写此方法。
onBind():其它组件调用bindService()时,系统会调用此方法,并返回一个IBinder。客户端通过返回的IBinder与服务端进行通信。如果Service不供其它组件bind,则不需要重写此方法。
onCreate():如果Service没有运行,组件调用startService()或bindService(),系统会调用此方法;如果Service已经在运行,则不会调用此方法。可以在此方法中执行一些初始化设置,如新建线程、注册Listener、注册Receiver等。
onDestroy():当Service不再被使用并被销毁时,系统将调用此方法。这是Service在销毁前的最后一个回调,所以应该在此函数中清理资源、取消线程、注销Listener和Receiver。
如果组件通过调用startService()(系统会调用onStartCommand())来启动Service,则Service会一直运行,直到它调用stopSelf()停止自己或另一个组件调用stopService()来停止它。如果组件调用bindService()来启动Service,并且onStartCommand()不被调用,那么有组件绑定到该Service时,该Service才会运行。绑定到Service的所有客户端解除绑定后,系统会销毁此Service。
当系统内存不足时,为了保证有足够的系统资源供前台的用户界面运行,Android系统会强制停止Service。如果Service绑定到一个有用户焦点的Activity,那么Service被Kill的可能会减小;如果Service被申明为Foreground,那么极小可能会被Kill。如果Service以Start方式启动后在后台长时间运行,系统会降低其在后台任务中的优先级,这会大大提高Service被Kill的概率。所以如果Service以Start方式启动,则必须要做好被Kill后重启的设计。如果您的Service被系统Kill,且onStartCommand()的返回值是START_STICKY,那么它会在系统资源恢复时再重启。更多关于系统Kill Service的资料,请参考 Processes and Threading 。
在manifest文件中申明Service
和Android其它组件(如Activity)一样,Service也需要在manifest文件中申明。在元素中添加 元素,如下所示:
<manifest ... >
...
<application ... >
<service android:name=".ExampleService" />
...
</application>
</manifest>
元素中的几个常用属性如下:
1、 android:name:这是Service中唯一的必要属性,它申明了实现Service的类名。APP发布后此属性的值应保持不变,防止其它组件(甚至其他APP中的组件)用显式Intent启动Service时找不到对应的Service类(参考 Things That Cannot Change)。
2、 android:exported:设置其他APP的组件是否能启动或调用此Service。如果为true,则可以;如果为false,则不可以。
3、 android:process:设置Service是否运行在其它单独的进程中,如果不设置此属性,则运行在APP的进程中
4、 android:description:简洁描述Service功能的字符串。用户可以看到哪些Service在其设备上运行。如果用户看到不认识或不信任的Service,可能Stop这个Service。为了防止你的Service被用户Stop,请在 元素中添加 android:description 属性
有关在manifest中申明service的更多资料,请参考[service]。
注意:为保证APP的安全,请使用显式Intent启动Service,并且不要给Service设置Intent Filter。使用隐式Intent启动Service是不安全的,因为你不能确定哪个Service会响应这个Intent,且用户不知道是哪个Service启动了。从Android 5.0(API 21)开始,如果使用隐式Intent调用bindService(),系统会抛出异常。
新建一个Started Service
Started Service是其他组件通过调用startService(),并运行了onStartCommand()的Service。Service被start后,其生命周期独立于start它的组件。它可以一直在后台运行,即使start它的组件destroy了。所以Started Service完成任务后应调用stopSelf()停止自己或由另一个组件调用stopService()来停止它。
组件通过调用startService()来start一个Service,并通过Intent来指定Service和传递Data。Service在onStartCommand()函数中接收Intent。例如,一个Activity需要保存数据到在线数据库。这个Activity可以传递一个携带待保存Data的Intent给startService(),并启动Serv