一、Service简介
四大组件之一,是一种可在后台执行长时间运行操作而不提供界面的应用组件。服务可由其他应用组件启动,而且即使用户切换到其他应用,服务仍将在后台继续运行。此外,组件可通过绑定到服务与之进行交互,甚至是执行进程间通信 (IPC)。例如,服务可在后台处理网络事务、播放音乐,执行文件 I/O 或与内容提供程序进行交互。
一般分为:前台服务、后台服务、绑定服务
前台服务:前台服务执行一些用户能注意到的操作。例如,音频应用会使用前台服务来播放音频曲目。前台服务必须显示通知即使用户停止与应用的交互,前台服务仍会继续运行
后台服务:后台服务执行用户不会直接注意到的操作。例如,如果应用使用某个服务来压缩其存储空间,则此服务通常是后台服务
绑定服务:当应用组件通过调用 bindService()绑定到服务时,服务即处于绑定状态。绑定服务会提供客户端-服务器接口,以便组件与服务进行交互、发送请求、接收结果,甚至是利用进程间通信 (IPC) 跨进程执行这些操作。仅当与另一个应用组件绑定时,绑定服务才会运行。多个组件可同时绑定到该服务,但全部取消绑定后,该服务即会被销毁。
二、生命周期
服务启动方式有两种:启动服务和绑定服务,下图是两种启动方式的生命周期
onStartCommand:
当另一个组件(如 Activity)请求启动服务时,系统会通过调用 startService() 来调用此方法。执行此方法时,服务即会启动并可在后台无限期运行。如果您实现此方法,则在服务工作完成后,您需负责通过调用 stopSelf() 或 stopService() 来停止服务。(如果您只想提供绑定,则无需实现此方法。)
onBind:
当另一个组件想要与服务绑定(例如执行 RPC)时,系统会通过调用 bindService() 来调用此方法。在此方法的实现中,您必须通过返回 IBinder 提供一个接口,以供客户端用来与服务进行通信。请务必实现此方法;但是,如果您并不希望允许绑定,则应返回 null。
onCreate:
首次创建服务时,系统会(在调用 onStartCommand() 或 onBind() 之前)调用此方法来执行一次性设置程序。如果服务已在运行,则不会调用此方法。
onDestroy:
当不再使用服务且准备将其销毁时,系统会调用此方法。服务应通过实现此方法来清理任何资源,如线程、注册的侦听器、接收器等。这是服务接收的最后一个调用。
三、onStartCommand的返回值介绍
START_STICKY:
在Service启动后,如果该Service被系统杀死了,系统会重新创建该Service,onStartCommand方法会被调用,但是传给Service的intent为null
START_NOT_STICKY:
在Service启动后,如果该Service被系统杀死了,系统不会重新创建该Service。除非开发者明确地使用startService的方式
START_REDELIVER_INTENT:
在Service启动后,如果该Service被系统杀死了,系统会重新创建该Service,onStartCommand方法会调用,并且将上次的Intent通过onStartCommand方法传递进来。除非开发者明确使用stopSelf方法停止该Service,否则其不会停止。
START_STICKY_COMPATIBILITY
其功能和START_STICKY一样,是START_STICKY的兼容版本,但是不保证onStartCommand方法会被调用。
四、开启服务的两种方式
1. startService
startService 第一次启动时会触发OnCreate--> onStartCommond方法, 如果Service已经存在,再次启动时则只处罚OnStartCommond方法,且第三个参数startId + 1,停止任务时,我们可以直接stopSelf,也可以通过stopSelf(startId)来停止服务
stopSelf(startId) 这种方式停止时,如果startService了多次,需要将所有startId关闭后才会触发OnDestroy回调
class LearnService : Service() {
override fun onBind(intent: Intent?): IBinder? {
"onBind".log()
return null
}
override fun onCreate() {
super.onCreate()
"onCreate".log()
}
override fun onDestroy() {
super.onDestroy()
"onDestroy".log()
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
"onStartCommand".log()
val mThread = object : Thread() {
override fun run() {
super.run()
var count: Int = 0
while (!isInterrupted) {
"任务执行中:$startId 执行次数:${count++}".log()
if (count > 5) {
interrupt()
stopSelf(startId)
}
SystemClock.sleep(1000)
}
}
}
mThread.start()
return super.onStartCommand(intent, flags, startId)
}
}
间隔两秒后开启两次 打印结果如下:
2020-11-12 15:48:50.839 3337-3337/com.czy.systemlearn D/YYYYYY: onCreate
2020-11-12 15:48:50.841 3337-3337/com.czy.systemlearn D/YYYYYY: onStartCommand
2020-11-12 15:48:50.842 3337-4173/com.czy.systemlearn D/YYYYYY: 任务执行中:1 执行次数:0
2020-11-12 15:48:51.848 3337-4173/com.czy.systemlearn D/YYYYYY: 任务执行中:1 执行次数:1
2020-11-12 15:48:52.856 3337-4173/com.czy.systemlearn D/YYYYYY: 任务执行中:1 执行次数:2
2020-11-12 15:48:53.005 3337-3337/com.czy.systemlearn D/YYYYYY: onStartCommand
2020-11-12 15:48:53.008 3337-4189/com.czy.systemlearn D/YYYYYY: 任务执行中:2 执行次数:0
2020-11-12 15:48:53.864 3337-4173/com.czy.systemlearn D/YYYYYY: 任务执行中:1 执行次数:3
2020-11-12 15:48:54.016 3337-4189/com.czy.systemlearn D/YYYYYY: 任务执行中:2 执行次数:1
2020-11-12 15:48:54.870 3337-4173/com.czy.systemlearn D/YYYYYY: 任务执行中:1 执行次数:4
2020-11-12 15:48:55.024 3337-4189/com.czy.systemlearn D/YYYYYY: 任务执行中:2 执行次数:2
2020-11-12 15:48:55.876 3337-4173/com.czy.systemlearn D/YYYYYY: 任务执行中:1 执行次数:5
2020-11-12 15:48:56.033 3337-4189/com.czy.systemlearn D/YYYYYY: 任务执行中:2 执行次数:3
2020-11-12 15:48:57.040 3337-4189/com.czy.systemlearn D/YYYYYY: 任务执行中:2 执行次数:4
2020-11-12 15:48:58.048 3337-4189/com.czy.systemlearn D/YYYYYY: 任务执行中:2 执行次数:5
2020-11-12 15:48:58.059 3337-3337/com.czy.systemlearn D/YYYYYY: onDestroy
2. bindService
同一个 Service 可以被多个组件绑定,只有所有绑定它的组件都进行了 unBind 操作,这个 Service 才会被销毁。
绑定服务 bindService, 其生命周期是 onCreate -> onBind ,绑定成功后再次调用bindService不会触发生命周期。启动方法参数如下:
bindService(Intent service, ServiceConnection conn,int flags)
- Intent : 要开启的服务
- ServiceConnection:一个接口,包含两个回调onServiceConnected(绑定成功后触发)和onServiceDisconnected(服务端意外挂掉触发)
- flags:一般选用BIND_AUTO_CREATE 当bindService时,该服务如果不存在则自动创建该服务
ServiceConnection的onServiceConnected会返回一个IBinder对象, Service 也有一个onBind方法,返回一个IBinder对象,他们是同一个对象,我们可以通过该对象来实现 Activity 调用Service方法通信
代码示例
class LearnService : Service() {
override fun onBind(intent: Intent?): IBinder? {
"onBind".log()
return MyBind()
}
override fun onUnbind(intent: Intent?): Boolean {
"onUnbind".log()
return super.onUnbind(intent)
}
override fun onCreate() {
super.onCreate()
"onCreate".log()
}
override fun onDestroy() {
super.onDestroy()
"onDestroy".log()
}
// 暴露给 Activity调用的方法
fun serviceInnerMethods(msg: String) {
msg.log()
}
open inner class MyBind : Binder() {
val service: LearnService
get() = this@LearnService
}
}
调用方法
class MainActivity : AppCompatActivity() {
lateinit var connection: ServerConnect
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
connection = ServerConnect()
btnBindService.setOnClickListener {
//绑定服务
bindService(Intent(MainActivity@ this, LearnService::class.java), connection, BIND_AUTO_CREATE)
}
btnUnBindService.setOnClickListener {
//解绑服务
unbindService(connection)
}
}
class ServerConnect : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
// 拿到Service对象 调用其内部方法
val myBind = service as LearnService.MyBind
myBind.service.serviceInnerMethods("Activity 已经和你建立了连接")
}
override fun onServiceDisconnected(name: ComponentName?) {
"连接断开".log()
}
}
}
最终结果
2020-11-12 17:31:36.607 13974-13974/com.czy.systemlearn D/YYYYYY: 点击绑定服务 ====
2020-11-12 17:31:36.623 13974-13974/com.czy.systemlearn D/YYYYYY: onCreate
2020-11-12 17:31:36.624 13974-13974/com.czy.systemlearn D/YYYYYY: onBind
2020-11-12 17:31:36.628 13974-13974/com.czy.systemlearn D/YYYYYY: Activity 已经和你建立了连接
2020-11-12 17:31:39.467 13974-13974/com.czy.systemlearn D/YYYYYY: 点击解绑服务 ====
2020-11-12 17:31:39.483 13974-13974/com.czy.systemlearn D/YYYYYY: onUnbind
2020-11-12 17:31:39.486 13974-13974/com.czy.systemlearn D/YYYYYY: onDestroy
上面只是Activity 向Service调用,如果Service获取到最终结果后如何将结果传递回Activity,除了广播外,我们可以在Service设置一个回调监听即可,
lateinit var dataCallback: DataCallback
//设置数据回调监听
fun setDataCallbackListener(dataCallback: DataCallback) {
this.dataCallback = dataCallback
}
interface DataCallback {
fun callBack(result: String)
}
// 在合适的时机触发即可
if(this::dataCallback.isInitialized){
dataCallback.callBack("Service 将结果传递给Activity")
}
3. 先StartService 在BindService的生命周期
先startService : onCreate --> onStartCommand
在BindService: --> onBinde
然后unBindService: -->onUnbind
最后stopService: -->onDestroy先绑定bindService: onCreate --> onBinde
在startService: --> onStartCommand
然后stopService: 不触发生命周期
最后UNBindService: --> unbind-->onDestroy
其他场景类似,只有stopService 跟unbindservice都执行了,才会走onDestroy生命周期
注意:混合使用时,如果我们绑定服务时flags 用的BIND_AUTO_CREATE,binderService后,在unbindService,如果之前此时Service还在运行,我们再次bindService是不会触发onBinde生命周期回调的
五、IntentService
IntentService是继承于Service的子类,它可以处理异步处理请求,Client使用startService(Intent)来启动Service。在它内部有一个工作线程来处理耗时操作,当它处理完所有请求的时候会停止它自身,不需要我们手动控制。另外,可以启动IntentService多次,而每一个耗时操作会以工作队列的方式在IntentService 的 onHandleIntent回调方法中执行,并且,每次只会执行一个工作线程,执行完第一个再执行第二个,以此类推。
而且,所有请求都在一个单线程中,不会阻塞应用程序的主线程(UI Thread),同一时间只处理一个请求。
总结下相比Service的优势和特点:
- 内部有一个工作线程,来处理耗时操作
- 工作线程已队列形式运行,一个执行完在执行另一个(串行执行。)
- 不需要开发者手动停止服务
使用场景和示例
IntentService 可多次启动,工作线程内部会顺序执行。另外 IntentService 因为是服务的原因,相比普通线程具有更高的优先级,适用更高优先级的的后台任务,不容易被系统杀死。可以用作后台下载任务。
class MyIntentService() : IntentService("name") {
override fun onCreate() {
super.onCreate()
"onCreate".log()
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
"onStartCommand".log()
return super.onStartCommand(intent, flags, startId)
}
override fun onDestroy() {
"onDestroy".log()
super.onDestroy()
}
override fun onHandleIntent(intent: Intent?) {
val name = intent?.getStringExtra("name")
"当前线程: ${Thread.currentThread().name}".log()
SystemClock.sleep(2000)
"$name 任务执行完毕".log()
}
}
使用
var index = 0
btnStart.setOnClickListener {
val intent = Intent(MainActivity@ this, MyIntentService::class.java)
intent.putExtra("name", index++.toString())
startService(intent)
}
结果
2020-11-16 17:31:52.588 8466-8466/com.czy.systemlearn D/YYYYYY: onCreate
2020-11-16 17:31:52.589 8466-8466/com.czy.systemlearn D/YYYYYY: onStartCommand
2020-11-16 17:31:52.590 8466-8626/com.czy.systemlearn D/YYYYYY: 当前线程: IntentService[name]
2020-11-16 17:31:53.077 8466-8466/com.czy.systemlearn D/YYYYYY: onStartCommand
2020-11-16 17:31:53.560 8466-8466/com.czy.systemlearn D/YYYYYY: onStartCommand
2020-11-16 17:31:54.594 8466-8626/com.czy.systemlearn D/YYYYYY: 0 任务执行完毕
2020-11-16 17:31:54.597 8466-8626/com.czy.systemlearn D/YYYYYY: 当前线程: IntentService[name]
2020-11-16 17:31:56.600 8466-8626/com.czy.systemlearn D/YYYYYY: 1 任务执行完毕
2020-11-16 17:31:56.603 8466-8626/com.czy.systemlearn D/YYYYYY: 当前线程: IntentService[name]
2020-11-16 17:31:58.606 8466-8626/com.czy.systemlearn D/YYYYYY: 2 任务执行完毕
2020-11-16 17:31:58.613 8466-8466/com.czy.systemlearn D/YYYYYY: onDestroy
源码分析
public abstract class IntentService extends Service {
private volatile Looper mServiceLooper;//子线程中创建的Looper对象
private volatile ServiceHandler mServiceHandler;// 跟子线程中的Looper关联的Hander
private String mName;//线程名
private boolean mRedelivery;//决定onStartCommand的返回值(服务是否重启)
// 处理hander的消息,并通过startId来停止服务
private final class ServiceHandler extends Handler {
public ServiceHandler(Looper looper) {
super(looper);
}
@Override
public void handleMessage(Message msg) {
onHandleIntent((Intent)msg.obj);
//如果服务中所有的startid都被停止了,才会触发onDestroy
stopSelf(msg.arg1);
}
}
public IntentService(String name) {
super();
//name 是线程名称,主要是在调试时方便测试
mName = name;
}
//enabled: 如果为true: onStartCommand返回START_REDELIVER_INTENT,否则:START_NOT_STICKY
public void setIntentRedelivery(boolean enabled) {
mRedelivery = enabled;
}
@Override
public void onCreate() {
super.onCreate();
//创建一个子线程 里边运行着Looper
HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
// 作用:创建Looper 并启动looper循环
thread.start();
mServiceLooper = thread.getLooper();
//创建子线程Hander;将hander和HanderThread线程关联起来
mServiceHandler = new ServiceHandler(mServiceLooper);
}
//在服务的onStartCommand方法被调用
//作用:通过子线程hander进行消息分发
@Override
public void onStart(@Nullable Intent intent, int startId) {
Message msg = mServiceHandler.obtainMessage();
msg.arg1 = startId;
msg.obj = intent;
mServiceHandler.sendMessage(msg);
}
public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
onStart(intent, startId);
return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
}
@Override
public void onDestroy() {
mServiceLooper.quit();
}
@Override
@Nullable
public IBinder onBind(Intent intent) {
return null;
}
//真正处理需要在子线程运行的业务
@WorkerThread
protected abstract void onHandleIntent(@Nullable Intent intent);
}
intentService源码比较简单,流程如下:
- 在onCreate的时候创建两个对象:一个 handerThread(一个拥有Looper的线程),另一个:ServiceHandler 对象
(与子线程looper绑定的Hander), - 在onSTartCommand的时候进行mServiceHandler.sendMessag消息分发
- 分发的消息在onHandleIntent((Intent)msg.obj); 处理,处理完后通过StopSelf(startId)停止服务
- 因为hander机制 决定了任务是需要队列运行的。
六、android 8.0后台限制
限制
Android 8.0对系统进行了优化,为节省资源不允许后台应用创建后台服务,
处于前台时,应用可以自由创建和运行前台与后台 Service。 进入后台时,在一个持续数分钟的时间窗内,应用仍可以创建和使用 Service。 在该时间窗结束后,应用将被视为处于空闲状态。 此时,系统将停止应用的后台 Service,就像应用已经调用 Service 的 Service.stopSelf() 方法一样。
我们看下在8.0之上的手机在后台开启服务的运行情况;
Handler().postDelayed({
startService(Intent(MainActivity@ this, LearnService::class.java))
},10000)
在后台十秒后自动开启服务,生命周期打印日志如下:
2020-11-17 09:55:29.841 32453-32453/com.czy.systemlearn D/YYYYYY: onCreate
2020-11-17 09:55:29.845 32453-32453/com.czy.systemlearn D/YYYYYY: onStartCommand
2020-11-17 09:56:21.499 32453-32453/com.czy.systemlearn D/YYYYYY: onDestroy
可以看出:在55分时候创建服务后,在56分时就自动执行了onDestroy
解决
在 Android 8.0 之前,创建前台 Service 的方式通常是先创建一个后台 Service,然后将该 Service 推到前台。 Android 8.0 有一项复杂功能:系统不允许后台应用创建后台 Service。 因此,Android 8.0 引入了一种全新的方法,即 startForegroundService(),以在前台启动新 Service。 在系统创建 Service 后,应用有五秒的时间来调用该 Service 的 startForeground() 方法以显示新 Service 的用户可见通知。 如果应用在此时间限制内未调用 startForeground(),则系统将停止此 Service 并声明此应用为 ANR
调用:
startForegroundService(Intent(MainActivity@ this, LearnService::class.java))
因为需要在五秒内调用startForeground,我们更改下服务的onCreate代码,添加该方法
服务代码:
class LearnService : Service() {
override fun onBind(intent: Intent?): IBinder? {
"onBind".log()
return MyBind()
}
override fun onUnbind(intent: Intent?): Boolean {
"onUnbind".log()
return super.onUnbind(intent)
}
@RequiresApi(Build.VERSION_CODES.O)
override fun onCreate() {
super.onCreate()
"onCreate".log()
val channel = NotificationChannel(
100.toString(),
"background",
NotificationManager.IMPORTANCE_MIN
)
channel.enableVibration(false)
val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
manager.createNotificationChannel(channel)
val builder: Notification.Builder = Notification.Builder(
applicationContext, 100.toString()
).setContentTitle("正在后台运行")
.setSmallIcon(R.mipmap.sym_def_app_icon)
startForeground(1, builder.build())
}
override fun onDestroy() {
super.onDestroy()
"onDestroy".log()
}
// 暴露给 Activity调用的方法
fun serviceInnerMethods(msg: String) {
msg.log()
}
open inner class MyBind : Binder() {
val service: LearnService
get() = this@LearnService
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
"onStartCommand".log()
return super.onStartCommand(intent, flags, startId)
}
}