安卓服务概览

服务概览

Service是一种可在后台执行长时间运行操作而不提供界面的应用组件。服务可由其他应用组件启动,而且即使用户切换到其他应用,服务仍将在后台继续运行。

此外,组件可以通过绑定到服务与之进行交互,甚至是执行进程间通信IPC。例如,服务可在后台处理网络事务、播放音乐,执行文件I/O或与ContentProvider进行交互。

以下是三种不同的服务类型:

  • 前台

    前台服务执行一些用户能感知的操作。

    例如,音频应用会使用前台服务来播放音频曲目。前台服务必须显示通知。即使用户停止与应用的交互,前台服务仍会继续运行。

  • 后台

    后台服务执行用户无感知的操作。

    例如,如果应用使用某个服务来压缩其存储空间,则此服务通常是后台服务。

    注意:如果您的应用面向 API 26 或更高版本,当应用本身未在前台运行时,系统会对运行后台服务施加限制。在诸如此类的大多数情况下,您的应用应改为使用计划作业。

  • 绑定

    应用组件通过调用bindService()绑定到服务时,服务即处于绑定状态。绑定服务会提供客户端-服务器接口,以便组件与服务进行交互、发送请求、接收结果,甚至是利用进程间通信IPC跨进程执行这些操作。

    仅当与另一个应用组件绑定时,绑定服务才会运行。<u>多个组件可以同时绑定到该服务,但全部取消绑定后,该服务才会被销毁</u>。

虽然分开概括讨论启动和绑定服务,但服务是可同时以这两种方式运行的,换句话说,它即可以是启动服务(无限期运行),亦支持绑定。唯一的问题在于是否实现一组回调方法:onStartCommand()(让组件启动服务)和onBind()(实现服务绑定)

无论服务是牌启动状态还是绑定状态(或者同时处理这两种状态),任何应用组件均可像使用Activity那样,通过调用Intent来使用服务(即此服务来自另一应用)。<u>不过可以通过AndroidManifest.xml将服务声明为私有的,并阻止其他应用访问</u>


<manifest ... >

  ...

  <application ... >

      <service android:name=".ExampleService" />

      ...

  </application>

</manifest>

在服务和线程之间进行选择

服务是一种即使用户未与应用交互也可以后台运行的组件,因此,只有在需要服务时才应创建服务。

<font color=BrulyWood>如果必须在主线程之外执行操作,但只在用户与应用有交互时执行相应的操作,那么应用创建线程。</font>

<u>例如,如果只是想在Activity运行的同时播放一些音乐,则可以onCreate()中创建线程,在onStart()中启动线程,然后在onStop()中停止线程。或者还可以考虑使用AsyncTask或者HandlerThread,而非传统的Thread类。如需了解有关线程的详细信息,请参阅进程和线程文档</u>

如果确实要使用服务,默认情况下,它仍会在应用的主线程中运行,因此,如果服务执行的是密集或者阻塞性操作,则仍应在服务内创建新线程。

基础知识

如要创建服务,您必须创建Service 的子类(或使用它的一个现有子类)。在实现中,您必须重写一些回调方法,从而处理服务生命周期的某些关键方面,并提供一种机制将组件绑定到服务(如适用)。以下是您应重写的最重要的回调方法:

  • onStartCommand()

    当另一个组件(如 Activity)请求启动服务时,系统会通过调用 startService() 来调用此方法。执行此方法时,服务即会启动并可在后台无限期运行。如果您实现此方法,则在服务工作完成后,您需负责通过调用 stopSelf() 或 stopService() 来停止服务。(如果您只想提供绑定,则无需实现此方法。)

    如果组件通过调用 startService() 启动服务(这会引起对 onStartCommand() 的调用),则服务会一直运行,直到其使用 stopSelf() 自行停止运行,或由其他组件通过调用 stopService() 将其停止为止。

  • onBind()

    当另一个组件想要与服务绑定(例如执行 RPC)时,系统会通过调用bindService() 来调用此方法。在此方法的实现中,您必须通过返回 IBinder 提供一个接口,以供客户端用来与服务进行通信。请务必实现此方法;但是,如果您并不希望允许绑定,则应返回 null。

    如果组件通过调用 bindService() 来创建服务,且未调用 onStartCommand(),则服务只会在该组件与其绑定时运行。当该服务与其所有组件取消绑定后,系统便会将其销毁。

  • onCreate()

    首次创建服务时,系统会(在调用 onStartCommand()onBind() 之前)调用此方法来执行一次性设置程序。如果服务已在运行,则不会调用此方法。

  • onDestroy()

    当不再使用服务且准备将其销毁时,系统会调用此方法。服务应通过实现此方法来清理任何资源,如线程、注册的侦听器、接收器等。这是服务接收的最后一个调用。

只有在内存过低且必须回收系统资源以供拥有用户焦点的 Activity 使用时,Android 系统才会停止服务。如果将服务绑定到拥有用户焦点的 Activity,则它其不太可能会终止;如果将服务声明为在前台运行,则其几乎永远不会终止。如果服务已启动并长时间运行,则系统逐渐降低其在后台任务列表中的位置,而服务被终止的概率也会大幅提升—如果服务是启动服务,则您必须将其设计为能够妥善处理系统执行的重启。如果系统终止服务,则其会在资源可用时立即重启服务,但这还取决于您从 onStartCommand() 返回的值。如需了解有关系统会在何时销毁服务的详细信息,请参阅进程和线程文档。

下文将介绍如何创建 startService() 和 bindService() 服务方法,以及如何通过其他应用组件使用这些方法。

使用清单文件声明服务

如同对 Activity 及其他组件的操作一样,您必须在应用的清单文件中声明所有服务。

如要声明服务,请添加 <service> 元素作为 <application> 元素的子元素。下面是示例:


<manifest ... >

  ...

  <application ... >

      <service android:name=".ExampleService" />

      ...

  </application>

</manifest>

您还可在 <service> 元素中加入其他属性,以定义一些特性,如启动服务及其运行时所在进程需要的权限。android:name 属性是唯一必需的属性,用于指定服务的类名。发布应用后,请保此类名不变,以避免因依赖显式 Intent 来启动或绑定服务而破坏代码的风险(请阅读博文 Things That Cannot Change [不能更改的内容])。

注意:为确保应用的安全性,在启动 Service 时,请始终使用显式 Intent,且不要为服务声明 Intent 过滤器。使用隐式 Intent 启动服务存在安全隐患,因为您无法确定哪些服务会响应 Intent,而用户也无法看到哪些服务已启动。从 Android 5.0(API 级别 21)开始,如果使用隐式 Intent 调用 bindService(),则系统会抛出异常。

您可以通过添加 android:exported 属性并将其设置为 false,确保服务仅适用于您的应用。这可以有效阻止其他应用启动您的服务,即便在使用显式 Intent 时也如此。

<u>注意:用户可以查看其设备上正在运行的服务。如果他们发现自己无法识别或信任的服务,则可以停止该服务。为避免用户意外停止您的服务,您需要在应用清单的 <service> 元素中添加 android:description。请在描述中用一个短句解释服务的作用及其提供的好处。</u>

创建启动服务

启动服务由另一个组件通过调用 startService() 启动,这会导致调用服务的 onStartCommand() 方法。

服务启动后,其生命周期即独立于启动它的组件。即使系统已销毁启动服务的组件,该服务仍可在后台无限期地运行。因此,服务应在其工作完成时通过调用 stopSelf() 来自行停止运行,或者由另一个组件通过调用 stopService() 来将其停止。

应用组件(如 Activity)可通过调用 startService() 方法并传递 Intent 对象(指定服务并包含待使用服务的所有数据)来启动服务。服务会在 onStartCommand() 方法接收此 Intent。

<u>例如,假设某 Activity 需要将一些数据保存到在线数据库中。该 Activity 可以启动一个协同服务,并通过向 startService() 传递一个 Intent,为该服务提供要保存的数据。服务会通过 onStartCommand() 接收 Intent,连接到互联网并执行数据库事务。事务完成后,服务将自行停止并销毁。</u>

注意:默认情况下,服务与服务声明所在的应用运行于同一进程,并且运行于该应用的主线程中。如果服务在用户与来自同一应用的 Activity 进行交互时执行密集型或阻止性操作,则会降低 Activity 性能。为避免影响应用性能,请在服务内启动新线程。

通常,您可以扩展两个类来创建启动服务:

  • Service

    这是适用于所有服务的基类。扩展此类时,您必须创建用于执行所有服务工作的新线程,因为服务默认使用应用的主线程,这会降低应用正在运行的任何 Activity 的性能。

  • IntentService

    这是 Service 的子类,其使用工作线程逐一处理所有启动请求。如果您不要求服务同时处理多个请求,此类为最佳选择。实现 onHandleIntent(),该方法会接收每个启动请求的 Intent,以便您执行后台工作。

扩展IntentService类

由于大多数启动服务无需同时处理多个请求(实际上,这种多线程情况可能很危险 - ???),因此最佳选择是利用 IntentService 类实现服务。

IntentService 类会执行以下操作:

  • 创建默认的工作线程,用于在应用的主线程外执行传递给 onStartCommand() 的所有 Intent。

  • 创建工作队列,用于将 Intent 逐一传递给 onHandleIntent() 实现,这样您就永远不必担心多线程问题。

  • 在处理完所有启动请求后停止服务,因此您永远不必调用 stopSelf()。

  • 提供 onBind() 的默认实现(返回 null)。

  • 提供 onStartCommand() 的默认实现,可将 Intent 依次发送到工作队列和 onHandleIntent() 实现。


private final class ServiceHandler extends Handler {

        public ServiceHandler(Looper looper) {

            super(looper);

        }

        @Override

        public void handleMessage(Message msg) {

            onHandleIntent((Intent)msg.obj);

            // 执行完成后,关闭当前服务

            stopSelf(msg.arg1);

        }

    }

@Override

public void onStart(@Nullable Intent intent, int startId) {

    Message msg = mServiceHandler.obtainMessage();

    msg.arg1 = startId;

    msg.obj = intent;

    mServiceHandler.sendMessage(msg);

}

/**

* You should not override this method for your IntentService. Instead,

* override {@link #onHandleIntent}, which the system calls when the IntentService

* receives a start request.

* @see android.app.Service#onStartCommand

*/

@Override

public int onStartCommand(@Nullable Intent intent, int flags, int startId) {

    onStart(intent, startId);

    return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;

}

/**

* Unless you provide binding for your service, you don't need to implement this

* method, because the default implementation returns null.

* @see android.app.Service#onBind

*/

@Override

@Nullable

public IBinder onBind(Intent intent) {

    return null;

}

@WorkerThread

protected abstract void onHandleIntent(@Nullable Intent intent);

如要完成客户端提供的工作,请实现 onHandleIntent()。不过,您还需为服务提供小型构造函数。

如果您还决定重写其他回调方法(如 onCreate()、onStartCommand() 或 onDestroy()),请确保调用超类实现,以便 IntentService 能够妥善处理工作线程的生命周期。

除 onHandleIntent() 之外,您无需从中调用超类的唯一方法就是 onBind()。只有在服务允许绑定时,您才需实现该方法。

扩展服务类

借助 IntentService,您可以非常轻松地实现启动服务。但是,若要求服务执行多线程(而非通过工作队列处理启动请求),则可通过扩展 Service 类来处理每个 Intent。

为进行比较,以下示例代码展示了 Service 类的实现,该类执行的工作与上述使用 IntentService 的示例完全相同。换言之,对于每个启动请求,其均使用工作线程来执行作业,且每次仅处理一个请求。


public class HelloService extends Service {

  private Looper serviceLooper;

  private ServiceHandler serviceHandler;

  // Handler that receives messages from the thread

  private final class ServiceHandler extends Handler {

      public ServiceHandler(Looper looper) {

          super(looper);

      }

      @Override

      public void handleMessage(Message msg) {

          // Normally we would do some work here, like download a file.

          // For our sample, we just sleep for 5 seconds.

          try {

              Thread.sleep(5000);

          } catch (InterruptedException e) {

              // Restore interrupt status.

              Thread.currentThread().interrupt();

          }

          // Stop the service using the startId, so that we don't stop

          // the service in the middle of handling another job

          stopSelf(msg.arg1);

      }

  }

  @Override

  public void onCreate() {

    // Start up the thread running the service. Note that we create a

    // separate thread because the service normally runs in the process's

    // main thread, which we don't want to block. We also make it

    // background priority so CPU-intensive work doesn't disrupt our UI.

    HandlerThread thread = new HandlerThread("ServiceStartArguments",

            Process.THREAD_PRIORITY_BACKGROUND);

    thread.start();

    // Get the HandlerThread's Looper and use it for our Handler

    serviceLooper = thread.getLooper();

    serviceHandler = new ServiceHandler(serviceLooper);

  }

  @Override

  public int onStartCommand(Intent intent, int flags, int startId) {

      Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();

      // For each start request, send a message to start a job and deliver the

      // start ID so we know which request we're stopping when we finish the job

      Message msg = serviceHandler.obtainMessage();

      msg.arg1 = startId;

      serviceHandler.sendMessage(msg);

      // If we get killed, after returning from here, restart

      return START_STICKY;

  }

  @Override

  public IBinder onBind(Intent intent) {

      // We don't provide binding, so return null

      return null;

  }

  @Override

  public void onDestroy() {

    Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show();

  }

}

相较于使用 IntentService,此示例需要执行更多工作。看代码,创建工作线程的代码和IntentService中是一致的。

但是,由于 onStartCommand() 的每个调用均有您自己处理,因此您可以同时执行多个请求。此示例并未这样做,但若您希望如此,则可为每个请求创建新线程,然后立即运行这些线程(而非等待上一个请求完成,可以调整为很多服务端使用的线程模型中的Acceptor和Workers线程处理多个请求)。

请注意,<font color=BrulyWood>onStartCommand()方法必须返回一个整数</font>。此整数用于描述系统应如何在系统终止服务的情况下继续运行服务。IntentService 的默认实现会为您处理此情况,但您可以对其进行修改。从 onStartCommand() 返回的值必须是以下常量之一:

  • START_NOT_STICKY

如果系统在 onStartCommand() 返回后终止服务,除非有待传递的挂起 Intent,否则系统不会重建服务。这是最安全的选项,可以避免在不必要时以及应用能够轻松重启所有未完成的作业时运行服务。

If the system kills the service after onStartCommand() returns, do not recreate the service unless there are pending intents to deliver. This is the safest option to avoid running your service when not necessary and when your application can simply restart any unfinished jobs.

  • START_STICKY

如果系统在 onStartCommand() 返回后终止服务,则其会重建服务并调用 onStartCommand(),但不会重新传递最后一个 Intent。相反,除非有挂起 Intent 要启动服务,否则系统会调用包含空 Intent 的 onStartCommand()。在此情况下,系统会传递这些 Intent。此常量适用于不执行命令、但无限期运行并等待作业的媒体播放器(或类似服务)。

<font color=BrulyWood>简单一点说:当返回START_STICKY时,系统会重新创建服务再次调用onStartCommand(),如果有挂起的Intent需要执行,onstartCommand()就传递该Intent,否则传递参数为null。</font>

  • START_REDELIVER_INTENT

如果系统在 onStartCommand() 返回后终止服务,则其会重建服务,并通过传递给服务的最后一个 Intent 调用 onStartCommand()。所有挂起 Intent 均依次传递。此常量适用于主动执行应立即恢复的作业(例如下载文件)的服务。

If the system kills the service after onStartCommand() returns, recreate the service and call onStartCommand() with the last intent that was delivered to the service. Any pending intents are delivered in turn. This is suitable for services that are actively performing a job that should be immediately resumed, such as downloading a file.

<font color=BrulyWood>实质上这两句话是矛盾的,看源码AMS会把所有挂起状态的Intent(存在于pendingStarts列表中)按照顺序依次通过onStartCommand()发送给重建后的service,而不仅仅是最后一个</font>

image
  • 经过在Nexus 6,androd 7.1.1上测试,确实如上分析所说的,当服务被杀掉后,所有处于Deliver started且还未被执行的Intent在其后的某一时刻,会按照顺序依次通过onStartCommand传递给重建后的service

  • 但在HUAWEI MATE 10 Pro,android 10,Galaxy S8,android 9等非原生系统上测试发现,AMS确实保存处于挂起状态的Intent,但是过了指定的时间间隔后依然没有见到,那些挂起状态的Intent被AMS发送过来?只能说国内的手机场商为了避免流氓应用无限保活而定制了AMS的逻辑。

启动服务

您可以通过将 Intent 传递给 startService() 或 startForegroundService(),从 Activity 或其他应用组件启动服务。Android 系统会调用服务的 onStartCommand() 方法,并向其传递 Intent,从而指定要启动的服务。

<font color=BrulyWood>注意:如果您的应用面向 API 26 或更高版本,除非应用本身在前台运行,否则系统不会对使用或创建后台服务施加限制。如果应用需要创建前台服务,则其应调用 startForegroundService()。此方法会创建后台服务,但它会向系统发出信号,表明服务会将自行提升至前台。创建服务后,该服务必须在五秒内调用自己的 startForeground() 方法。</font>

startService() 方法会立即返回,并且 Android 系统会调用服务的 onStartCommand() 方法。如果服务尚未运行,则系统首先会调用 onCreate(),然后调用 onStartCommand()。

例如,Activity 可以结合使用显式 Intent 与 startService(),从而启动上文中的示例服务 (HelloService):


Intent intent = new Intent(this, HelloService.class);

startService(intent);

如果服务亦未提供绑定,则应用组件与服务间的唯一通信模式便是使用 startService() 传递的 Intent。但是,如果您希望服务返回结果,则启动服务的客户端可以为广播(通过 getBroadcast() 获得)创建一个 PendingIntent,并将其传递给启动服务的 Intent 中的服务。然后,服务便可使用广播传递结果。

多个服务启动请求会导致多次对服务的 onStartCommand() 进行相应的调用。但是,如要停止服务,只需一个服务停止请求(使用 stopSelf() 或 stopService())即可。

停止服务

启动服务必须管理自己的生命周期。因为,除非必须回收内存资源,否则系统不会停止或者销毁服务,并且服务在onStartCommand()返回后仍会继续执行。服务必须通过调用自身的stopSelf()自行停止运行,或者由其他组件调用stopService()停止。

一旦请求使用 stopSelf()stopService() 来停止服务,系统便会尽快销毁服务。

如果服务同时处理多个对onStartCommand()的请求,则不应该在处理完一个请求后停止服务,因为可能收到新的请求(在第一个请求结束时停止会终止第二个请求)。为了避免此问题,可以使用stopSelf(int)确保服务停止请求始终基于最近的启动请求。

<font color=BrulyWood>换句话说,在调用stopSelf(int)时,需要传递与停止请求ID相对应的启动请求ID(就是onStartCommand(int)中的参数startId)。此外,如果服务在在调用stopSelf(int)之前收到新的启动请求,由于ID不匹配,服务也不会停止。</font>

注意:为避免浪费系统资源和消耗电池电量,请确保应用在工作完成之后停止其服务。如有必要,其他组件可通过调用 stopService() 来停止服务。即使为服务启用绑定,如果服务收到对 onStartCommand() 的调用,始终仍须亲自停止服务。

创建绑定服务

绑定服务允许应用组件通过调用 bindService() 与其绑定,从而创建长期连接。此服务通常不允许组件通过调用 startService() 来启动它。

如需 Activity 与其他应用组件中的服务进行交互,或需要通过进程间通信 (IPC) 向其他应用公开某些应用功能,则应创建绑定服务。

如要创建绑定服务,您需通过实现 onBind() 回调方法返回 IBinder,从而定义与服务进行通信的接口。然后,其他应用组件可通过调用 bindService() 来检索该接口,并开始调用与服务相关的方法。服务只用于与其绑定的应用组件,因此若没有组件与该服务绑定,则系统会销毁该服务。您不必像通过 onStartCommand() 启动的服务那样,以相同方式停止绑定服务。

要创建绑定服务,必须定义客户端如何与服务端进行通信的接口。服务与客户端之间的这个接口必须是IBinder的实现,并且服务必须从onBind()方法中返回该接口。收到IBinder后,客户端便可开始通过该接口与服务进行交互。

多个客户端可以同时绑定到同一个服务。完成与服务的交互后,客户端会通过调用unbindService()来取消绑定。如果检测到没有绑定到服务的客户端,系统会销毁该服务。

实现绑定服务有多种方法,并且此实现比启动服务更为复杂。具体信息请参阅另一份文档绑定服务,了解关于绑定服务的详细介绍。

向用户发送通知

处于运行状态时,服务可以使用 Toast 通知或状态栏通知来通知用户所发生的事件。

Toast 通知是指仅在当前窗口界面显示片刻的消息。状态栏通知在状态栏中提供内含消息的图标,用户可通过选择该图标来执行操作(例如启动 Activity)。

通常,当某些后台工作(例如文件下载)已经完成,并且用户可对其进行操作时,状态栏通知是最佳方法。当用户从展开视图中选定通知时,该通知即可启动 Activity(例如显示已下载的文件)。

如需了解详细信息,请参阅 Toast 通知或状态栏通知开发者指南。

在前台运行服务

前台服务是用户可感知的一种服务,即使在内存不足时,系统也不会考虑将其终止。<font color=BrulyWood>前台服务必须为状态栏提供通知,将其放在运行中的标题下方。这意味着除非将服务停止或从前台移除,否则不能清除该通知。</font>

  • <font color=Tan>请限制应用使用前台服务</font>

    只有当应用执行的任务需供用户查看(即使该任务未直接与应用交互)时,您才应使用前台服务。因此,前台服务必须显示优先级为 PRIORITY_LOW 或更高的状态栏通知,这有助于确保用户知道应用正在执行的任务。如果某操作不是特别重要,因而您希望使用最低优先级通知,则可能不适合使用服务;相反,您可以考虑使用计划作业。

    每个运行服务的应用都会给系统带来额外负担,从而消耗系统资源。如果应用尝试使用低优先级通知隐藏其服务,则可能会降低用户正在主动交互的应用的性能。因此,如果某个应用尝试运行拥有最低优先级通知的服务,则系统会在抽屉式通知栏的底部调用出该应用的行为。

例如,应将通过服务播放音乐的音乐播放器设置为在前台运行,因为用户会明确意识到其操作。状态栏中的通知可能表示正在播放的歌曲,并且其允许用户通过启动 Activity 与音乐播放器进行交互。同样,如果应用允许用户追踪其运行,则需通过前台服务来追踪用户的位置。

  • <font color=Tan>权限申请</font>

    注意:如果应用面向 Android 9(API 28)或更高版本并使用前台服务,则其必须请求 FOREGROUND_SERVICE 权限。这是一种普通权限,因此,系统会自动为请求权限的应用授予此权限。

    如果面向 API 28 或更高版本的应用试图创建前台服务但未请求 FOREGROUND_SERVICE,则系统会抛出 SecurityException。

如要请求让服务在前台运行,请调用 startForeground()。此方法采用两个参数:唯一标识通知的整型数和用于状态栏的 Notification。此通知必须拥有 PRIORITY_LOW 或更高的优先级。下面是示例:


Intent notificationIntent = new Intent(this, ExampleActivity.class);

PendingIntent pendingIntent =

        PendingIntent.getActivity(this, 0, notificationIntent, 0);

Notification notification =

          new Notification.Builder(this, CHANNEL_DEFAULT_IMPORTANCE)

    .setContentTitle(getText(R.string.notification_title))

    .setContentText(getText(R.string.notification_message))

    .setSmallIcon(R.drawable.icon)

    .setContentIntent(pendingIntent)

    .setTicker(getText(R.string.ticker_text))

    .build();

startForeground(ONGOING_NOTIFICATION_ID, notification);

如要从前台移除服务,请调用 stopForeground()。此方法采用布尔值,指示是否需同时移除状态栏通知。此方法不会停止服务。但是,如果您在服务仍运行于前台时将其停止,则通知也会随之移除。

管理服务的生命周期

服务的生命周期比 Activity 的生命周期要简单得多。但是,密切关注如何创建和销毁服务反而更加重要,因为服务可以在用户未意识到的情况下运行于后台。

服务生命周期(从创建到销毁)可遵循以下任一路径:

  • 启动服务

    该服务在其他组件调用 startService() 时创建,然后无限期运行,且必须通过调用 stopSelf() 来自行停止运行。此外,其他组件也可通过调用 stopService() 来停止此服务。服务停止后,系统会将其销毁。

  • 绑定服务

    该服务在其他组件(客户端)调用 bindService() 时创建。然后,客户端通过 IBinder 接口与服务进行通信。客户端可通过调用 unbindService() 关闭连接。多个客户端可以绑定到相同服务,而且当所有绑定全部取消后,系统即会销毁该服务。(服务不必自行停止运行。)

这两种使用服务的方式并非完全独立。您可以绑定到已使用 startService() 启动的服务。例如,您可以使用 Intent(标识要播放的音乐)来调用 startService(),从而启动后台音乐服务。随后,当用户需稍加控制播放器或获取有关当前所播放歌曲的信息时,Activity 可通过调用 bindService() 绑定到服务。此类情况下,在所有客户端取消绑定之前,stopService() 或 stopSelf() 实际不会停止服务。

实现生命周期回调

与 Activity 类似,服务也拥有生命周期回调方法,您可通过实现这些方法来监控服务状态的变化并适时执行工作。以下框架服务展示了每种生命周期方法:


public class ExampleService extends Service {

    int startMode;      // indicates how to behave if the service is killed

    IBinder binder;      // interface for clients that bind

    boolean allowRebind; // indicates whether onRebind should be used

    @Override

    public void onCreate() {

        // The service is being created

    }

    @Override

    public int onStartCommand(Intent intent, int flags, int startId) {

        // The service is starting, due to a call to startService()

        return mStartMode;

    }

    @Override

    public IBinder onBind(Intent intent) {

        // A client is binding to the service with bindService()

        return mBinder;

    }

    @Override

    public boolean onUnbind(Intent intent) {

        // All clients have unbound with unbindService()

        return mAllowRebind;

    }

    @Override

    public void onRebind(Intent intent) {

        // A client is binding to the service with bindService(),

        // after onUnbind() has already been called

    }

    @Override

    public void onDestroy() {

        // The service is no longer used and is being destroyed

    }

}

与 Activity 生命周期回调方法不同,您不需要调用这些回调方法的超类实现。

image

左图显示使用 startService() 创建的服务的生命周期,右图显示使用 bindService() 创建的服务的生命周期。

通过实现这些方法,您可以监控服务生命周期的以下两种嵌套循环:

  • 服务的整个生命周期贯穿调用 onCreate() 和返回 onDestroy() 之间的这段时间。与 Activity 类似,服务也在 onCreate() 中完成初始设置,并在 onDestroy() 中释放所有剩余资源。例如,音乐播放服务可以在 onCreate() 中创建用于播放音乐的线程,然后在 onDestroy() 中停止该线程。

  • 服务的活动生命周期从调用 onStartCommand() 或 onBind() 开始。每种方法均会获得 Intent 对象,该对象会传递至 startService() 或 bindService()。

对于启动服务,活动生命周期与整个生命周期会同时结束(即便是在 onStartCommand() 返回之后,服务仍然处于活动状态)。对于绑定服务,活动生命周期会在 onUnbind() 返回时结束。

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

`

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,406评论 6 503
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,732评论 3 393
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,711评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,380评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,432评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,301评论 1 301
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,145评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,008评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,443评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,649评论 3 334
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,795评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,501评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,119评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,731评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,865评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,899评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,724评论 2 354

推荐阅读更多精彩内容

  • 前言:本文所写的是博主的个人见解,如有错误或者不恰当之处,欢迎私信博主,加以改正!原文链接,demo链接 Serv...
    PassersHowe阅读 1,413评论 0 5
  • [文章内容来自Developers] Service是一个可以在后台执行长时间运行操作而不提供用户界面的应用组件。...
    岳小川阅读 865评论 0 7
  • 服务基本上分为两种形式 启动 当应用组件(如 Activity)通过调用 startService() 启动服务时...
    pifoo阅读 1,272评论 0 8
  • 2.1 Activity 2.1.1 Activity的生命周期全面分析 典型情况下的生命周期:在用户参与的情况下...
    AndroidMaster阅读 3,040评论 0 8
  • Service是一个可以在后台执行长时间运行操作而不提供用户界面的应用组件,由其他应用组件启动,即时切换到其他应用...
    Discredited阅读 579评论 0 1