文章目录
Service 简介
借用官方话术:
- Service 是一种可在后台执行长时间运行操作而不提供界面的应用组件。服务可由其他应用组件启动,而且即使用户切换到其他应用,服务仍将在后台继续运行。此外,组件可通过绑定到服务与之进行交互,甚至是执行进程间通信 (IPC)。
- 例如,服务可在后台处理网络事务、播放音乐,执行文件 I/O 或与内容提供程序进行交互。
- 同时它还是 Android 四大组件之一; 另外还有 Activity, content provider, broadcast receiver
Service 分类
1、按前后台分类
分类 | 介绍 |
---|---|
前台服务 | 官方介绍: 前台服务执行一些用户能注意到的操作。例如,音频应用会使用前台服务来播放音频曲目。前台服务必须显示通知。即使用户停止与应用的交互,前台服务仍会继续运行。 补充: 内存不足时, 一般也不会杀死前台服务. 必须要在状态栏显示一条通知, 以告知用户 服务存在. |
后台服务 | 官方介绍: 后台服务执行用户不会直接注意到的操作。例如,如果应用使用某个服务来压缩其存储空间,则此服务通常是后台服务。 补充: 多数情况下, 后台服务可以使用 JobScheduler (作业), 或者 WorkManager (jetPack库) 代替 |
Android 8.0 以上需要注意:
前台服务需要添加一条权限
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
请使用 context
的 startForegroundService()
函数来启动一个 前台Service; 或者使用兼容方式
// 需要判断版本: Build.VERSION.SDK_INT >= 26
startForegroundService (intent);
// 兼容方法
ContextCompat.startForegroundService(this, intent)
2、按 远程服务, 本地服务 分类
分类 | 介绍 |
---|---|
本地服务(LocalService) | 1.跟主体应用同一进程 2.运行在主线程, 耗时任务需开子线程 3.主进程被杀死, 服务也会终止 4.单实例 |
远程服务(RemoteService) | 1.运行在独立进程 2.需要进行 IPC 通讯(AIDL, Messenger) 3.服务后台运行, 不受Activity影响 |
3、按是否可以通信分类
启动方式 | 介绍 |
---|---|
startService() | 不可通信 |
bindService() | 通过 ServiceConnection 接收 Service 中 onBind() 函数 返回的 IBinder 对象 进行通讯 |
感觉这个分类 有点鸡肋, 像是加戏 🤭
Service 生命周期
来张官方图:
由于存在 startService()
, bindService()
两种启动方式, 它们的对应的生命周期是不同的. 因此上图是两条路线.
推荐这篇文章, 图画的不错: Service生命周期 完全解析
总结:
可以看到, startService() 不会走 onBind() 方法, 也就没有 IBinder 对象, 这种Service不可通信(不算用注册广播等其他方式)
Service 只有一个, 多次startService() 不会重复执行 onCreate(), 但每次都会执行 onStartCommand(); 所以请把一次性的工作放在 onCreate() 中;
如果 先 startService(), 随后 bindService(). 则需要先 解绑(unBindService), 然后再 停止(stopService)
Service 使用:
1.简单使用:
首先自定义类, 继承 Service. 重写需要用到的方法;
class MyService : Service() {
val TAG = "MyService"
override fun onCreate() {...}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {...}
override fun onBind(intent: Intent?): IBinder? = null
override fun onUnbind(intent: Intent?): Boolean {...}
override fun onDestroy() {...}
}
其次, 配置 清单文件
<!-- 前台Service 的权限 -->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<service android:name=".seervice.MyService"/>
最后在 Activity 中:
val serviceIntent = Intent(this, MyService::class.java)
// 启动服务
startService(serviceIntent)
// 停止服务
stopService(serviceIntent)
2.启动一个前台服务
我们用 startForegroundService
启动服务. 不要忘记权限;
val serviceIntent = Intent(this, MyService::class.java)
ContextCompat.startForegroundService(this, serviceIntent)
当然也不能忘了通知, 不然几秒后会 ANR;
在 onStartCommand()
时显示通知, onDestroy()
时移除通知
class MyService : Service() {
val TAG = "MyService"
override fun onCreate() {
super.onCreate()
Log.d(TAG, "onCreate")
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
Log.d(TAG, "onStartCommand")
// 通知栏显示通知
showNotification()
return super.onStartCommand(intent, flags, startId)
}
override fun onBind(intent: Intent?): IBinder? = null
override fun onUnbind(intent: Intent?): Boolean {
Log.d(TAG, "onUnbind")
return super.onUnbind(intent)
}
override fun onDestroy() {
super.onDestroy()
Log.d(TAG, "onDestroy")
// 移除通知
stopForeground(true)
}
private fun showNotification(){
val mNotificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
val name = "我的应用"
val channelId = "my_channel_01"
val resultIntent = Intent(this, TestServiceActivity::class.java)
val resultPendingIntent = PendingIntent.getActivity(this,0, resultIntent, PendingIntent.FLAG_UPDATE_CURRENT)
val notification = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val mChannel = NotificationChannel(channelId, name, NotificationManager.IMPORTANCE_LOW)
mNotificationManager.createNotificationChannel(mChannel)
Notification.Builder(this, channelId)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle("我是标题")
.setContentText("我是内容")
.setContentIntent(resultPendingIntent)
.build()
}else{
NotificationCompat.Builder(this, channelId)
.setSmallIcon(R.mipmap.ic_launcher)
.setContentTitle("我是标题")
.setContentText("我是内容")
.setContentIntent(resultPendingIntent)
.setDefaults(Notification.DEFAULT_ALL)
.build()
}
startForeground(1, notification)
}
}
3.绑定服务后通信
首先我们改造 Service: 自定义一个 Binder
子类, 在 onBind()
函数中返回;
class MyService : Service() {
private var mData: String? = null
override fun onBind(intent: Intent?): IBinder = MyBinder()
inner class MyBinder : Binder() {
fun setData(data: String): Boolean {
mData = data
return mData == "设置数据"
}
}
}
然后 定义一个 ServiceConnection
子类;
class TestServiceActivity : ...{
private var mBindService: MyService.MyBinder? = null
private var mConn: MyConnection? = null
...
// 点击事件
fun onClick(v: View?){
when(v?.id){
R.id.tv_two -> { // 绑定
mConn = MyConnection()
bindService(Intent(this, MyService::class.java), mConn!!, Context.BIND_AUTO_CREATE)
}
R.id.tv_three -> mConn?.let { unbindService(it) } // 解绑
R.id.tv_four -> { val success = mBindService?.setData("设置数据") } // 通信
}
}
...
inner class MyConnection : ServiceConnection {
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
mBindService = service as MyService.MyBinder
}
override fun onServiceDisconnected(name: ComponentName?) {}
}
}
总结
没啥总结
补充:
IntentService
? 异步任务的后台Service, 源码实现是通过 HandlerThread+Handler
;
已经过时了. 如今官方建议使用 JetPack
组件中的 WorkManager
或者 JobIntentService
类代替它。
上一篇: Activity: 三、Android Result API
下一篇: Service: 二、简单的音乐播放器