Services
一个Service是一个app的组件,可以提供后台的长时间的操作,而不提供用户界面。别的app组件可以开启一个service,即使用户切换到别的app,它也会继续在后台运行。另外,一个组件可以绑定一个service与其相互作用,甚至提供进程间通信。(IPC, interprocess communication)例如,一个service可以在后台控制着network transaction,播放音乐,提供文件I/O流,或者和contentprovider相互作用。
一个service本质上有两种形式:
Started
当app组件调用startService()时,一个service就启动了,且其状态是“started”。一旦service启动了,它就在后台一直运行,即使启动这个service的组件被销毁了,该service会一直运行。通常情况下,一个开启的service表现一个独立的操作,并且不会给调用者返回结果。例如,它会通过网络下载或上传文件。当操作完成时,service也会自动停止。
Bound
当app组件调用bindService()方法的时候,一个service就与调用者绑定在一起了,并且是“bound”状态。一个受约束的service提供客户端(Client-Server Interface)允许组件与service相互交流,发送请求,获得结果,甚至通过进程间通信(IPC)与其他进程进行交流。一个受约束的service只有在app组件与其绑定期间才运行。大多数组件可以直接绑定到service上,但是一旦解除绑定,service会被摧毁。
尽管文档分别讨论了这两个service类型,你的service可以同时以这两种形式工作——它可以started(为了无限运行)同时也允许绑定。只要你简单的实现下面两个回调方法即可:onStartCommand()允许组件开启service而onBind()方法允许绑定。
和activity一样,任何组件都可以使用service。然而你可以在manifest文件中将service定义为私有的来阻止别的app访问它。这一点会在后文Declaring the service in the manifest中讨论。
Caution:一个service会在持有它的进程(process)的主线程(main thread)中运行——service不会创建它自己的线程(thread),也不会在别的process中运行(除非你特别指定一个process运行这个service)。这意味着,如果你的service将要进行任何高强度CPU工作或者阻塞操作(例如播放MP3或者上网),你应该创建一个新的线程(thread)并把service放入其中工作。使用单独的线程,你可以降低错误“应用程序无应答”发生的(ANR, Application Not Responding)的风险,同时应用程序的主线程可以更专注于处理用户与activity的交流。
The Basics
创建一个service,你必须创建Service类的子类(或者已经存在的Service类的子类)。在你的实现中,你需要覆盖一些处理service生命周期的重要的回调方法,在需要的情况下还要提供组件与该service绑定的机制。最重要的需要覆盖的回调方法如下:
onStartCommand ()
当别的组件,例如一个activity请求service启动(调用startService()方法)的时候,系统会调用这个方法。一旦这个方法执行了,service就启动并且在后台无限期运行。如果你执行了这个方法,在工作完成之后你必须通过调用stopSelf()或者stopService()方法停止这个service。如果你仅仅是提供绑定,那么你不需要实现这个方法
onBind ()
当别的组件通过bindService()方法绑定该service时(例如提供RPC服务,RemoteProcedure Call Protocol,远程过程调用协议),系统会调用这个方法。对这个方法的实现中,你必须提供用户可以与该service交流的接口。(可以通过返回一个IBinder实现)这个方法你必须执行,如果你不允许该service与其他组件绑定,那么返回null即可。
onCreate ()
当service最初被创建的时候,系统会调用这个方法来执行一次性设置程序。(在它调用onStartCommand()和onBind()之前)如果service已经在运行了,那么这个方法不会被调用。
onDestroy ()
当service不再使用且将要被销毁的时候,系统会调用这个方法。你的service应该执行这个方法来清除类似线程(thread),注册监听器(register listener),接收器(receiver)等资源。这是service最后接收到的调用。
如果一个组件通过startService()启动service(这将导致调用onStartCommand()),那么service会一直运行,直到它调用stopSelf()停止自己,或者别的组件调用stopService()停止该service。
如果一个组件调用了bindService()创建一个service(同时onStartCommand()没有调用),那么该service只在绑定到组件期间运行,一旦service与组件解除绑定,系统会销毁该service。
Android系统只有在内存低的时候会强制停止一个service,因为它要回收系统资源给用户当前关注的activity使用。如果service正好绑定在获得用户焦点的当前activity,那么它不太可能会被系统停止,如果一个service被声明在前台运行(后面会讨论到),那么它将永远不可能被停止。另外,如果service已经被启动并且长时间运行,那么随着时间的流逝,系统会降低它在后台task中的位置,该service就越有可能被关闭——如果你的service启动了,那么你必须将它设计为可以被系统重启的模式。如果系统关闭了你的service,当资源再次可用的时候它会很快重启该service(尽管这也取决于你返回给onStartCommand()的值,稍后讨论)。更多关于系统可能摧毁service的文档,请参照Processes and Threading。
下面是如何创建不同种类的service以及如何使用它们。
Declaring a service in the manifest
像是activity(以及其他租组件),你必须在app的manifest文件中声明所有的service。
在<application>标签下添加<service>子标签即可声明。如下所示:
<manifest …>
… …
<application…>
<serviceandroid: name=”.ExampleService”/>
……
</application>
</manifest>
你可以在<service>元素中包含别的属性定义service属性,像是定义权限许可(permission required)启动service以及定义service在那个process中运行等。属性android:name是唯一要求的定义的属性——它指定service的类名。当你发布你的app时,你不应该修改这个名字,如果你修改了这个名字,那么你会冒着破坏依赖显式intent启动或绑定service代码的风险。(可以参考文档Things That Cannot Change)
为了确保你的app是安全的,可以使用显式intent开启或者绑定你的service,且不为这个service声明intent filter。如果你需要允许一些模糊service启动,你可以给你的service提供intent filter同时将组件名称从Intent中排除(exclude the component name from the Intent),但是你仍然需要使用setPackage()为intent设置package,它为目标service提供足够的消除歧义服务。
另外,通过设置android: exported标签为false可以限制你的service只为你的app所用。这举措有效的阻止别的app启动你的service,即使是使用显式intent也无法启动。
Shouldyou use a service or a thread?
Service是一个简单的可以在后台运行的组件,并不需要用户与你的app进行交互。如果你需要在主线程之外执行某些工作,但是又需要在你的app运行期间执行,那么与其创建一个service,你更应该使用thread。例如,如果你想在你的activity运行期间播放音乐,你可以在onCreate()中创建一个thread,在onStart()方法中启动该thread,最后在onStop()方法中停止它。你也可以考虑使用AsynTask或者HandlerThread替代传统的Thread类。可以查看Processes and Threading文档获取更多内容。
记住如果你使用了一个service,它在默认情况下运行在你的app的主线程中(mainthread),如果你的service进行集中或阻塞操作,你应该将它放入一个新创建的线程中。
Creating a Started Service
别的app组件调用startService(),导致调用service的onStartCommand()来启动一个service。
当一个service启动时,它会有一个生命周期,并且这个生命周期依赖于启动它的组件的生命周期,即使启动它的组件已经被销毁,它也会在后台无限运行下去。只有通过它自己调用stopSelf()或者别的组件调用stopService()时,该service才会停止。
一个app组件例如activity调用startService()启动service,并传递一个Intent声明该service,并在Intent中包含一些该service可能需要的数据。通过onStartCommand(),service获得这个intent。
例如,假设一个activity保存一些数据到在线数据库上。这个activity可以启动一个伴随service(companion service),通过方法startService()传递需要保存的数据给service。该service通过onStartCommand()获得传递过来的intent,连接网络并执行数据库事务。当事务结束后,service会停止并销毁。
Caution: 默认情况下service与app在同一个process下运行(即app的mainthread中)。所以,如果当用户在与同一个线程中app下的一个activity进行交互时,而你的service需要进行集中或阻塞操作,service会降低activity的性能。为了避免这种情况,你应该为service开启一个新的thread。
通常情况下,你可以继承下面两者之一来创建一个started service:
Service
这是所有service的基本类。当你继承这个类的时候,最重要的是你必须创建一个新的thread,在这个thread中执行所有的service。
IntentService
这是Service类的子类,使用一个worker thread处理所有的启动请求,一次一个。如果你不要求你的service同时处理多请求,那么这个类是最好的选择。你需要做的只是执行onHandleIntent()方法,该方法可以从每个启动请求中获得intent以便于你进行后台任务。
接下来的部分描述如何使用上述类实现你的service。
Extending the IntentService class
因为大部分的started service不需要同时处理多请求(这是很危险的多线程场景),所以IntentService可能是最佳实现你的service的类。
IntentService包括:
● 创建一个默认的worker thread,从你的app主程序中分别发送所有的intent给方法onStartCommand()
● 创建一个工作队列,每次传递一个intent给你的实现方法onHandleIntent(),这样你将永远不必担心多线程的问题
● 当所有的开启请求被处理以后,停止service,你永远不需要调用stopSelf()方法
● 提供默认实现onBind()并返回null
● 提供默认实现onStartCommand()并传递intent到工作队列(work queue)中,然后再发送到onHandleIntent()方法中。
综上所述,你需要做的仅仅是提供onHandleIntent()方法的实现(尽管你也需要提供service的构造器)
下面就是IntentService实现的例子:
public class HelloIntentService extendsIntentService{
/**
* Aconstructor is required, and must call the super IntentService (String)
*constructor with a name for the worker thread
*/
publicHelloIntentService(){
super(“HelloIntentService”);
}
/**
*TheIntentService calls this method from the default worker thread with the *intentthat started the service. When this method returns, IntentService stops *theservice, as appropriate.
*/
@Override
protectedvoid onHandleIntent(Intent intent){
//Normally we would do some work here,like download a file.
//For our sample, we just sleep for 5seconds.
longendTime=System.currentTimeMillis()+5*1000;
while(System.currentTimeMillis()<endTime){
synchronized(this){
try{
wait(endTime-System.currentTimeMillis());
}catch (Exception e){}
}
}
}
}
这就是你需要的所有代码:一个构造器和onHandleIntent()的实现。
如果你也想覆盖别的回调方法,像是onCreate(),onStartCommand()或者是onDestroy(),确定调用其父类的实现,然后IntentService就可以适当的操作worker thread的生命周期。
例如,onStartCommand()必须返回默认的实现(该实现传递intent到方法onHandleIntent())
@Override
public int onStartCommand(Intent intent, intflags, int startId){
Toast.makeText(this, “service starting”, Toast.LENGTH_SHORT).show();
returnsuper.onStartCommand(intent, flags, startId);
}
除了onHandleIntent()方法,另外一个唯一不需要调用其父类方法的是onBind()。
下一部分你将看到继承Service类的相同类型的service是如何实现的,它们允许处理并发的启动请求。
Extending the Service class
就像你在先前部分看到的,使用IntentService使得实现一个started service非常简单。然而,如果你要求你的service可以提供多线程服务(而不是通过一个工作队列分别实现启动请求),那么你可以继承Service类处理每个intent。
为了方便对比,下面的代码和上面的继承了IntentService的类一样,实现相同的功能。即为每个启动请求,它都使用一个worker thread执行,每次只在线程中实现一个请求。
public class HelloService extends Service {
privateLooper mServiceLooper;
privateServiceHandler mServiceHandler;
//Handlerthat receives message from the thread
privatefinal class ServiceHandler extends Handler{
publicServiceHandler (Looper looper){
super(looper);
}
@Override
public voidhandleMessage (Message msg){
//Normally we would do some work here,like download a file
//For our sample, we just sleep for 5seconds
longendTime=System.currentTimeMillis()+5*1000;
while(System.currentTimeMillis()<endTime) {
synchronize(this) {
try{
wait(endTime-System.currentTimeMillis());
}catch (Exception e){}
}
}
}
//Stop theservice using the startId, so that we don’t stop
//the servicein the middle of handling another job
stopSelf(msg.arg1);
}
@Override
publicvoid onCreate (){
// Start upthe thread running the service. Note that we create a //separate thread becausethe service normally runs in the process’s //main thread, which we don’t wantto block. We also make it //background priority so CPU-intensive work will notdisrupt our UI.
HandlerThreadthread= new HandlerThread(“ServiceStartArguments”,
Process.THREAD_PRIORITY_BACKGROUND);
thread.start();
//Getthe HandlerThread’s Looper and use it for our Handler
mServiceLooper=thread.getLooper();
mServiceHandler=newServiceHandler(mServiceLooper);
}
@Override
public intonStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(this, “service starting”,Toast.LENGTH_SHORT).show();
//For eachstart request, send a message to start a job and deliver the //start ID so weknow which request we’re stopping when we finish the job
Messagemsg=mServiceHandler.obtainMessage();
msg.arg1=startId;
mServiceHandler.sentMessage(msg);
//if we getkilled, after returning from here, restart
returnSTART_STICKY;
}
@Override
publicIBinder onBind (Intent intent) {
// we don’t provide binding, so returnnull
return null;
}
@Override
public voidonDestroy(){
Toast.makeText (this, “service done”,Toast.LENGTH_SHORT).show();
}
}
对比之下可以知道,这比IntentService多出了很多工作。
然而,因为你自己在onStartCommand()中处理每一个调用,你可以同时处理多个请求。当然例子中没有示范,但是只要你想,你可以为每一个请求创建一个thread并马上运行它们(而不是等待前一个请求结束)
注意onStartCommand()方法必须返回一个整数。这个整数描述系统应该如何在event中继续执行service以及关闭它。从onStartCommand()中返回的值一定是一下的其中之一:
START_NOT_STICKY
如果系统在onStartCommand()返回后关闭service,那么除非有intent传递过来时,否则不要重新创建service。这是避免在没必要的时候,或者是你的app可以简单的重启任何没有完成的任务的时候运行的service的最安全的方法。
START_STICKY
如果系统在onStartCommand()方法返回后关闭service,重新创建service且调用onStartCommand(),但是不重新发送最后一个intent。取而代之的是,除非他们发送一个intent来启动该service,否则系统会以一个null intent调用onStartCommand()。这个方法使用于类似播放器的service,他们不执行命令,但是可以无限运行并且可以暂时中断。
START_REDELIVER_INTENT
如果系统在onStartCommand()返回后关闭service,重新创建service且使用最后一个intent重新调用onStartCommand()方法。任何传递进来的intent都会按顺序排列。这个方法适用于那些需要积极执行任务并且需要立即重启的service,例如下载文件等。
更多信息,请查看它们的连接
Starting a Service
你可以从一个activity或其他app组件通过传递一个Intent(声明启动service)给startService()来启动一个service。Android系统会调用onStartCommand()方法并传递intent。(你永远不需要自己手动调用onStartCommand()方法)。
例如,一个activity启动示例service可以使用显式intent传递给startService():
Intent intent=new Intent(this,HelloService.class);
startService(intent);
startService()方法会立即返回然后android系统调用service的onStartCommand()方法。如果service没有运行,那么系统会首先调用onCreate()然后再调用onStartCommand()。
如果service不提供绑定,传递给startService()的intent是只允许app组件与service交流的模式。然而,如果你希望service返回一个值,那么可以创建一个PendingIntent广播(使用getBroadcast()),将它放在intent中传递给service并开启它。Service可以使用该广播传递结果。
Stopping a service
一个started service必须管理它自己的生命周期。就是说,系统不会停止或销毁service除非它必须恢复系统内存,那么service会在onStartCommand()返回后继续运行。所以,在这个情况下service必须调用stopSelf()停止其自身或者其他app调用stopService()停止该service。
一旦调用stopSelf()或者stopService(),系统会尽快销毁service。
然而,如果你的service在onStartCommand()并发处理多个请求,那么当你处理完一个启动请求时也不应该停止当前service,因为你可能已经收到一个新的启动请求(在第一个请求结束的时候停止service可能会终止第二个请求的继续)。为了避免这种事情的发生,你可以使用stopSelf(int)保证你的请求终止service总是依赖于最近的启动请求。(you can use stopSelf(int) to ensure that yourrequest to stop the service is always based on the most recent start request.)就是说,当你调用stopSelf(int)的时候,将启动请求的ID传递给与之相关联的停止请求。(startId传递给onStartCommand())那么在你调用stopSelf(int)之前,service收到一个新的启动请求,那么ID不匹配service也不会停止。
Caution:当工作完成时,app停止service非常重要,这样避免浪费系统资源以及导致电池电量低下。如果有需要的话,别的app可以调用stopService()停止service。即使你启用绑定service,如果service没有收到onStartCommand()调用,你也必须自己的停止该service。
更多信息请参见Managing the Lifecycle of a Service.
Creating a Bound Service
一个bound service是允许app组件调用bindService()绑定的service,这样可以创建一个long-standing connection(且通常不允许组件通过startService()启动它)。
当你想和来自app中的activity或其他组件的service交流时,或通过IPC(进程间通信)给其他app显示你的app的一些功能的时候,你可以创建一个boundservice。
创建一个bound service,你必须实现onBind()方法,返回一个IBinder定义与service交互的接口。别的app组件也可以调用bindService()方法获得该接口,调用service中的方法。该service只存在于与组件的绑定期间,如果没有任何组件与该service绑定,系统会销毁它。(你不需要像停止通过onStartCommand()启动的service那样停止绑定的service)
创建一个绑定的service,你最先要做的事情是定义用户如何与service交互的接口。这个存在于用户和service之间的接口必须是IBinder类的实现,并且IBinder类也是你从onBind()方法中返回的结果。一旦用户收到IBinder,那么它立即通过这个接口与service交互。
大部分client(组件)可以立即与service绑定。当一个用户结束与service的交互,它会调用unbindService()解除绑定。一旦没有用户与service绑定,系统会销毁service。
有多种方法实现bound service且实现方法也比实现started service要复杂,实现bound service的具体实施可以参照文档Bound Services.
Sending Notifications to the User
一旦运行,一个service可以使用Toast Notification或Status Bar Notification通知事件的用户。
一个toast notification是显示在当前屏幕的一条信息,通常存在一会儿就消失;而bar notification在status bar中提供带有信息(message)的图标,用户可以点击它执行一个action。(例如启动一个activity)
通常情况下,当一些后台工作结束时使用status bar notification是最好的选择(例如文件下载完成),用户可以马上操作该文件。当用户从界面选择该notification时,这个notification会启动一个activity。(例如查看下载好的文件)
可以查看文档Toast Notification或Status Bar Notification获得更多信息
Running a Service in the Foreground
一个foreground service是一个用户主动注意到的东西且不会再系统内存低时被销毁。一个前台service必须为status bar提供notification,放在“正在运行(Ongoing)”标题下,这意味着除非service停止或者从前台上被移除,通知不会消失。
例如,一个利用service播放音乐的播放器必须设置在前端运行,因为用户需要明确清楚他的操作。在status bar上的notification(通知)可以显示当前播放的音乐,允许用户launch一个activity与当前音乐播放器交互。
调用startForeground()可以定义一个service在前端运行。这个方法有两个参数:一个整数定义特定的notification以及在status bar上显示的notification对象,例如:
Notification notification=new Notification
(R.drawable.icon,getText(R.string.ticker_text), System.currentTimeMillis());
Intent notificationIntent=new Intent(this,ExampleActivity.class);
PendingIntent pendingIntent=PendingIntent.getActivity(this,0, notificationIntent,
0);
notification.setLatesEventInfo(this,getText(R.string.notification_title),
getText(R.string.notification_message),pendingIntent);
startForeground(ONGOING_NOTIFICATION_ID , notification);
Caution:你传递给startForeground()的ID一定不能是0。
调用stopForeground()可以将前台运行的service移除。该方法带有一个boolean参数表明是否将status bar一起移除。该方法不会停止service。然而,如果在service仍在前台运行的时候停止它,那么notification也会移除。
更多信息,参见Creating Status Bar Notification。
Managing the Lifecycle of a Service
一个service的生命周期比activity的简单得多。然而,你更应该关注的是你service如何被创建和销毁,因为service可以在后台不被用户注意的运行。
Service生命周期——从它被创建到它被销毁——可能遵从下面两条路径:
● A started service
其他组件调用startService()创建这一类型service。该类型service无限运行并且必须通过其自身调用stopSelf()停止。其他组件也可以调用stopService()停止它。当它停止时,系统会摧毁该service。
● A bound service
当别的app组件(或client)调用bindService()时创建。Client利用IBinder接口与service交互。当调用unbindService()时该service立即关闭连接。多个client可以与同一个service绑定,当他们全部解除绑定的时候,系统才会销毁该service。(该;类型service不需要手动停止)
这两种路径并不完全分离。意思是,你可以将一个已经通过startService()启动的service与其他的app组件绑定。例如,一个在后台运行的音乐播放器service可以由一个声明了播放某一首音乐的Intent传递到startService()来启动。然后,如果用户想要控制该播放器或想要获取当前播放的歌曲的信息,可以让一个activity与service绑定(通过bindService())。像这个例子,stopService()或stopSelf()并不能完全停止service,除非client已经与service解除绑定。
Implementing the lifecycle callbacks
和activity相似,service也有生命周期回调方法,你可以实现这些回调方法监控service的状态,以及在适当的时间执行相应的工作。下面的框架说明生命周期中的不同方法:
public class ExampleService extends Service{
intmStartMode; //indicates how to behave ifthe service is killed
IBindermBinder; //interface for clientsthat bind
booleanmAllowRebind; //indicateswhether onRebind should be used
@Override
publicvoid onCreate(){
//Theservice is being create
}
@Override
publicint onStartCommand(Intent intent, int flags, int startId){
//The service is starting, due to a callto startService()
return mStartMode;
}
@Override
publicIBinder onBind(Intent intent){
//A client is binding to the service withbindService()
return mBinder;
}
@Override
publicboolean onUnbind(Intent intent){
//All clients have unbound with unbindService()
}
@Override
publicvoid onRebind(Intent intent){
//Aclient is binding to the service with bindService()
//afteronUnbind() has already been called
}
@Override
publicvoid onDestroy(){
//The service is no longer used and isbeing destroyed
}
}
Note: 与activity的生命周期回调方法不同,你不需要调用service的父类回调方法。
左图是service的生命周期。靠左边的图标说明的是通过startService()创建的created service生命周期而靠右图表说明通过bindService()创建的bind service生命周期。
通过这些方法的实施,你可以监控service生命周期的两个嵌套循环(nested loops):
● entirelifetime
service的完整生命周期是从方法onCreate()的调用到onDestroy()的调用返回为止。和activity一样,service也在onCreate()中初始化其设置,释放所有保存在onDestroy()中的资源。例如,一个音乐播放service可以在onCreate()中创建thread,这样音乐可以在onCreate()中播放,而在onDestroy()中停止该thread。
● activelifetime
service的有效时间指service调用onStartCommand()或onBind()时。这两个方法都处理从startService()或bindService()传递过来的intent。
如果service是started类型的,那么activelifetime与entirelifetime同时间结束(即使onStartCommand()返回后,service依旧active),如果service是bound类型,那么active lifetime会在onUnbind()返回后结束。
Note:尽管startedservice会被stopSelf()或者stopService()停止,但是没有相关的service回调方法(没有onStop()回调方法)所以除非service与client绑定,否则系统会在service停止的时候摧毁它,且onDestroy()是唯一能够接受到这个结果的回调方法。
图二说明了service的经典回调方法。尽管started和bound类型的service被不同的方法开启(startService()/bindService()),但是每个service都可能潜在的被绑定,即是说通过onStartCommand()的service也可以收到方法onBind()的调用。
更多信息可以参考Bound Service文档,它说明更多关于onRebind()方法的使用以及Managing the Lifecycle of a BoundService。