1. 服务是什么
Service
是一种可在后台执行长时间运行操作而不提供界面的应用组件。服务可由其他应用组件启动,而且即使用户切换到其他应用,服务仍将在后台继续运行。此外,组件可通过绑定到服务与之进行交互,甚至是执行进程间通信 (IPC)。例如,服务可在后台处理网络事务、播放音乐,执行文件 I/O 或与内容提供程序进行交互。
2. 服务的分类
按照启动方式,分为绑定服务和直接启动的服务。其中绑定类的服务可以实现与其他组件的通信。
按照运行类型,分为前台服务和后台服务。他们之间最直观的区别体现在前台服务会伴随通知。
-
按照运行地点,分为本地服务和远程服务。
- 本地服务:依附在主进程上,而不是独立进程
- 远程服务:该服务是独立的进程,一般用于常驻的提供系统服务的Service
3. 服务的实现流程
- 在
AndroidManifest
里声明Service
类;前台服务需要额外声明前台服务权限 - 继承
Service
类,并实现方法-
onStartCommand(): 当其他组件有可能通过调用
startService
方法来启动该服务时,需要实现此方法;这种情况需要通过调用stopService
来终止服务;我们在这个方法中接收启动服务者(可能是活动)传来的Intent,并根据Intent的参数在方法中调用当服务启动/停止时,需要执行的操作。 -
onBind(): 当不需要与组件绑定时,返回
null
; 需要与组件绑定时,这个binder
通过一个内部类来创建,并在它里面提供一个获得此服务的方法。 - onCreate(): 创建服务。
- onDestroy(): 销毁服务并清理线程等资源。
-
onStartCommand(): 当其他组件有可能通过调用
- 创建通知:只有前台服务需要实现
4. 绑定服务和直接启动的服务
4.1 绑定服务
4.1.1 什么是绑定服务
与组件绑定,由绑定和解绑来控制服务的开始和终止。可以很方便的与组件进行通信。
4.1.2 绑定服务的实现
- 服务类需要实现
onBind()
和onUnbind()
方法,在onBind()
返回的binder
中,可以设置一个返回当前服务的方法,方便后续的通信
override fun onBind(intent: Intent?): IBinder = binder
inner class MusicBinder : Binder() {
fun getService(): MusicService = this@MusicService
}
- 调用服务的类(一般为Activity) 通过
bindService()
和unBindService()
来绑定和解绑服务
bindService(Intent(this, MusicService::class.java), boundServiceConnection, Context.BIND_AUTO_CREATE)
- 这两个方法需要一个
ServiceConnection
对象,需要新建一个对象来实现ServiceConnection
中的onServiceConnected()
和onServiceDisconnected()
方法- 在Activity与Service建立关联和解除关联的时候调用
private val boundServiceConnection = object : ServiceConnection {
override fun onServiceConnected(className: ComponentName, service: IBinder) {
val binder: MusicService.MusicBinder = service as MusicService.MusicBinder
musicService = binder.getService()
mainViewModel.isMusicServiceBound = true
}
override fun onServiceDisconnected(arg0: ComponentName) {
musicService?.runAction(MusicState.STOP)
musicService = null
mainViewModel.isMusicServiceBound = false
}
}
- 其调用链为:bindService() -> onCreate() -> onBind() -> onUnbind() -> onDestroy()
4.2 非绑定服务
4.2.1 什么是非绑定服务
通过
startService
来启动,如果不主动调用stopService
停止方法,将会一直存在。
4.2.2 非绑定服务的实现
- 服务类在
onStartCommand()
中执行启动服务后的具体操作:
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
super.onStartCommand(intent, flags, startId)
intent?.extras?.run {
when (getSerializable(SERVICE_COMMAND) as TimerState) {
TimerState.START -> startTimer()
TimerState.PAUSE -> pauseTimerService()
TimerState.STOP -> endTimerService()
else -> return START_NOT_STICKY
}
}
return START_NOT_STICKY
}
- 启动服务的类通过
startService()
来启动服务,stopService()
来终止服务
//启动
Intent startIntent = new Intent(this, MyService.class);
startService(startIntent);
//停止
Intent stopIntent = new Intent(this, MyService.class);
stopService(stopIntent);
5. 前台服务和后台服务
5.1 前台服务
5.1.1 什么是前台服务
用户可以直观感知到并可与之产生交互的一种服务类型,其中直观感知体现在每个前台服务都需要配备一个通知显示在通知栏,交互则是通知用户可以与其交互完成一些操作,如启动某个特定的活动等。
5.1.2 前台服务的实现
前台服务实现过程的特别之处主要在于:
每个前台服务都需要配备一个通知,所以下面简单介绍一下通知的创建与实现。
5.1.3 通知的创建过程
自Build.Version.O版本起,在安卓手机上发送通知需要创建Notification Channel
- 通知的创建通过 NotificationManager.notify(ID, notification) 来进行
notificationManager.notify(NOTIFICATION_ID, notificationBuilder.build())
- 其中notification需要通过NotificationCompat.Builder来创建
private val notificationBuilder: NotificationCompat.Builder by lazy {
NotificationCompat.Builder(context, CHANNEL_ID)
.setContentTitle(context.getString(R.string.app_name))
.setSound(null)
.setContentIntent(contentIntent)
.setSmallIcon(R.drawable.ic_launcher_foreground)
.setPriority(NotificationCompat.PRIORITY_HIGH)
.setAutoCancel(true)
}
- NotificationBuilder里需要一个contentIntent,为PendingIntent类型
private val contentIntent by lazy {
PendingIntent.getActivity(
context,
0,
Intent(context, MainActivity::class.java),
PendingIntent.FLAG_UPDATE_CURRENT
)
}
- 还需要通过NotificationManager.createChannel(channel)来创建channel
notificationManager.createNotificationChannel(createChannel())
- 其中channel需要通过NotificationChannel来创建
private fun createChannel() =
NotificationChannel(
CHANNEL_ID,
CHANNEL_NAME,
NotificationManager.IMPORTANCE_DEFAULT
).apply {
description = CHANNEL_DESCRIPTION
setSound(null, null)
}
-
如果需要更新通知
- 在builder中进行具体更新的内容,如更新文本即调用
notificationBuilder.setContentText()
- 之后调用
notificationManager.notify()
进行通知的更新与替换
- 在builder中进行具体更新的内容,如更新文本即调用
5.1.4 如何开始和停止前台服务
- 开始和停止服务:
- 开始服务:如果是在服务类内,使用
startForeground
, 需要传入NotificationID
和notification
; 如果是在服务类外(如Activity)启动服务,调用startForegroundService
。 - 移除服务:
stopForeground
- 开始服务:如果是在服务类内,使用
5.2 后台服务
5.2.1 什么是后台服务
服务启动和终止的时候用户不会有感知,如天气更新、时间同步
5.2.2 后台工作的实现方式
- 后台服务
- IntentService:自安卓11后被废弃
- JobIntentService:IntentService的替代,应用JobScheduler来执行工作
- WorkManager:常应用于执行周期性工作或当有工作限制时使用
6. 附录
参考文章(图源):
https://www.jianshu.com/p/d963c55c3ab9
https://www.raywenderlich.com/20123726-android-services-getting-started#toc-anchor-005
https://developer.android.com/guide/components/services?hl=zh-cn
https://blog.csdn.net/lingyun_blog/article/details/41518589
https://juejin.cn/post/6844903664876781575
https://www.jianshu.com/p/e04c4239b07e