Android Service 探索(一)— 概览

一、什么是 Service?

Service 是一个可以在后台执行长时间任务而不提供用户界面的应用组件。服务可由其他应用组件启动,而且即使用户切换到其他应用,服务仍将在后台继续运行。此外,组件可以绑定到服务,和它进行交互,甚至是执行进程间通信 (IPC)。例如,服务可以处理网络事务、播放音乐,执行文件 I/O 或与内容提供程序交互,而所有这一切均可在后台进行。

注意:服务依赖于创建服务时所在的应用程序进程,默认是进程的主线程,它既不创建自己的线程,也不在单独的进程中运行(除非另行指定)。

  • 这意味着,当某个应用程序进程被杀掉时,所有依赖于该进程的服务也会停止。

  • 如果服务将执行任何 CPU 密集型工作或阻止性操作(例如 MP3 播放或联网),则应在服务内创建子线程来完成这项工作。

通过使用单独的线程,可以降低发生 “应用无响应”(ANR) 错误的风险,而应用的主线程仍可继续专注于运行用户与 Activity 之间的交互。

二、两种形式的服务

1. 启动服务

当应用组件(如 Activity)通过调用 startService() 启动服务时,服务即处于 “启动” 状态。一旦启动,服务即可在后台无限期运行,即使启动服务的组件已被销毁也不受影响。已启动的服务通常是执行单一操作,而且不会将结果返回给调用方。例如,它可能通过网络下载或上传文件。

2. 绑定服务

当应用组件通过调用 bindService() 绑定到服务时,服务即处于 “绑定” 状态。绑定服务提供了一个客户端-服务器接口,允许组件与服务进行交互、发送请求、获取结果,甚至是利用进程间通信 (IPC) 跨进程执行这些操作。 仅当与另一个应用组件绑定时,绑定服务才会运行。 多个组件可以同时绑定到该服务,但全部取消绑定后,该服务即会被销毁。

服务可以同时以这两种方式运行,也就是说,它既可以是启动服务(以无限期运行),也允许绑定。

问题只是在于是否实现了一组回调方法:onStartCommand()(允许组件启动服务)和 onBind()(允许绑定服务)。

任何应用组件均可像使用 Activity 那样通过调用 Intent 来使用服务(即使此服务来自另一应用)。不过,可以通过 AndroidManifest.xml 文件将服务声明为私有服务,并阻止其他应用访问。

三、什么时候使用服务,什么时候使用线程?

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

  • 如果是在主线程外部执行工作,不过只是在用户正在与应用交互时才有此需要,则应创建新线程而非服务。 例如,如果只是想在 Activity 运行的同时播放一些音乐,则可在 onCreate() 中创建线程,在 onStart() 中启动线程,然后在 onStop() 中停止线程。当然还可以使用 AsyncTask 或 HandlerThread,而非传统的 Thread 类。

注意:服务在默认情况下是在应用的主线程中运行,因此,如果服务执行的是密集型或阻止性操作,则应在服务内创建新线程。

四、如何创建服务?

要创建服务,就必须创建 Service 的子类(或使用它的一个现有子类)。在实现中需要重写一些回调方法,包括:

1. onStartCommand()

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

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return super.onStartCommand(intent, flags, startId);
    }
2. onBind()

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

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
3. onCreate()

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

    @Override
    public void onCreate() {
        super.onCreate();
    }
4. onDestroy()

当服务不再使用且将被销毁时,系统将调用此方法。服务应该实现此方法来清理所有资源,如线程、注册的侦听器、接收器等。 这是服务接收的最后一个调用。

    @Override
    public void onDestroy() {
        super.onDestroy();
    }

两种方式创建服务

1. startService()

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

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

如果组件是通过调用 bindService() 来创建服务(不会调用 onStartCommand() ),则服务只会在该组件与其绑定时运行。一旦该服务与所有客户端之间的绑定全部取消,系统便会销毁它。

Intent intent = new Intent(this, HelloService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);

五、什么时候系统会停止服务?

仅当内存过低且必须回收系统资源时,Android 系统才会强制停止服务。

  • 如果将服务绑定到具有用户焦点的 Activity,则它不太可能会终止;

  • 如果将服务声明为在前台运行,则它几乎永远不会终止。

  • 如果服务已启动并要长时间运行,则系统会随着时间的推移降低服务在后台任务列表中的位置,而服务也将随之变得非常容易被终止;

  • 如果服务是启动服务,则必须将其设计为能够妥善处理系统对它的重启。如果系统终止服务,那么一旦资源变得再次可用,系统便会重启服务(不过这还取决于从 onStartCommand() 返回的值)。

六、如何声明服务?

如同 Activity(以及其他组件)一样,必须在应用的清单文件中声明所有服务。要声明服务,请添加 <service> 元素作为 <application> 元素的子元素。例如:

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

android:name 属性是唯一必需的属性,用于指定服务的类名。应用一旦发布,即不应更改此类名,不然,可能会存在因依赖显式 Intent 启动或绑定服务而破坏代码的风险。

为了确保应用的安全性,请始终使用显式 Intent 启动或绑定 Service,且不要为服务声明 Intent 过滤器。启动哪个服务存在一定的不确定性,而如果对这种不确定性的考量非常有必要,则可为服务提供 Intent 过滤器并从 Intent 中排除相应的组件名称,但随后必须使用 setPackage() 方法设置 Intent 的软件包,这样可以充分消除目标服务的不确定性。

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

七、服务生命周期

服务的生命周期(从创建到销毁)根据服务类型不同而有两种路径:

1. 启动服务

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

2. 绑定服务

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

这两条路径并非完全独立。也就是说,你可以绑定到已经使用 startService() 启动的服务。例如,可以通过使用 Intent(标识要播放的音乐)调用 startService() 来启动后台音乐服务。随后,用户可能需要控制播放器或获取有关当前播放歌曲的信息时,Activity 可以通过调用 bindService() 绑定到服务。在这种情况下,除非所有客户端均取消绑定,否则 stopService()stopSelf() 不会实际停止服务。

八、实现生命周期回调

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

public class ExampleService extends Service {
    int mStartMode;       // indicates how to behave if the service is killed
    IBinder mBinder;      // interface for clients that bind
    boolean mAllowRebind; // 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 生命周期回调方法不同,你不需要调用这些回调方法的超类实现。(IntentService 则需要确保调用超类实现,以便 IntentService 能够妥善处理工作线程的生命周期。)

服务生命周期

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

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

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

无论服务是通过 startService() 还是 bindService() 创建,都会为所有服务调用 onCreate() 和 onDestroy() 方法。

  • 服务的有效生命周期从调用 onStartCommand() 或 onBind() 方法开始。每种方法均有 Intent 对象,该对象分别传递到 startService() 或 bindService()。

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

  • 对于绑定服务,有效生命周期在 onUnbind() 返回时结束。

不管启动方式如何,任何服务均有可能允许客户端与其绑定。因此,最初使用 onStartCommand()(通过客户端调用 startService() )启动的服务仍可接收对 onBind() 的调用(当客户端调用 bindService() 时)。

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 【Android Service】 Service 简介(★★★) 很多情况下,一些与用户很少需要产生交互的应用程...
    Rtia阅读 8,344评论 1 21
  • 前言:本文所写的是博主的个人见解,如有错误或者不恰当之处,欢迎私信博主,加以改正!原文链接,demo链接 Serv...
    PassersHowe阅读 5,306评论 0 5
  • 服务基本上分为两种形式 启动 当应用组件(如 Activity)通过调用 startService() 启动服务时...
    pifoo阅读 5,051评论 0 8
  • 2.1 Activity 2.1.1 Activity的生命周期全面分析 典型情况下的生命周期:在用户参与的情况下...
    AndroidMaster阅读 8,340评论 0 8
  • [文章内容来自Developers] Service是一个可以在后台执行长时间运行操作而不提供用户界面的应用组件。...
    岳小川阅读 4,367评论 0 7

友情链接更多精彩内容