一、介绍
Service是一种可在后台执行长时间运行操作而不提供界面的应用组件。服务可由其他应用组件启动,而且即使用户切换到其他应用,服务仍将在后台继续运行。此外,组件可通过绑定到服务与之进行交互,甚至是执行进程间通信 (IPC)。例如,服务可在后台处理网络事务、播放音乐,执行文件 I/O 或与内容提供程序进行交互。
二、3种服务
服务分为三种:前台服务、后台服务、绑定服务
【1】前台服务
前台服务执行一些用户能注意到的操作。
例如,音频应用会使用前台服务来播放音频曲目。
前台服务必须显示通知。即使用户停止与应用的交互,前台服务仍会继续运行。
【2】后台服务
后台服务执行用户不会直接注意到的操作。
例如,如果应用使用某个服务来压缩其存储空间,则此服务通常是后台服务。
【3】绑定服务
当应用组件通过调用 bindService() 绑定到服务时,服务即处于绑定状态。
绑定服务会提供客户端-服务器接口,以便组件与服务进行交互、发送请求、接收结果,
甚至是利用进程间通信 (IPC) 跨进程执行这些操作。仅当与另一个应用组件绑定时,绑定服务才会运行。
多个组件可同时绑定到该服务,但全部取消绑定后,该服务即会被销毁。
三、注册服务
Service需要在清单文件中声明:
ExampleService 是 Service 的子类:
public class ExampleService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
return super.onStartCommand(intent, flags, startId);
}
}
四、启动服务和停止服务
通过 startService 启动服务:
// 必须显示启动services
Intent intent = new Intent(MainActivity.this, ExampleService.class);
startService(intent);
Intent 必须是显式,否则程序将会报错。
另外,还有一种启动方式,启动前台服务:
startForegroundService() // Android 8.0新增
Android 8.0(API 26)对Service做了一些限制,因为某些限制,新增了 startForegroundService 接口。
API 26 之后,当在前台启动Service时,startService 和 startForegroundService 并没有什么区别。当在后台启动Service时,则必须使用 startForegroundService 接口,将 Service 从后台切换到前台,并且在5秒之内调用自己的 startForeground()
方法。
通过 stopService 可以停止服务:
Intent intent = new Intent(MainActivity.this, ExampleService.class);
stopService(intent);
当然,也可以在服务结束的地方执行 stopSelf() 来停止服务。
启动服务之后,服务将在内存中一直运行,直到执行 stopService 或者 stopSelf 停止服务。
五、IntentService
onStartCommand 方法中不能存在耗时操作,否则主线程会被阻塞,甚至可能导致发生crash和ANR。
即使 onStartCommand 执行的不是耗时操作,当多次启动服务,onStartCommand 会被执行多次,这种情况仍然可能导致程序异常或者ANR。
为了保守起见,onStartCommand 的工作应该另起一个线程执行,除非能够保证没有耗时操作。
IntentService 会创建一个作业线程来执行任务,并且还可以保证线程安全,所以可以把 IntentService 做为 Service 的替代方案。
IntentService同样需要在注册:
public class HelloIntentService extends IntentService {
public HelloIntentService() {
super("HelloIntentService");
}
@Override
protected void onHandleIntent(Intent intent) {
}
}
启动IntentService:
Intent intent = new Intent(MainActivity.this, HelloIntentService.class);
startService(intent);
启动IntentService会执行以下操作:
- 创建默认的工作线程,用于在应用的主线程外执行传递给 onStartCommand() 的所有 Intent。
- 创建工作队列,用于将 Intent 逐一传递给 onHandleIntent() 实现,这样您就永远不必担心多线程问题。
- 在处理完所有启动请求后停止服务,因此您永远不必调用 stopSelf()。
- 提供 onBind() 的默认实现(返回 null)。
- 提供 onStartCommand() 的默认实现,可将 Intent 依次发送到工作队列和 onHandleIntent() 实现。
IntentService的业务逻辑是在 onHandleIntent 方法中执行的,onHandleIntent 方法执行在工作线程,并且能够保证线程安全,这就是 IntentService 的强大之处了。
IntentService 会自动调用 stopSelf() ,所以不需要主动调用 stopService 或者 stopSelf。但是,因为这个特性,IntentService无法保证让 Service 一直处理Running状态。
六、绑定服务
使用 bindService 绑定服务:
Intent intent = new Intent(MainActivity.this, ExampleService.class);
bindService(intent, serviceConnection, Service.BIND_AUTO_CREATE);
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
binder = IExampleBinder.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
binder = null;
}
};
绑定服务后,Service 会自动启动。
解绑服务:
unbindService(serviceConnection);
onBind的返回值不能为null:
public class ExampleService extends Service {
private class MyBinder extends IExampleBinder.Stub {
@Override
public String getName() throws RemoteException {
return "zhangsan";
}
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
Log.d("ExampleService", "==onBind==");
return new MyBinder();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d("ExampleService", "==onStartCommand1==");
return super.onStartCommand(intent, flags, startId);
}
}
IExampleBinder 是AIDL文件(IExampleBinder.aidl):
interface IExampleBinder {
String getName();
}
七、新特性
Android 8 之前, 只要调用 startService 就可以启动前台服务和后台服务。
但是,在Android 8.0之后,如果当前程序在后台,就必须启动前台服务 startForegroundService:
startForegroundService(new Intent(getApplicationContext(), RemoteService.class));
启动前台服务之后,必须在5秒之内执行 startForeground 方法:
NotificationChannel channel = new NotificationChannel("persident", "persident",
NotificationManager.IMPORTANCE_LOW);
NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
manager.createNotificationChannel(channel);
Notification notification = new NotificationCompat.Builder(this, "persident")
.setAutoCancel(true)
.setCategory(Notification.CATEGORY_SERVICE)
.setOngoing(true)
.setPriority(NotificationManager.IMPORTANCE_LOW).build();
startForeground(10, notification);
startForeground 的 ID 不能为0。
如要从前台移除服务,请调用 stopForeground()。
Android 9 之后,还需要添加权限:
[本章完...]