Service 比处于非活动状态的 Activity 更高的优先级,因此当系统请求资源时,他们被终止的可能性最小,如果系统过早的终止了一个 Service,那么当系统处于资源足够的情况下则会重新启动 Service。他们运行在应用程序的主线程下,就像 Activity 和 Broadcast Receiver 一样。为了确保应用程序能够及时的响应,通过使用 Thread 和 AsyncThread 类把进程移到后台线程中。

如果 Service 需要和用户直接进行互动(例如播放音乐),那么有必要把这个 Service 标识为前台组件,从而提高它的优先级,这样可以保证除了极端情况以外,该 Service 都不会被终止。但是这样会降低运行时管理资源的能力,可能会降低用户体验。


创建和控制 Service

要想创建一个 Service 就需要扩展 Service 类。需要重写 onCreate 和 onBind 方法。

当创建好服务类之后,想要对一个服务进行使用,必须对其进行注册。需要在 manifest 文件下的 application 下对其进行注册。
<service android:name="com.example.transport" android:enabled="true" />

为了确保 Service 只能由自己的服务启动和停止,需要在 service 节点下添加 permission 属性
<service android:name="com.example.transport" android:enabled="true" android:permission="com.example.transport.MyService" />

如果第三方应用程序想访问被限制的 Service 那么就需要在 manifest 文件下配置 uses-permission 属性。


执行一个 Service 并控制它的重新启动行为

重写 onStartCommand 事件处理程序,当 Service 通过 startService 启动时,会调用 onStartCommand 方法,这个方法可能会被执行多次。
onStartCommand 是从主线程上运行的,最好是在 onStartCommand 方法内创建一个新线程运行处理程序,并在该线程运行完成后终止 Service。

onStartCommand 方法是在 API Level 5(android 2.0) 以后才引入的,代替了 onStart 事件,它提供了和 onStart 一样的功能,还允许你告诉系统,如果系统在显示调用 stopService 或 stopSelf 之前终止了 Service,那么该如何重新启动服务。

@Overvider
public int onStartCommand(Intent intent, int flags, int startId) {
    startBackgroundTask(intent, startId);
    return Service.START_STICKY;
}

返回参数可以控制重新启动 Service 行为,常量列表如下:

  • START_STICKY 在运行时终止 Service 后,当重新启动 Service 时,将会调用 onStartCommand。注意,当重启 Service 时,传入 onStartCommand 的 Intent 参数将是 null。
    这种模式通常用于处理自身状态的 Service,以及根据需要通过 startService 和 stopService 显式的启动和终止的 Service。
    一般用于处理持续性操作的后台任务,例如音乐播放

  • START_NOT_STICKY 这种模式用于启动以处理特殊的操作和命令的 Service。通常当命令完成后,这些 Service 会调用 stopSelf 终止自己。
    当运行时终止后,只有当存在未处理的启动调用时,设为这个模式的 Service 才会重新启动,如果在终止 Service 后没有进行 startService,那么 Service 将停止运行,而不会调用 onStartCommand。
    对于处理特殊请求,例如更新或者网络轮询这样的定期业务,这是一种理想模式,这种模式比较谨慎,当停止 Service 后,它会在下一个调用间隔中尝试重新启动,而不会在存在资源竞争时重新启动。
    一般用于周期性执行的任务。

  • START_REDELIVER_INTENT 在一些情况下,需要确保从 Service 中请求的命令得以完成,例如在时效性比较重要的情况下。
    这种模式是前两种的组合,如果 Service 被运行时终止,那么只有当存在未处理的启动调用或者进程在调用 stopSelf 之前被终止时,才会重新启动 Service。在后一种情况下,将会调用 onStartCommand,并传入没有正常完成处理的 Intent。

在处理完成后,每种模式都需要使用 stopService 和 stopSelf 显式的停止 Service。

在 Android 2.0 (API Level 5)之前, Service 会 触发 onStart 事件来执行操作,但现在实现 onStart 方法等于是 onStartCommand 返回 START_STICKY。

第一次启动时会传入一个 Intent 给 startService 来启动 Service,当系统重新启动 Service 时, Intent 在 START_STICKY 模式中将为 null,而在 START_REDELIVER_INTENT 模式中将为原来的 Intent。

可以使用 flag 参数来找出启动 Service 的方式:

  • START_FLAG_REDELIVER 表示 Intent 参数是由于系统运行时在通过调用 stopSelf 显式停止 Service 之前终止它而重新启动。
  • FLAG_REIRY 表示 Service 已经在异常终止后重新启动,如果 Service 之前被设为 START_STICKY,则会传入这个标志。

启动和停止 Service

要启动一个 Service,需要调用 startService。和 Activity 一样,即可以在注册合适的 Intent Receiver 后使用操作来隐式的启动 Service,也可以显式的指定 Service 的类名以启动一个 Service。如果应用程序不具有此 Service 所要求的权限,将会抛出一个 SecurityException 异常。

private void explicitStart() {
    // 显式启动
    Intent intent = new Intent(this, MyService.class);
    startService(intent);
}

private void implicitStart() {
    // 隐式的启动一个音乐服务
    Intent intent = new Intent(MyMusicService.PLAY_ALBUM);
    intent.putExtra(MyMusicService.ALBUM_NAME_EXTRA, "United");
    intent.putExtra(MyMusicService.ARTIST_NAME_EXTRA, "Pheonix");
    startService(intent);
}

停止服务:

// 显式终止
stopService(new Intent(this, MyService.class));

// 隐式终止
Intent intent = new Intent(MyMusicService.PLAY_ALBUM);
stopService(intent);

由于 startService 不能嵌套,因此不管 startService 被执行了多少次,对于 stopService 来说执行一次就可以关闭该服务了。

当 Service 处理完成以后显式停止 Service,可以执行 stopSelf 终止它。此时可以不传入参数,强行终止服务。也可以传入一个 startId 值,确保为目前调用的每个 startService 实例完成处理。


将 Service 绑定到 Activity

Service 可以和 Activity 绑定,后者会维持一个对前者实例的引用。此引用允许你像对待其他实例化类那样,对正在运行的 Service 进行方法调用。

@Override
public IBinder onBind(Intent intent) {
    return binder;
}

public class MyBinder extends Binder {
    public MyMusicService getService() {
        return MyMusicService.this;
    }
}

private final IBinder binder = new MyBinder();

startService 退出Activity之后不会destroy service
stopService

bindService 在推出Activity之后会destroy service
unbindService

两者都可以启动服务,不同的是一个市绑定在当前activity上,另一个是全局。如果需要和当前服务进行通讯的话,必须要绑定到这个service上。


ytwman
294 声望7 粉丝