Android-App Components之service

本知识点只是个人见解,具体知识及使用请查阅官网,以免被误导,同时大家可以对此文发表自己的见解。App Components即android的组件,按照官网上面的API Guides来看,基础组件包括:

Intents and Intent Filters

Activities

Services

Content Providers

App Widgets

Processes and Threads

以上组件,组成了android的基础组件,了解可以完善我们的android知识体系

此处的知识点包含了平常使用service容易忽略的部分,如若有误请及时告知。此处只包含services的知识点,后续会更新。


Service基础知识点
管理Service生命周期
服务生命周期(从创建到销毁)可以遵循两条不同的路径:
  • 启动服务
该服务在其他组件调用  startService() 时创建,然后无限期运行,且必须通过调用  stopSelf() 来自行停止运行。此外,其他组件也可以通过调用 stopService() 来停止服务。服务停止后,系统会将其销毁。
  • 绑定服务
该服务在另一个组件(客户端)调用  bindService() 时创建。然后,客户端通过  IBinder 接口与服务进行通信。客户端可以通过调用 unbindService() 关闭连接。多个客户端可以绑定到相同服务,而且当所有绑定全部取消后,系统即会销毁该服务。 (服务不必自行停止运行。)
这两条路径并非完全独立。也就是说,您可以绑定到已经使用  startService() 启动的服务。例如,可以通过使用  Intent(标识要播放的音乐)调用  startService() 来启动后台音乐服务。随后,可能在用户需要稍加控制播放器或获取有关当前播放歌曲的信息时,Activity 可以通过调用  bindService()绑定到服务。在这种情况下,除非所有客户端均取消绑定,否则  stopService() 或  stopSelf() 不会实际停止服务。

回调方法
重写的回调方法,以处理服务生命周期的某些关键方面并提供一种机制将组件绑定到服务(如适用)。 应重写的最重要的回调方法包括:
当另一个组件(如 Activity)通过调用  startService() 请求启动服务时,系统将调用此方法。一旦执行此方法,服务即会启动并可在后台无限期运行。 如果您实现此方法,则在服务工作完成后,需要由您通过调用  stopSelf() 或  stopService() 来停止服务。(如果您只想提供绑定,则无需实现此方法。)
当另一个组件想通过调用  bindService() 与服务绑定(例如执行 RPC)时,系统将调用此方法。在此方法的实现中,您必须通过返回  IBinder 提供一个接口,供客户端用来与服务进行通信。请务必实现此方法,但如果您并不希望允许绑定,则应返回 null。
首次创建服务时,系统将调用此方法来执行一次性设置程序(在调用  onStartCommand() 或  onBind() 之前)。如果服务已在运行,则不会调用此方法。
当服务不再使用且将被销毁时,系统将调用此方法。服务应该实现此方法来清理所有资源,如线程、注册的侦听器、接收器等。 这是服务接收的最后一个调用。
如果组件通过 调用  startService()  启动服务(这会导致对  onStartCommand() 的调用),则服务将一直运行,直到服务使用  stopSelf() 自行停止运行,或由其他组件通过调用  stopService() 停止它为止。
如果组件是通过 调用  bindService()  来创建服务(且未调用  onStartCommand(),则服务只会在该组件与其绑定时运行。一旦该服务与所有客户端之间的绑定全部取消,系统便会销毁它。
仅当内存过低且必须回收系统资源以供具有用户焦点的 Activity 使用时,Android 系统才会强制停止服务。
如果将服务声明为 在前台运行(稍后讨论),则它几乎永远不会终止。
如果服务是启动服务,则您必须将其设计为能够妥善处理系统对它的重启。 如果系统终止服务,那么一旦资源变得再次可用,系统便会重启服务(不过这还取决于从  onStartCommand() 返回的值,本文稍后会对此加以讨论)。

创建启动服务
服务启动之后,其生命周期即独立于启动它的组件,并且可以在后台无限期地运行,即使启动服务的组件已被销毁也不受影响。 因此,服务应通过调用 stopSelf() 结束工作来自行停止运行,或者由另一个组件通过调用  stopService() 来停止它。
可以拓展以下两个类来创建服务:
这是适用于所有服务的基类。扩展此类时,必须创建一个用于执行所有服务工作的新线程,因为默认情况下,服务将使用应用的主线程,这会降低应用正在运行的所有 Activity 的性能。
这是  Service 的子类,它使用工作线程逐一处理所有启动请求。如果您不要求服务同时处理多个请求,这是最好的选择。 您只需实现  onHandleIntent() 方法即可,该方法会接收每个启动请求的 Intent,使您能够执行后台工作。

注意: onStartCommand() 方法必须返回整型数。整型数是一个值,用于描述系统应该如何在服务终止的情况下继续运行服务(如上所述, IntentService 的默认实现将为您处理这种情况,不过您可以对其进行修改)。从  onStartCommand() 返回的值必须是以下常量之一:
如果系统在  onStartCommand() 返回后终止服务,则除非有挂起 Intent 要传递,否则系统不会重建服务。这是最安全的选项,可以避免在不必要时以及应用能够轻松重启所有未完成的作业时运行服务。
如果系统在  onStartCommand() 返回后终止服务,则会重建服务并调用  onStartCommand(),但不会重新传递最后一个 Intent。相反,除非有挂起 Intent 要启动服务(在这种情况下,将传递这些 Intent ),否则系统会通过空 Intent 调用  onStartCommand()。这适用于不执行命令、但无限期运行并等待作业的媒体播放器(或类似服务)。
如果系统在  onStartCommand() 返回后终止服务,则会重建服务,并通过传递给服务的最后一个 Intent 调用  onStartCommand()。任何挂起 Intent 均依次传递。这适用于主动执行应该立即恢复的作业(例如下载文件)的服务。

停止服务
如果服务同时处理多个  onStartCommand() 请求,则您不应在处理完一个启动请求之后停止服务,因为您可能已经收到了新的启动请求(在第一个请求结束时停止服务会终止第二个请求)。为了避免这一问题,您可以使用  stopSelf(int) 确保服务停止请求始终基于最近的启动请求。也就说,在调用 stopSelf(int) 时,传递与停止请求的 ID 对应的启动请求的 ID(传递给  onStartCommand() 的 startId)。然后,如果在您能够调用  stopSelf(int) 之前服务收到了新的启动请求,ID 就不匹配,服务也就不会停止。

向用户发送通知
一旦运行起来,服务即可使用  Toast 通知状态栏通知来通知用户所发生的事件。

在前台运行服务
 前台服务必须为状态栏提供通知,这意味着除非服务停止或从前台移除,否则不能清除通知。

要请求让服务运行于前台,请调用  startForeground()。此方法采用两个参数:唯一标识通知的整型数和状态栏的  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.setLatestEventInfo(this, getText(R.string.notification_title),
        getText(R.string.notification_message), pendingIntent);
startForeground(ONGOING_NOTIFICATION_ID, notification);
注意:提供给  startForeground() 的整型 ID 不得为 0。
要从前台移除服务,请调用  stopForeground()。此方法采用一个布尔值,指示是否也移除状态栏通知。 此方法不会停止服务。 但是,如果您在服务正在前台运行时将其停止,则通知也会被移除。


实现生命周期回调
以下是生命周期的回调图

图 2. 服务生命周期。左图显示了使用  startService() 所创建的服务的生命周期,右图显示了使用  bindService() 所创建的服务的生命周期。
通过实现这些方法,您可以监控服务生命周期的两个嵌套循环:
  • 服务的整个生命周期从调用 onCreate() 开始起,到 onDestroy() 返回时结束。与 Activity 类似,服务也在 onCreate() 中完成初始设置,并在 onDestroy() 中释放所有剩余资源。例如,音乐播放服务可以在 onCreate() 中创建用于播放音乐的线程,然后在 onDestroy() 中停止该线程。
无论服务是通过  startService() 还是  bindService() 创建,都会为所有服务调用  onCreate() 和  onDestroy() 方法。
对于启动服务,有效生命周期与整个生命周期同时结束(即便是在  onStartCommand() 返回之后,服务仍然处于活动状态)。对于绑定服务,有效生命周期在  onUnbind() 返回时结束。

注:尽管启动服务是通过调用  stopSelf() 或  stopService() 来停止,但是该服务并无相应的回调(没有 onStop() 回调)。因此,除非服务绑定到客户端,否则在服务停止时,系统会将其销毁 —  onDestroy() 是接收到的唯一回调。

图 2 说明了服务的典型回调方法。尽管该图分开介绍通过  startService() 创建的服务和通过  bindService() 创建的服务,但是请记住,不管启动方式如何,任何服务均有可能允许客户端与其绑定。因此,最初使用  onStartCommand()(通过客户端调用  startService())启动的服务仍可接收对  onBind()的调用(当客户端调用  bindService() 时)。

绑定service

基础知识
绑定服务是  Service  类的实现,可让其他应用与其绑定和交互。要提供服务绑定,您必须实现  onBind() 回调方法。该方法返回的  IBinder  对象定义了客户端用来与服务进行交互的编程接口。

绑定到已启动服务
多个客户端可同时连接到一个服务。不过,只有在第一个客户端绑定时,系统才会调用服务的  onBind() 方法来检索  IBinder 。系统随后无需再次调用  onBind() ,便可将同一  IBinder  传递至任何其他绑定的客户端。
当最后一个客户端取消与服务的绑定时,系统会将服务销毁(除非  startService()  也启动了该服务)。
当您实现绑定服务时,最重要的环节是定义您的  onBind()  回调方法返回的接口。您可以通过几种不同的方法定义服务的  IBinder  接口,下文对这些方法逐一做了阐述。

可以创建同时具有已启动和绑定两种状态的服务。 也就是说,可通过调用  startService() 启动该服务,让服务无限期运行;此外,还可通过调用  bindService()  使客户端绑定到服务。
如果您确实允许服务同时具有已启动和绑定状态,则服务启动后,系统“不会”在所有客户端都取消绑定时销毁服务。 为此,您必须通过调用 stopSelf()  或  stopService()  显式停止服务。
尽管您通常应该实现  onBind()   onStartCommand() ,但有时需要同时实现这两者。例如,音乐播放器可能发现让其服务无限期运行并同时提供绑定很有用处。 这样一来,Activity 便可启动服务进行音乐播放,即使用户离开应用,音乐播放也不会停止。 然后,当用户返回应用时,Activity 可绑定到服务,重新获得回放控制权。
请务必阅读 管理绑定服务的生命周期 部分,详细了解有关为已启动服务添加绑定时该服务的生命周期信息。
客户端可通过调用  bindService()  绑定到服务。调用时,它必须提供  ServiceConnection  的实现,后者会监控与服务的连接。调用  onServiceConnected() ,向客户端传递用来与服务通信的 IBinder

创建绑定服务
创建提供绑定的服务时,您必须提供  IBinder ,用以提供客户端用来与服务进行交互的编程接口。 您可以通过三种方法定义接口:
如果服务是供您的自有应用专用,并且在与客户端相同的进程中运行(常见情况),则应通过扩展 Binder  类并从  onBind()  返回它的一个实例来创建接口。客户端收到  Binder  后,可利用它直接访问  Binder  实现中乃至  Service  中可用的公共方法。
如果服务只是您的自有应用的后台工作线程,则优先采用这种方法。 不以这种方式创建接口的唯一原因是,您的服务被其他应用或不同的进程占用。
如需让接口跨不同的进程工作,则可使用  Messenger  为服务创建接口。服务可以这种方式定义对应于不同类型  Message  对象的  Handler 。此  Handler  是  Messenger  的基础,后者随后可与客户端分享一个  IBinder ,从而让客户端能利用  Message  对象向服务发送命令。此外,客户端还可定义自有 Messenger ,以便服务回传消息。
这是执行进程间通信 (IPC) 的最简单方法,因为  Messenger  会在单一线程中创建包含所有请求的队列,这样您就不必对服务进行线程安全设计。
如需查看如何提供双向消息传递的示例,请参阅  MessengerService.java (服务)和  MessengerServiceActivities.java (客户端)示例。
使用 AIDL
AIDL(Android 接口定义语言)执行所有将对象分解成原语的工作,操作系统可以识别这些原语并将它们编组到各进程中,以执行 IPC。 之前采用  Messenger  的方法实际上是以 AIDL 作为其底层结构。 如上所述, Messenger  会在单一线程中创建包含所有客户端请求的队列,以便服务一次接收一个请求。 不过,如果您想让服务同时处理多个请求,则可直接使用 AIDL。 在此情况下,您的服务必须具备多线程处理能力,并采用线程安全式设计。
如需直接使用 AIDL,您必须创建一个定义编程接口的  .aidl  文件。Android SDK 工具利用该文件生成一个实现接口并处理 IPC 的抽象类,您随后可在服务内对其进行扩展。
:如果您确定自己需要直接使用 AIDL,请参阅  AIDL  文档。

关于AIDL的理解和使用见这篇博客 https://blog.csdn.net/yang1349day/article/details/79931071


绑定到服务
有关绑定到服务的重要说明:
  • 您应该始终捕获 DeadObjectException 异常,它们是在连接中断时引发的。这是远程方法引发的唯一异常。有关绑定到服务的重要说明:
  • 如果您只需要在 Activity 可见时与服务交互,则应在 onStart() 期间绑定,在 onStop() 期间取消绑定。
  • 如果您希望 Activity 在后台停止运行状态下仍可接收响应,则可在 onCreate() 期间绑定,在 onDestroy() 期间取消绑定。请注意,这意味着您的 Activity 在其整个运行过程中(甚至包括后台运行期间)都需要使用服务,因此如果服务位于其他进程内,那么当您提高该进程的权重时,系统终止该进程的可能性会增加。

:通常情况下, 切勿 在 Activity 的  onResume()  和  onPause()  期间绑定和取消绑定,因为每一次生命周期转换都会发生这些回调,您应该使发生在这些转换期间的处理保持在最低水平。此外,如果您的应用内的多个 Activity 绑定到同一服务,并且其中两个 Activity 之间发生了转换,则如果当前 Activity 在下一个 Activity 绑定(恢复期间)之前取消绑定(暂停期间),系统可能会销毁服务并重建服务。 ( Activity 文档中介绍了这种有关 Activity 如何协调其生命周期的 Activity 转换。)



管理绑定服务的生命周期

当服务与所有客户端之间的绑定全部取消时,Android 系统便会销毁服务(除非还使用  onStartCommand()  启动了该服务)。因此,如果您的服务是纯粹的绑定服务,则无需对其生命周期进行管理 — Android 系统会根据它是否绑定到任何客户端代您管理。
不过,如果您选择实现  onStartCommand()  回调方法,则您必须显式停止服务,因为系统现在已将服务视为 已启动 。在此情况下,服务将一直运行到其通过  stopSelf()  自行停止,或其他组件调用  stopService()  为止,无论其是否绑定到任何客户端。
此外,如果您的服务已启动并接受绑定,则当系统调用您的  onUnbind()  方法时,如果您想在客户端下一次绑定到服务时接收  onRebind()  调用,则可选择返回  true onRebind()  返回空值,但客户端仍在其  onServiceConnected()  回调中接收  IBinder 。下文图 1 说明了这种生命周期的逻辑。

图 1.  允许绑定的已启动服务的生命周期。
如需了解有关已启动服务生命周期的详细信息,请参阅 服务 文档。

下一篇: Android-App Components之BroadcastReceive

本文参考自:

Android Developers ( http://android.xsoftlab.net/) 英文版(比较全)


猜你喜欢

转载自blog.csdn.net/yang1349day/article/details/80105846