Service简介
Service(服务)是一个没有用户界面的在后台运行执行耗时操作的应用组件。其他应用组件能够启动Service,并且当用户切换到另外的应用场景,Service将持续在后台运行。另外,一个组件能够绑定到一个service与之交互(IPC机制),例如,一个service可能会处理网络操作,播放音乐,操作文件I/O或者与内容提供者(content provider)交互,所有这些活动都是在后台进行。
Service分类
第一、按运行地点分类:
类别 |
区别 |
优点 |
缺点 |
应用 |
本地服务 (Local) |
该服务依附在主进程上 |
服务依附在主进程上而不是独立的进程,这样在一定程度上节约了资源,另外Local服务因为是在同一进程因此不需要IPC,也不需要AIDL。相应bindService会方便很多。 |
主进程被Kill后,服务便会终止。 |
非常常见的应用如:HTC的音乐播放服务,天天动听音乐播放服务。 |
远程服务 (Remote) |
该服务是独立的进程 |
服务为独立的进程,对应进程名格式为所在包名加上你指定的android:process字符串。由于是独立的进程,因此在Activity所在进程被Kill的时候,该服务依然在运行,不受其他进程影响,有利于为多个进程提供服务具有较高的灵活性。 |
该服务是独立的进程,会占用一定资源,并且使用AIDL进行IPC稍微麻烦一点。 |
一些提供系统服务的Service,这种Service是常驻的。 |
第二、按运行类型分类:
类别 |
区别 |
应用 |
前台服务 |
会在通知一栏显示 ONGOING的 Notification |
当服务被终止的时候,通知一栏的 Notification也会消失,这样对于用户有一定的通知作用。常见的如音乐播放服务。 |
后台服务 |
默认的服务即为后台服务,即不会在通知一栏显示 ONGOING的 Notification |
当服务被终止的时候,用户是看不到效果的。某些不需要运行或终止提示的服务,如天气更新,日期同步,邮件同步等。 |
有同学可能会问,后台服务我们可以自己创建 ONGOING 的 Notification 这样就成为前台服务吗?答案是否定的,前台服务是在做了上述工作之后需要调用 startForeground ( android 2.0 及其以后版本 )或 setForeground (android 2.0 以前的版本)使服务成为前台服务。这样做的好处在于,当服务被外部强制终止掉的时候,ONGOING 的Notification 任然会移除掉。
第三、按照使用方式分类:
类别 |
区别 |
startService启动的服务 |
主要用于启动一个服务执行后台任务,不进行通信。停止服务使用stopService |
bindService启动的服务 |
该方法启动的服务要进行通信。停止服务使用unbindService |
startService 同时也 bindService 启动的服务 |
停止服务应同时使用stepService与unbindService |
Service的状态
Service有两种状态,“started”和"bound”
通过startService()启动的服务处于“started”状态,一旦启动,service就在后台运行,即使启动它的应用组件已经被销毁了。通常started状态的service执行单任务并且不返回任何结果给启动者。比如当下载或上传一个文件,当这项操作完成时,service应该停止它本身。
通过bindService()启动的服务处于"bound"状态,一个绑定的service提供一个允许组件与service交互的接口,可以发送请求、获取返回结果,还可以通过夸进程通信来交互(IPC)。绑定的service只有当应用组件绑定后才能运行,多个组件可以绑定一个service,当调用unbind()方法时,这个service就会被销毁了。
特别注意:service与activity一样都存在与当前进程的主线程中,所以,一些阻塞UI的操作,比如耗时操作不能放在service里进行,比如另外开启一个线程来处理诸如网络请求的耗时操作。如果在service里进行一些耗CPU和耗时操作,可能会引发ANR警告,这时应用会弹出是强制关闭还是等待的对话框。所以,对service的理解就是和activity平级的,只不过是看不见的,在后台运行的一个组件,这也是为什么和activity同被说为Android的基本组件
Service的生命周期
见下图图1 Service的生命周期
1、使用context.startService() 启动Service是会会经历:
context.startService() ->onCreate()- >onStart()->Servicerunning
context.stopService() | ->onDestroy() ->Service stop
如果Service还没有运行,则android先调用onCreate()然后调用onStart();如果Service已经运行,则只调用onStart(),所以一个Service的onStart方法可能会重复调用多次。
stopService的时候直接onDestroy,如果是调用者自己直接退出而没有调用stopService的话,Service会一直在后台运行。该Service的调用者再启动起来后可以通过stopService关闭Service。
所以调用startService的生命周期为:onCreate--> onStart(可多次调用) -->onDestroy
2、使用使用context.bindService()启动Service会经历:
context.bindService()->onCreate()->onBind()->Servicerunning
onUnbind() -> onDestroy() ->Service stop
onBind将返回给客户端一个IBind接口实例,IBind允许客户端回调服务的方法,比如得到Service运行的状态或其他操作。这个时候把调用者(Context,例如Activity)会和Service绑定在一起,Context退出了,Srevice就会调用onUnbind->onDestroy相应退出。
所以调用bindService的生命周期为:onCreate--> onBind(只一次,不可多次绑定) --> onUnbind -->onDestory。
启动service,根据onStartCommand的返回值不同,有两个附加的模式:
1. START_STICKY 用于显示启动和停止service。
2. START_NOT_STICKY或START_REDELIVER_INTENT用于有命令需要处理时才运行的模式。
启动和绑定Service的选择
服务不能自己运行,需要通过调用Context.startService()或Context.bindService()方法启动服务。这两个方法都可以启动Service,但是它们的使用场合有所不同。
1. 使用startService()方法启用服务,调用者与服务之间没有关连,即使调用者退出了,服务仍然运行。
如果打算采用Context.startService()方法启动服务,在服务未被创建时,系统会先调用服务的onCreate()方法,接着调用onStart()方法。
如果调用startService()方法前服务已经被创建,多次调用startService()方法并不会导致多次创建服务,但会导致多次调用onStart()方法。
采用startService()方法启动的服务,只能调用Context.stopService()方法结束服务,服务结束时会调用onDestroy()方法。
2. 使用bindService()方法启用服务,调用者与服务绑定在了一起,调用者一旦退出,服务也就终止,大有“不求同时生,必须同时死”的特点。
onBind()只有采用Context.bindService()方法启动服务时才会回调该方法。该方法在调用者与服务绑定时被调用,当调用者与服务已经绑定,多次调用Context.bindService()方法并不会导致该方法被多次调用。
采用Context.bindService()方法启动服务时只能调用onUnbind()方法解除调用者与服务解除,服务结束时会调用onDestroy()方法。
Service和Thread的区别
我们拿服务来进行一个后台长时间的动作,为了不阻塞线程,然而,Thread就可以达到这个效果,为什么我们不直接使用Thread去代替服务呢?
Thread
Thread 是程序执行的最小单元,它是分配CPU的基本单位。可以用 Thread 来执行一些异步的操作。
Service
Service 是android的一种机制,当它运行的时候如果是Local Service,那么对应的 Service 是运行在主进程的 main 线程上的。如:onCreate,onStart 这些函数在被系统调用的时候都是在主进程的 main 线程上运行的。如果是Remote Service,那么对应的 Service 则是运行在独立进程的 main 线程上。因此请不要把 Service 理解成线程。
既然这样,那么我们为什么要用 Service 呢?其实这跟 android 的系统机制有关,我们先拿 Thread 来说。Thread 的运行是独立于 Activity 的,也就是说当一个Activity被 finish 之后,如果你没有主动停止 Thread 或者 Thread 里的run 方法没有执行完毕的话,Thread 也会一直执行。因此这里会出现一个问题:当 Activity 被 finish 之后,你不再持有该 Thread 的引用。另一方面,你没有办法在不同的 Activity 中对同一 Thread 进行控制。
举个例子:如果你的 Thread 需要不停地隔一段时间就要连接服务器做某种同步的话,该 Thread 需要在 Activity 没有start的时候也在运行。这个时候当你 start 一个 Activity 就没有办法在该 Activity 里面控制之前创建的 Thread。因此你便需要创建并启动一个 Service ,在 Service 里面创建、运行并控制该 Thread,这样便解决了该问题(因为任何 Activity 都可以控制同一 Service,而系统也只会创建一个对应 Service 的实例)。
因此你可以把 Service 想象成一种消息服务,而你可以在任何有 Context 的地方调用 Context.startService、Context.stopService、Context.bindService,Context.unbindService,来控制它,你也可以在 Service 里注册 BroadcastReceiver,在其他地方通过发送 broadcast 来控制它,当然这些都是 Thread 做不到的。
在AndroidManifest.xml 里 Service 元素的常见选项
参数 |
参数含义 |
android:name |
服务类名 |
android:label |
服务的名字,如果此项不设置,那么默认显示的服务名则为类名 |
android:icon |
服务的图标 |
android:permission |
申明此服务的权限,这意味着只有提供了该权限的应用才能控制或连接此服务 |
android:process |
表示该服务是否运行在另外一个进程,如果设置了此项,那么将会在包名后面加上这段字符串表示另一进程的名字 |
android:enabled |
如果此项设置为 true,那么 Service 将会默认被系统启动,不设置默认此项为 false |
android:exported |
表示该服务是否能够被其他应用程序所控制或连接,不设置默认此项为 false |
参考
1、《Android中的Service全面总结》
http://www.cnblogs.com/newcj/archive/2011/05/30/2061370.html
2、《Android开发四大组件——Service详解》
http://android.tgbus.com/Android/androidnews/201204/419662.shtml