[TOC]
1. 什么是Service
Service 是与Activity最为相似的组件,主要区别在于Service没有用户界面。Service是Android中实现程序后台运行解决方案,例如音乐的后台播放功能,消息的推送收取,后台文件的下载等等。它适合不与用户交互但需要执行较长周期的任务。这也是它与Activity的最大区别,它不收用户的操作影响,即使程序切换到后台,或打开其他应用程序,也能正常运行
2.使用场景
- 后台音乐播放
- 消息定时推送
- 后台下载任务
- 。。。
3. 生命周期
方法 | 说明 | 补充 |
---|---|---|
onCreate() | 创建服务 | 只会创建一次 |
onStartCommand(intent: Intent?, flags: Int, startId: Int): Int | 无绑定启动服务 | 多次启动,多次回调调用 返回值决定了服务异常结束情况下的重启操作:1.START_STICKY 2.START_NOT_STICKY 3.START_REDELIVER_INTENT 4.START_STICKY_COMPATIBILITY 5.START_STICKY |
onBind(intent: Intent): IBinder | 绑定启动服务 | 多次启动,多次回调调用(前提是调用的Intent不一样,否则不会执行多次) 这里返回的IBinder是服务内部实现并传递给绑定方用于交互的对象 |
onUnbind(intent: Intent?): Boolean | 解绑服务 | 多次调用会导致崩溃,解绑时会解绑所有已绑定的调用 |
stopService(Intent name) | 停止服务 | 多次调用只执行一次 |
onDestroy() | 销毁前调用 | |
stopSelf() | 停止自身 | |
onRebind(intent: Intent?) | 重新绑定 | 触发步骤:先启动,再绑定,再解绑,再次绑定<br />发生条件: 1. 需要由Activity先startService启动 2. onUnbind时需要返回true 3. 传递的intent需要一致 |
- START_STICKY:如果service进程被kill掉,保留service的状态为开始状态,但不保留递送的intent对象。随后系统会尝试重新创建service,由于服务状态为开始状态,所以创建服务后一定会调用onStartCommand(Intent,int,int)方法。如果在此期间没有任何启动命令被传递到service,那么参数Intent将为null。
- START_NOT_STICKY:“非粘性的”。使用这个返回值时,如果在执行完onStartCommand后,服务被异常kill掉,系统将会把它置为started状态,系统不会自动重启该服务,直到startService(Intent intent)方法再次被调用;
- START_REDELIVER_INTENT:重传Intent。使用这个返回值时,如果在执行完onStartCommand后,服务被异常kill掉,系统会自动重启该服务,并将Intent的值传入
- START_STICKY_COMPATIBILITY:START_STICKY的兼容版本,但不保证服务被kill后一定能重启。
4. 使用
2.1 Service的简要记录图表
创建与使用
2.2 无绑定启动
无绑定启动意味着Service与访问者之间无太大关联,同时也意味着它们无法通信或是数据交换,因此Service中执行自有的业务逻辑即可,等待着被销毁或任务结束自行销毁
//启动
var intent=Intent(this@MainActivity,SimpleService::class.java)
startService(intent)
//停止
var intent=Intent(this@MainActivity,SimpleService::class.java) intent.action="android.intent.action.SimpleService"
intent.`package`=this@MainActivity.packageName
stopService(intent)
调用方调用方法 | 说明 | 补充 |
---|---|---|
startService(Intent service) | 启动服务 | |
stopService(Intent name) | 停止服务 | 多次调用只执行一次 |
2.3 绑定启动
使用绑定启动时,意味着我们需要与Service进行交互,此时的做法是,在Service中先定义出Binder,在Binder中编写需要的功能,例如后台录音功能startRecord()与stopRecord(),然后通过bindService(intent,serviceConnection, Context.BIND_AUTO_CREATE)实现绑定,serviceConnection就是用来获取绑定成功时返回的binder对象,此时便可以进行交互了
//绑定服务
bindService(intent,serviceConnection, Context.BIND_AUTO_CREATE)
//解绑服务
unbindService(serviceConnection)
var serviceConnection = object : ServiceConnection {
override fun onServiceDisconnected(p0: ComponentName?) {
Log.e(TAG,"异常解绑时调用")
}
override fun onServiceConnected(p0: ComponentName?, p1: IBinder?) {
binder = p1 as SimpleService.CountBinder;
binder.startRecord()
binder.stopRecord()
Log.e(TAG,""+binder.count+"")
}
}
方法 | 说明 | 补充 |
---|---|---|
bindService(Intent service, ServiceConnection conn, int flags) | 绑定启动服务 | conn是服务绑定需要的中间类,通过它来绑定解绑 flags是指明绑定服务时是否自动创建(0-否,Context.BIND_AUTO_CREATE-是) |
unbindService(ServiceConnection conn) | 解绑服务 | 多次调用会出现异常java.lang.IllegalArgumentException: Service not registered
|
ServiceConnection.onServiceDisconnected(ComponentName var1) | 服务异常解绑时会调用该方法 | 正常解绑时不会触发改方法 |
ServiceConnection.onServiceConnected(ComponentName var1, IBinder var2) | 绑定成功时的回调 | var2就可以向下转换为我们需要的binder |
5.其他补充
服务实际上每个只会存在一个实例,在整个应用程序范围内都是通用的,绑定多个都可以获取到相同的binder实例
5.1 当Service既被无绑定启动又被绑定启动时,需要执行解绑及停止时才能进入销毁流程
Android系统机制:一个服务只要被启动或者被绑定了之后,就会一直处于运行状态
绑定关系实际上是Service将自己的内部对象binder传递给了调用方,并不会完全将Service的生命周期绑定到调用方,调用方调用unBindService方法实际上是切断了与Service的关联
5.2 错误:*java.lang.IllegalArgumentException: Service Intent must be explicit: Intent {act=android.intent.action.SimpleService }
Android 5.0 (Lollipop) 之后的规定。 不能用包名的方式定义Service Intent, 而要用显性声明
想继续使用隐式意图的话,加上包名信息即可
5.3多次启动或绑定服务时,也只会有一个服务存在,且binder也是同一个
通过设置不同的type实现绑定多个intent.type=""
5.4创建前台服务
与普通的Service类似,唯一不同就是在onCreate中到调用startForeground方法,这样在下拉通知栏中就可以看到该服务
@Override
public void onCreate() {
super.onCreate();
Intent intent = new Intent(this, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, 0);
Notification notification = new NotificationCompat.Builder(this)
.setContentTitle("Foreground")
.setContentText("前台服务")
.setSmallIcon(R.mipmap.ic_launcher)
.setContentIntent(pendingIntent)
.build();
startForeground(1,notification);
}
5.5 IntentService
Service不会专门启动一条单独的进程,与所在应用再同一个进程中
Service也不是专门一条新的线程,因此不应直接再Service中直接处理耗时任务
IntentService使用队列来管理请求 Intent
每当调用方通过Intent请求启动IntentService时,IntentService将该Intent加入队列中,然后开启一条新的worker线程处理该Intent。对于异步的startService请求,IntentService会按次序依次处理队列中的Intent,该线程保证同一时刻只处理一个Intent。IntentService使用新的worker线程处理Intent请求,onHandleIntent(@Nullable Intent intent) 方法中即在子线程中运行
因此IntentService不好阻塞主线程,自己就可以处理耗时任务默认实现onBind ,返回null
默认实现 onStartCommand,将请求Intent添加到队列中
-
自定义实现
//同样需要在清单文件中注册 public class MyIntentService extends IntentService { private static final String TAG = "MyIntentService"; public MyIntentService() { super("MyIntentService"); } @Override protected void onHandleIntent(@Nullable Intent intent) { Log.e(TAG, "onHandleIntent: " ); } }
执行完任务后自动停止