首页 > 代码库 > Android开发-API指南-服务
Android开发-API指南-服务
Service
英文原文:http://developer.android.com/guide/components/services.html
采集(更新)日期:2014-12-23
原博客:http://blog.sina.com.cn/s/blog_48d49130010102pn.html
在本文中
- 概述
- 在manifest中声明服务
- 创建一个started服务
- 扩展IntentService类
- 扩展Service类
- 启动一个服务
- 终止一个服务
- 创建一个bound服务
- 向用户发送通知
- 在前台运行服务
- 对服务的生命周期进行管理
- 实现生命周期回调方法
关键类
Service
IntentService
范例
ServiceStartArguments
LocalService
参阅
- bound 类型的服务
Service
是一种应用程序组件,它能够在后台进行一些耗时较长的工作,并且不提供用户界面。 服务能被其他应用程序的组件启动,即使用户切换到另一个应用也还能保持后台运行。 此外,服务还能被某个应用程序组件绑定,并与其交互,甚至可以进行进程间通信(IPC)。 比如,服务可以处理网络传输、音乐播放、执行文件I/O、或者与content provider进行交互,所有这些都是后台进行的。
服务有以下两种基本形式:
- Started
- 如果服务是由某个应用程序组件(比如一个 Activity)通过调用
startService()
来启动的,则该服务就被称为 “started” 类型。 一旦被启动,即便启动它的组件已经被销毁了,服务还是能在后台一直运行下去,。 通常,started 类型的服务只完成一项操作,并且不会向调用者返回结果。 比如,它可以通过网络下载或上传文件。 当此项操作完成后,服务就应该自行终止。 - Bound
- 如果服务被某个应用程序组件通过调用
bindService()
进行了绑定,则该服务就被称为 “bound” 类型。 bound 服务提供了一种客户端/服务器的方式,允许组件与其进行交互、发送请求、获取结果,甚至可以利用进程间通信(IPC)跨进程来完成这些操作。 bound 服务的生存期和被绑定的应用程序组件相同。 多个组件可以同时绑定一个服务。仅当所有的组件都解除绑定后,该服务才会被销毁。
虽然本文对上述两种类型的服务是分别进行介绍的,但是服务可以同时工作于两种方式下——既是 started 类型的(一直运行下去),又是能被绑定的。 只是有一点比较麻烦,需要实现两种回调方法:实现 onStartCommand()
以允许组件启动服务、实现 onBind()
以允许绑定。
无论服务是 started、bound、还是两者都支持,任何应用程序组件都可以使用它(即使是从另一个应用程序中),这类似于任何组件都可以使用某一个 Activity ——通过一个Intent
即可。不过,通过在 manifest 文件中把服务声明为私有(private)的,就可以阻止其他应用程序的访问了。这将在在 manifest 中声明服务一节中详细介绍。
警告: 服务运行于宿主进程的主线程中——它不会创建自己的线程也不是运行在单独的进程中(除非明确指定)。 这意味着,如果某个服务将要执行CPU消耗很高的任务,或者阻塞式的操作(比如播放MP3或网络操作),就应该在服务中创建一个新的线程来完成这些工作。 通过使用单独的线程,可以降低 Activity 发生“应用程序停止响应”(ANR)错误的风险,应用程序的主线程也可以专心处理用户与 Activity 的交互了。
概述
该使用服务还是线程?
服务只是一种能在后台运行的组件而已,哪怕用户不再与应用程序交互也没关系。 因此,应该只在确实必要时才创建服务。
如果你需要在主线程之外运行一些任务,但都是在用户与应用程序存在交互的情况下才会用到, 那就该创建一个新的线程才合适,而不是服务。 比如,应用程序需要播放一些音乐,但只是在 Activity 运行时才需要播放,就可以在 onCreate()
中创建一个线程,在 onStart()
中开始运行这个线程,然后在 onStop()
中终止它。还可以考虑使用 AsyncTask
或 HandlerThread
来代替传统的 Threadd
类。关于线程的详细信息,请参阅 进程和线程。
请记住,如果用到了服务,它默认就运行于应用程序的主线程中。 因此,如果服务将执行密集计算或者要完成阻塞式任务,还是应该在服务中创建一个新的线程来完成。
要创建一个服务,必须新建一个 Service
类的子类(或一个已有 Service 的子类)。 请在代码中按需重写(override)一些回调方法,用于对服务生命周期的重要节点进行响应,并为组件提供绑定机制。 最重要的需要重写的回调方法包括:
onStartCommand()
- 当其他组件,比如一个 Activity,通过调用
startService()
请求 started 类型的服务时,系统将会调用本方法。 一旦执行了本方法,服务就被启动,并在后台一直运行下去。 如果实现了本方法,就有责任在操作完成后通过调用stopSelf()
或stopService()
终止服务。 (如果只想提供 bind 类型的服务,就不需要实现本方法。) onBind()
- 当其他组件需要通过
bindService()
绑定服务时(比如执行 RPC ),系统将会调用本方法。 在本方法的实现代码中,必须返回IBinder
来提供一个接口,客户端用它和服务进行通信。 本方法必须实现。如果不允许被绑定,返回 null 即可。 onCreate()
- 当服务第一次被创建时,系统会调用本方法,用于执行一些一次性的配置工作(之前已调用过
onStartCommand()
或onBind()
) 了。如果服务已经在运行了,则本方法就不会被调用。 onDestroy()
- 当服务不再使用了并要被销毁时,系统会调用本方法。 应该通过实现本方法来进行一些清理资源的工作,诸如释放线程、注销已注册的侦听器(listener)和接收器(receiver)等。 这是服务收到的最后一个调用。
如果组件通过调用 startService()
(这会导致 onStartCommand()
的调用)启动了服务,那么服务将一直保持运行,直至自行用 stopSelf()
终止或由其他组件调用 stopService()
来终止它。
如果组件调用 bindService()
来创建服务(那 onStartCommand()
就不会被调用),则服务的生存期就与被绑定的组件一致。 一旦所有客户端都解除了绑定,系统就会销毁该服务。
只有当可用内存太少且必须为拥有用户焦点的 Activity 回收系统资源时,Android 系统才会强行终止一个服务。 如果服务由拥有用户焦点的 Activity 绑定着,它一般不会被杀死。 如果服务声明为前台运行(下文讨论),则它几乎永远不会被杀死。 除此之外,如果服务已启动并运行了很长时间,那系统将会随时间推移而降低它在后台任务列表中的地位, 此类服务很有可能就会被杀死——如果服务是 started 类型的,就必须经过精心设计,以便从容应对系统对其重启的情况。 如果系统杀死了某服务,只要资源再度够用,系统就会再次启动该服务(当然这还取决于 onStartCommand()
的返回值,下文将会述及)。关于系统何时可能销毁服务的详细信息,请参阅 进程和线程。
下一节将会介绍如何创建各种类型的服务,以及如何在应用程序组件中使用它们。
在 manifest 中声明服务
与 Activity (或其他组件)类似,必须在应用程序的 manifest 文件中声明所有的服务。
要声明自定义的服务,请把 <service>
元素作为子元素加入到 <application>
元素中即可。例如:
<manifest ... > ... <application ... > <serviceandroid:name=".ExampleService"/> ... </application></manifest>
在<service>
元素中可以包含很多其他的属性,比如定义启动服务所需权限、服务运行的进程之类的属性。 android:name
是唯一必需的属性——它定义了服务的类名。应用程序一经发布,就不得再修改这个类名。因为这么做可能会破坏某些显式引用该服务的 Intent 功能(参阅博客文章 Things That Cannot Change )。
为了确保应用程序的安全性,在启动或绑定服务时,请确保使用显式 Intent ,并且不要为服务声明 Intent 过滤器。 如果非要允许某些不确定的 Intent 来启动服务,可以为服务给定 Intent 过滤器,并省略 Intent
中的组件名称,但是必须用 setPackage()
设置 Intent 的包名称,以便能足够精确地指定所需的服务。
此外,还可以把 android:exported
属性设为"false"
,以保证该服务仅供应用程序内部使用。 这种方式可以有效避免其他应用程序启动该服务,用显式 Intent 也不行。
创建 started 类型的服务
started 类型的服务是指由其他组件通过调用 startService()
来启动的服务,这会引发对该服务 onStartCommand()
方法的调用。
一旦此类服务被启动,它就拥有了自己的生命周期,这是独立于启动它的组件的。 即使启动它的组件已被销毁,该服务仍然能够在后台一直运行下去。 因此,此类服务应该能够在任务完成后自行终止,通过调用 stopSelf()
即可,或者由其他组件通过调用 stopService()
也可以。
诸如 Activity 之类的应用程序组件,可以通过调用 startService()
来启动此类服务,并传入一个给出了服务及其所需数据的 Intent
对象。服务将在 onStartCommand()
方法中接收到该 Intent
对象。
举个例子,假定某个 Activity 需要把一些数据保存到在线数据库中。 此 Activity 可以启动一个伴随(companion)服务并通过传入 startService()
一个 Intent 把需要保存的数据发送给该服务。该服务在 onStartCommand()
内接收到 Intent,连接 Internet,并进行数据库事务处理。 当事务完成后,服务自行终止,并被系统销毁。
警告: 默认情况下,服务与声明它的应用程序运行于同一个进程中,且都在应用程序的主线程中。 因此,如果服务要执行密集计算或进行阻塞式的操作,同时用户又需要与同一个应用程序中的 Activity 进行交互,那么服务将会降低 Activity 的性能。 为了避免影响应用程序的性能,应该在服务中启动一个新的线程。
传统的做法是扩展两个类来创建 started 类型的服务:
Service
- 这是所有服务的基类。如果你要扩展该类,很重要的一点是:请在其中创建一个新的线程来完成所有的服务工作。 因为服务默认是使用应用程序的主线程的,这会降低应用程序中所有 Activity 的运行性能。
IntentService
- 这是
Service
类的子类,它使用了工作(worker)线程来处理所有的启动请求,每次请求都会启动一个线程。 如果服务不需要同时处理多个请求的话,这是最佳的选择。 要做的全部工作就是实现onHandleIntent()
即可,它会接收每个启动请求的 Intent,然后就能在后台完成任务了。
下一节描述了如何用这两个类来编写服务的代码。
扩展IntentService类
因为大多数 started 类型的服务都不需要同时处理多个请求(这实际上是一种危险的多线程应用场景),所以最佳方式也许就是用 IntentService
类来实现服务。
IntentService
将完成以下步骤:
- 创建一个默认的工作(worker)线程,它独立于应用程序主线程来受理所有发送到
onStartCommand()
的 Intent。 - 创建一个工作队列,用于每次向
onHandleIntent()
传入一个 Intent,这样就永远不必操心多线程的问题了。 - 在处理完所有的启动请求后,终止服务,因此就永远不必调用
stopSelf()
了。 - 提供默认的
onBind()
实现代码,它返回 null。 - 提供默认的
onStartCommand()
实现代码,它把 Intent 送入工作队列,稍后会再传给onHandleIntent()
。
上述步骤加在一起:所有要做的工作就是实现 onHandleIntent()
中的代码,来完成客户端提交的任务。(当然,还需要为服务给出一小段构造方法。)
以下是一个 IntentService
的代码示例:
1 publicclassHelloIntentServiceextendsIntentService{ 2 3 /** 4 * 构造方法是必需的,必须用工作线程名称作为参数 5 * 调用父类的IntentService(String)构造方法。 6 */ 7 publicHelloIntentService(){ 8 super("HelloIntentService"); 9 }10 11 /**12 * IntentService从缺省的工作线程中调用本方法,并用启动服务的intent作为参数。 13 * 本方法返回后,IntentService将适时终止这个服务。14 */15 @Override16 protectedvoid onHandleIntent(Intent intent){17 // 通常我们会在这里执行一些工作,比如下载文件。18 // 作为例子,我们只是睡5秒钟。19 long endTime =System.currentTimeMillis()+5*1000;20 while(System.currentTimeMillis()< endTime){21 synchronized(this){22 try{23 wait(endTime -System.currentTimeMillis());24 }catch(Exception e){25 }26 }27 }28 }29 }
要做的全部工作就是:一个构造方法和一个 onHandleIntent()
方法的代码。
如果还要重写其他的回调方法,比如 onCreate()
、 onStartCommand()
、 onDestroy()
, 请确保调用一下父类的实现代码,以便 IntentService
能正确处理工作线程的生命周期。
比如, onStartCommand()
必须返回默认代码的结果(默认代码实现了 Intent 将如何分发给 onHandleIntent()
):
1 @Override2 publicint onStartCommand(Intent intent,int flags,int startId){3 Toast.makeText(this,"service starting",Toast.LENGTH_SHORT).show();4 returnsuper.onStartCommand(intent,flags,startId);5 }
除了 onHandleIntent()
以外,唯一不需要调用父类代码的方法就是 onBind()
(不过,如果该服务允许被绑定,还是需要实现它的)。
在下一节中,将会介绍如何扩展 Service
基类来实现同一个服务,代码量会稍多一些,但也许更适合处理多个同时发起的启动请求。
扩展 Service 类
如上节所述,利用 IntentService
来实现一个 started 类型的服务是非常简单的。 不过,如果某个服务需要多线程运行(而不是通过一个工作队列来处理启动请求),那么可以扩展 Service
类来处理每个 Intent。
作为对照,以下例程实现了 Service
类,它完成的工作与上述使用 IntentService
的例子一样。 也就是说,所有启动请求都是用同一个工作线程来进行处理,且每次只处理一个请求。
1 publicclassHelloServiceextendsService{ 2 privateLooper mServiceLooper; 3 privateServiceHandler mServiceHandler; 4 5 // 处理从线程接收的消息 6 privatefinalclassServiceHandlerextendsHandler{ 7 publicServiceHandler(Looper looper){ 8 super(looper); 9 }10 @Override11 publicvoid handleMessage(Message msg){12 // 通常我们在这里执行一些工作,比如下载文件。13 // 作为例子,我们只是睡个5秒钟。14 long endTime =System.currentTimeMillis()+5*1000;15 while(System.currentTimeMillis()< endTime){16 synchronized(this){17 try{18 wait(endTime -System.currentTimeMillis());19 }catch(Exception e){20 }21 }22 }23 // 根据startId终止服务,这样我们就不会在处理其它工作的过程中再来终止服务24 stopSelf(msg.arg1);25 }26 }27 28 @Override29 publicvoid onCreate(){30 // 启动运行服务的线程。31 // 请记住我们要创建一个单独的线程,因为服务通常运行于进程的主线程中,可我们不想阻塞主线程。32 // 我们还要赋予它后台运行的优先级,以便计算密集的工作不会干扰我们的UI。33 HandlerThread thread =newHandlerThread("ServiceStartArguments",34 Process.THREAD_PRIORITY_BACKGROUND);35 thread.start();36 37 // 获得HandlerThread的Looper队列并用于Handler38 mServiceLooper = thread.getLooper();39 mServiceHandler =newServiceHandler(mServiceLooper);40 }41 42 @Override43 publicint onStartCommand(Intent intent,int flags,int startId){44 Toast.makeText(this,"service starting",Toast.LENGTH_SHORT).show();45 46 // 对于每一个启动请求,都发送一个消息来启动一个处理47 // 同时传入启动ID,以便任务完成后我们知道该终止哪一个请求。48 Message msg = mServiceHandler.obtainMessage();49 msg.arg1 = startId;50 mServiceHandler.sendMessage(msg);51 52 // 如果我们被杀死了,那从这里返回之后被重启53 return START_STICKY;54 }55 56 @Override57 publicIBinder onBind(Intent intent){58 // 我们不支持绑定,所以返回null59 returnnull;60 }61 62 @Override63 publicvoid onDestroy(){64 Toast.makeText(this,"service done",Toast.LENGTH_SHORT).show(); 65 }66 }
如你所见,这次要做的事情比用 IntentService
时多了很多。
不过,因为是自行处理每次的 onStartCommand()
调用,因此可以同时处理多个请求。 本例中没有这么去实现,但只要愿意,就可以为每个请求创建一个新的线程,并立即运行它们(而不是等待前一个请求处理完毕)。
请注意, onStartCommand()
方法必须返回一个整数。这个整数是描述系统在杀死服务之后应该如何继续下去的(上一节中默认的 IntentService
代码已经处理好这一点,不过尽可以自行修改)。 onStartCommand()
的返回值必须是以下常量之一:
START_NOT_STICKY
- 如果系统在
onStartCommand()
返回后杀死了服务,则不再重建服务,除非还存在未分发的 Intent 。 如果服务不是必需的,且应用程序能够轻易地重启那些未完成的工作,那么这是避免服务一直运行的最安全的选项。 START_STICKY
- 如果系统在
onStartCommand()
返回后杀死了服务,则将重建服务并调用onStartCommand()
,但不会再次送入前一个 Intent, 而是用 null Intent来调用onStartCommand()
。不过,如果还有启动服务的 Intent 未发送完,那么这些剩下的 Intent 会继续发送。 这适用于媒体播放器(或类似服务),它们不执行命令,但需要一直运行并随时待命。 START_REDELIVER_INTENT
- 如果系统在
onStartCommand()
返回后杀死了服务,则将重建服务并调用onStartCommand()
,且传入最后一个 Intent。 所有被挂起的 Intent 也都会按顺序分发。 这适用于那些需要立即恢复工作的活跃服务,比如下载文件。
关于上述返回值的详细信息,请参阅每个常量的参考文档链接
启动一个服务
从 Activity 或其他应用程序组件中可以启动一个服务,调用 startService()
并传入一个 Intent
(指定所需启动的服务)即可。 Android 系统将调用服务的 onStartCommand()
方法,并传入该 Intent
(请勿直接调用 onStartCommand()
。)
例如, Activity 可以用一个显式的 Intent 通过 startService()
启动上一节中所示的服务(HelloSevice
):
1 Intent intent =newIntent(this,HelloService.class);2 startService(intent);
startService()
方法会立即返回,Android 系统会去调用服务的 onStartCommand()
方法。如果服务还未运行,系统会首先调用 onCreate()
,然后再去调用 onStartCommand()
。
如果服务不同时支持绑定,那么通过 startService()
传入的 Intent 将是应用程序组件与服务进行交互的唯一途径。 当然,如果你期望服务能返回结果,那启动服务的客户端可以创建一个 PendingIntent
来获得一个广播(Broadcast)(利用 getBroadcast()
),并把它放入启动服务的 Intent
中分发给服务。这样,服务就能用这个广播来传递结果了。
多个启动服务的请求将会引发 onStartCommand()
方法的多次调用。不过,终止服务的请求(用 stopSelf()
或 stopService()
)只会被接受并执行一次。
终止一个服务
一个 started 类型的服务一个 started 类型的服务必须管理好自己的生命周期。也就是说,除非必须恢复系统内存,系统不会终止或销毁这类服务,服务在 onStartCommand()
返回后将一直运行下去。 因此,服务必须调用 stopSelf()
自行终止,或者由其他组件调用 stopService()
来终止它。
调用 stopSelf()
或 stopService()
的终止请求一旦发出,系统就会尽快销毁服务。
不过,如果服务同时要处理多个 onStartCommand()
请求,则在处理启动请求的过程中,就不应该去终止服务,因为接下来还可能会接收到新的启动请求(在第一个请求之后终止服务将停止第二个请求的处理)。 为了避免这个问题,可以用 stopSelf(int)
来确保终止服务的请求总是根据最近一次的启动请求来完成。 也就是说,在调用 stopSelf(int)
时,把启动请求ID(传给 onStartCommand()
的 startId
)传给了对应的终止请求。这样,即便服务在能够调用 stopSelf(int)
之前接收到了新的启动请求,ID 也将会不一样,服务将不会被终止。
警告: 为了避免系统资源的浪费,并节省电池的电量,在服务完成工作后,应用程序应该及时终止它,这一点非常重要。在必要时,其他组件可以通过调用 stopService()
来终止服务。即使服务允许被绑定,也必须保证:只要服务收到过 onStartCommand()
调用,就能被终止。
关于服务生命周期的详细信息,请参阅下文的 对服务的生命周期进行管理 章节。
创建 bound 类型的服务
bound 类型的服务是指允许应用程序组件调用 bindService()
进行绑定的服务,用于创建一个长连接(并且一般不再允许组件通过调用 startService()
来启动 服务)。
当应用程序中的 Activity 或其他组件需要与服务进行交互,或者应用程序中的某些功能需要暴露给其他应用程序使用时,就应该创建一个 bound 类型的服务,并通过进程间通信(IPC)来完成。
要创建一个 bound 类型的服务,必须实现 onBind()
回调方法,并返回一个 IBinder
对象,该对象定义了与服务进行通信的接口。 然后,其他应用程序组件就可以调用 bindService()
来获得接口并调用服务中的方法。 这类服务只在为绑定的应用程序组件工作时才会存活,因此,只要没有组件绑定到服务,系统就会自动销毁服务(不需要像用 onStartCommand()
启动的 started 类型服务那样来终止一个 bound 类型的服务)。
要创建一个 bound 类型的服务,首先必须定义接口,用于定义客户端与服务之间的通信方式。 这个客户端与服务之间的接口必须是一个 IBinder
对象的实现,并且服务必须在 onBind()
回调方法中返回这个对象。一旦客户端接收到这个 IBinder
,它就可以通过这个接口来与服务进行交互。
同一个服务可以被多个客户端绑定。当客户端完成交互时,会调用 unbindService()
来解除绑定。一旦不存在与服务绑定的客户端时,系统就会销毁该服务。
实现 bound 类型服务的方式有很多种,实现的过程也比 started 类型的服务更为复杂,因此 bound 类型的服务将在单独的 bound 服务 文档中讨论。
向用户发送通知
一旦开始运行,服务就能够通过 Toast 通知 或状态栏通知 把向用户发送事件通知。
Toast 通知是一种显示在当前窗口之上的消息框,显示一会儿之后它会自行消失。 而状态栏通知则是在状态栏上显示一个附带消息的图标,用户可以选中它来执行一个动作(Action)(比如启动一个 Activity)。
通常,当某些后台工作已经完成后(比如文件下载完毕),状态栏通知是最好的通知手段,用户这时可以在其上直接进行操作。 当用户在展开的通知栏上选中某条通知后,可以启动一个 Activity (比如查看已下载的文件)。
详情请参阅 Toast 通知 或 状态栏通知 开发指南。
在前台运行服务
前台服务是指那些很容易被用户觉察到异常的服务,因此内存不足时它也不会成为被杀的对象。 前台服务必须提供一个状态栏通知,并归于“正在运行的”(“Ongoing”)栏目之下。这意味着只有在服务被终止或移出前台之后,此通知会消失。
例如,通过服务来播放音乐的播放器就应该运行在前台,因为用户显然会觉察到它的运行情况。 状态栏通知可能会标明当前播放的歌曲,并允许用户启动一个 Activity 来与播放器进行交互。
要把一个服务请求为前台运行,可以调用 startForeground()
方法。该方法有两个参数:唯一标识通知的整数值和状态栏通知 Notification
对象。例如:
1 Notification notification =newNotification(R.drawable.icon, getText(R.string.ticker_text),2 System.currentTimeMillis());3 Intent notificationIntent =newIntent(this,ExampleActivity.class);4 PendingIntent pendingIntent =PendingIntent.getActivity(this,0, notificationIntent,0);5 notification.setLatestEventInfo(this, getText(R.string.notification_title),6 getText(R.string.notification_message), pendingIntent);7 startForeground(ONGOING_NOTIFICATION, notification);
警告: startForeground()
中的整数值 ID 不能为 0 。
要从前台移除服务,请调用 stopForeground()
方法,调用参数为布尔值,表示是否同时移除状态栏通知。 该方法不会终止服务。 不过,假如正在前台运行的服务被终止了,那其状态栏通知也会被移除。
有关状态栏通知的详细信息,请参阅 创建状态栏通知 。
对服务的生命周期进行管理
服务的生命周期与 Activity 的非常类似。 不过,因为服务可以在后台运行,用户不会注意到它,所以更需要重点关注服务的创建和销毁环节。
服务的生命周期——从创建到销毁——可以有两种路线:
- started 类型的服务
这类服务由其他组件调用
startService()
来创建。然后它保持运行,必须通过调用stopSelf()
自行终止。其他组件也可通过调用stopService()
终止这类服务。 服务终止后,系统会把它销毁。 - bound 类型的服务
服务由其他组件(客户端)调用
bindService()
来创建。然后客户端通过一个IBinder
接口与服务进行通信。客户端可以通过调用unbindService()
来关闭联接。 多个客户端可以绑定到同一个服务上,当所有的客户端都解除绑定后,系统会销毁服务。(服务不需要自行终止。)
这两种路线并不是完全独立的。 也就是说,可以绑定到一个已经用 startService()
启动的服务上。例如,一个后台音乐服务可以通过调用 startService()
来启动,传入一个标识了所需播放音乐的 Intent
。 之后,也许用户需要对播放器进行一些控制,或者需要查看当前歌曲的信息,这时某个 Activity 可以通过调用 bindService()
与此服务绑定。在类似这种情况下, stopService()
或 stopSelf()
不会真的终止服务,而是要等到所有的客户端都解除了绑定才会终止。
实现生命周期回调方法
与 Activity 类似,服务也有一些生命周期回调方法,通过实现这些方法可以监控服务的状态变化,并在适当的时机执行一些操作。 以下代码段展示了服务的全部生命周期回调方法:
1 public class ExampleService extends Service { 2 int mStartMode; // 标明服务被杀死后的处理方式 3 IBinder mBinder; // 用于客户端绑定的接口 4 boolean mAllowRebind; // 标明 onRebind 是否可用 5 6 @Override 7 public void onCreate() { 8 // 服务即将被创建 9 }10 @Override11 public int onStartCommand(Intent intent, int flags, int startId) {12 // 服务即将被启动,由startService() 调用触发13 return mStartMode;14 }15 @Override16 public IBinder onBind(Intent intent) {17 // 客户端即将与服务绑定,通过bindService()调用触发18 return mBinder;19 }20 @Override21 public boolean onUnbind(Intent intent) {22 // 所有客户端都已通过unbindService()解除了绑定23 return mAllowRebind;24 }25 @Override26 public void onRebind(Intent intent) {27 // 在已经发生过 onUnbind() 调用之后,28 // 客户端又要与服务绑定,通过 bindService()触发。29 }30 @Override31 public void onDestroy() {32 // 服务不再有用,即将被销毁。33 }34 }
注意: 与 Activity 的生命周期回调方法不同,这里不一定非要再调用一遍父类的回调方法。
通过实现以下方法,即可监控服务中两个嵌套的生命周期循环
- 服务的完整生存期(entire lifetime)开始于
onCreate()
调用,结束于onDestroy()
返回时。与 Activity 类似,服务在onCreate()
中完成初始设置工作,并在onDestroy()
中释放所有已占用的资源。例如,一个音乐播放服务可以在onCreate()
中创建音乐播放线程,而在onDestroy()
中终止该线程。不管是用
startService()
还是bindService()
创建的,所有的服务都会调用onCreate()
和onDestroy()
方法。 - 服务的活跃生存期(active lifetime)开始于
onStartCommand()
或onBind()
的调用。这两个方法分别会带入传给startService()
或bindService()
的Intent
。如果服务是 started 类型的,则活跃生存期与完整生存期同时结束(在
onStartCommand()
返回后,服务仍然处于活跃期。)如果服务是 bound 类型的,则活跃生存期在onUnbind()
返回后结束。
注意: 虽然 started 类型的服务是用 stopSelf()
或 stopService()
调用来终止的,但是服务却没有相应的回调方法(不存在onStop()
回调方法)。因此,除非服务已经与客户端进行了绑定,否则系统就会在服务终止时销毁它—— 这时只会收到 onDestroy()
回调方法。
图 2 标明了服务中典型的回调方法。尽管此图把 startService()
创建的服务和 bindService()
创建的分开展示了,但请记住,无论启动的方式如何,所有的服务都可能允许被客户端绑定。 因此,一开始用 onStartCommand()
启动的服务(客户端调用 startService()
)仍然可以收到 onBind()
调用(当客户端调用了 bindService()
时)。
关于创建可绑定服务的详情,请参阅 bound 类型的服务 文档,在其中的 管理 bound 类型服务的生命周期 章节中包含了有关 onRebind()
回调方法的详细信息。
Android开发-API指南-服务