概述
1、当用户在与当前应用程序不同的应用程序时,Service可以继续在后台运行。
2、Service可以让其他组件绑定,以便和它交互并进行进程间通信。
3、Service默认运行在创建它的应用程序的主线程中。
Service的使用主要是因为应用程序里面可能需要长时间地运行一些任务但是又不需要用户界面或者应用程序本身需要对外提供一些函数给其他的应用程序调用。每一个Service实体类必须相应地在它的包中的AndroidManifest.xml有一个<service>
配置节的声明。Services可以用 Context.startService()
或Context.bindService()来启动。
下面我们将会涉及到几个主题:
1、什么是Service?
2、Service的生命周期
3、进程生命周期
什么是Service?
很多Android的初学者都对Service抱有浓浓的疑惑,Service到底是什么,但是似乎更多的问题是Service到底不是什么?
- Service不是一个独立的进程。Service不像他的名字所暗示的,它没有自己独立的进程;除非另外指定,Service运行在跟应用程序相同的进程中,并且他是应用程序进程的一部分。
- Service不是一个线程。但也不是说Service就运行在界面主线程(Main Thread)中( Android中为了避免应用程序没有响应的错误)。
其实Service非常简单,它主要有两个特性:
- 它是一种应用程序跟系统进行沟通的机制,告诉系统有一些任务要在后台运行(即在用户使用应用程序的时候)。我们可以对应地调用
Context.startService()
让系统运行Service,Service会一直运行,直到Service本身或者其他人显式地停止它。 - 它是一个应用程序对外(其他应用程序)暴露函数接口的机制。我们可以对应地调用
Context.bindService()来运行,这样子其他的应用程序就可以长时间地跟Service连接,进行一些交互。
在一个Service组件被创建的时候,系统做的工作其实就是在主线程上实例化Service组件,然后调用Service的
onCreate()还有其他Service的回调函数而已。也就是说,可以认为系统除了调用一下Service的函数,其他什么都不会再做了,如果你想要运行什么长时间的任务,比如从网络上下载文件,那么跟以前一样,你需要自己写一个线程,当然是在Service内对应的函数中。
不过正因为Service本身如此简单,我们可以自由定义Service,使之与外界进行一些简单或者复杂的交互。我们可以将Service视为本地Java对象,进行直接的方法调用(具体的示例见Local Service Sample),也可以通过AIDL提供一个完全的远程接口。
Service的生命周期
Service是由Android系统进行调度的。如果有人调用Context.startService(),系统就会将Service取回(有必要的时候创建它,并调用它的
onCreate()
方法)然后调用它的onStartCommand(Intent, int, int)
方法,使用客户端提供的参数。这时候,Service会继续运行运行直到Context.stopService()
or stopSelf()被调用。注意:我们可以多次调用Context.startService()不会发送嵌套调用(尽管会有多次onStartCommand()的响应),所以不管Service多少次被启动,它都只会在一次Context.stopService() 或stopSelf()的调用中被停止。然而,service可以使用它的
stopSelf(int)方法来保证自己不会被停止,直到启动它的intents被处理完。
对于一个启动的服务(started Service)有两种操作模型,这决定了它们不同的运行方式,而运行方式由它们的OnStartCommand()函数的返回值决定。
START_STICKY适用于一个服务需要被明确地指定启动或停止的情况。而while
START_NOT_STICKY
或START_REDELIVER_INTENT使用于一个服务需要一直运行直到处理完它们收到的命令。
客户端也可以使用
Context.bindService()
去取得与一个Service的长连接。它同样会创建一个Service,如果Service没有运行(调用onCreate()
来创建),但不会调用onStartCommand()。客户端将会接收从service的onBind(Intent)方法返回的
IBinder对象,从而客户端就可以调用service上的方法了。只要客户端和service的连接一建立,service就会一直保持运行状态(无论客户端是否有保存service的IBinder引用)。通常IBinder返回的是一个用aidl写好了的复杂接口。
一个service可以同时被启动和绑定。在这种情况下,系统会保持一个带有
Context.BIND_AUTO_CREATE 标识的service一直运行只要他被启动或者有一个或多个连接跟这个服务绑定着。一旦没有被启动和连接,service的
onDestroy()就会被调用然后service就会被终止。在
onDestroy()方法返回前,所有的线程和资源都应该被清理掉。
Process Lifecycle
前面我们讲到,Service其实是运行在寄居在应用程序的进程当中的,也就是说当应用程序的进程被销毁的时候,Service也就被销毁了。当然,Android系统会尽力保证带有Service的应用程序进程存活尽可能长的时候,只要进程中有Service已经启动或者有别的客户端需要使用着Service的服务。当Android系统内的内存不足,需要杀死一些进程来释放内存时,在下面情况中,线程的优先级会因为Service而增高,进而减少被系统回收的可能性:
如果进程中的Service正在执行onCreate()
,onStartCommand()
, 或onDestroy()方法,那么这个进程就会变成前台处理(foreground process)进程,进而能够保证进程不会被回收。
如果一个Service被启动了,那么他的宿主进程就会被系统认为比当前用户正在使用的的应用程序的进程更不重要,但比其他的后台进程更重要。因为通常用户当前只使用一小部分进程(也就是用户看得见的进程),这意味着Service所在的进程不应该被回收掉,除非系统内存极度缺乏。
如果一个客户端(Client)跟Service绑定,那么这个Service的宿主进程会被系统认为跟客户端进程是同等重要的,也就是,如果一个客户端程序对用户是可见的(也就是正在被使用),那么Service自身也会被认为是可见的。
已经开始运行了的Service可以使用startForeground(int, Notification) API将当前进程放到前台状态(Foreground state),这样系统就会认为他是用户想要使用的进程,因此就不会成为低内存时的待回收进程。(但是理论上,在内存极度匮乏的情况下,这个进程仍有可能被回收。实际上,一般不会有这么极端的情况)。
注意,这说明当我们的Service正在运行的时候,它都不会被系统杀死,除非内存快满了。如果Service被杀掉了,那么随后系统还会尝试着重新启动Service。这样子,我们就必须考虑到如果我们实现
onStartCommand()方法,用异步或者其他线程来完成Service的任务,那么我们就可能需要使用
START_FLAG_REDELIVERY
让系统重新传递一个Intent,以便在Service重新启动后,Intend会丢失。
在同一个进程中的应用程序组件(application components)像Service(或者Activity)可以增加进程在所有进程中的重要性,而不是仅仅增加Service的重要性。
补充说明:
前面谈到Service是由系统进行调度的,那么对我们来讲有什么意义呢?他又体现在什么地方呢?
因为Service是由系统进行调度的,那么它的生命周期与Activity的生命周期也就无关了,也就是假设ServiceA是由ActivityA启动的,即使当ActivityA因为某种原因调用了OnDestroy()方法将自己销毁掉之后,ServiceA依然欢快地运行着。