**Service服务:**不需要和用户交互,且需要长期运行任务的解决方案。负责后台任务,比如播放音乐,socket长连接
Service启动后默认是运行在主线程中,在执行具体耗时任务过程中要手动开启子线程,应用程序进程被杀死,所有依赖该进程的Service服务也会停止运行。
图片和部分文字转载于https://www.songyubao.com/book/primary/activity/service.html
1.Service启动方式与生命周期
Service启动方式分为两种,普通启动startService 、绑定启动bindService
1.1 普通启动startService()
一般用于创建一个长时间持续运行的后台任务的时候才会使用,比如说socket,文件上传下载服务
普通启动startService(),它的生命周期和应用程序的生命周期一样长,只要应用程序不被杀死,服务就会一直运行 ,除非我们使用stopService()
①首次启动会创建一个Service实例,依次调用onCreate()和onStartCommand()方法,此时Service 进入运行状态
②如果再次调用StartService启动Service,将不会再创建新的Service对象, 系统会直接复用前面创建的Service对象,调用它的onStartCommand()方法!
③这样的Service与它的调用者无必然的联系,就是说当调用者结束了自己的生命周期, 但是只要不调用stopService,那么Service还是会继续运行的!
④无论启动了多少次Service,只需调用一次StopService即可停掉Service
定义Service服务
class TestService1 : Service(){
private var TAG = "TestService1"
override fun onCreate() {
super.onCreate()
Log.e(TAG,"onCreate")
}
override fun onBind(p0: Intent?): IBinder? {
Log.e(TAG,"onBind")
return null
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
Log.e(TAG,"onStartCommand")
return super.onStartCommand(intent, flags, startId)
}
override fun onDestroy() {
Log.e(TAG,"onDestroy")
super.onDestroy()
}
}
AndroidManifest.xml完成Service注册
<application>
<service android:name=".TestService1"/>
</application>
在Avtivity中StartService启动服务 (要在AndroidManifest注册)
private lateinit var binding : ActivityTestServiceBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityTestServiceBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.startService.setOnClickListener {
val intent = Intent(this , TestService1::class.java)
startService(intent)
}
binding.stopService.setOnClickListener {
val intent = Intent(this , TestService1::class.java)
stopService(intent)
}
}
日志输出与结果分析
对于使用startService的方式而言,onStartCommand 就是我们用于做后台任务的地方,如果我们多次点击startService按钮,会直接调onStartCommand ,而不再回调onCreate
2021-11-11 22:37:16.274 6417-6417/com.example.componentlearn E/TestService1: onCreate
2021-11-11 22:37:16.275 6417-6417/com.example.componentlearn E/TestService1: onStartCommand
2021-11-11 22:37:20.167 6417-6417/com.example.componentlearn E/TestService1: onStartCommand
2021-11-11 22:37:21.579 6417-6417/com.example.componentlearn E/TestService1: onStartCommand
2021-11-11 22:37:22.612 6417-6417/com.example.componentlearn E/TestService1: onDestroy
2021-11-11 22:37:24.957 6417-6417/com.example.componentlearn E/TestService1: onCreate
2021-11-11 22:37:24.959 6417-6417/com.example.componentlearn E/TestService1: onStartCommand
1.2绑定启动bindService()
运行一些和Activity生命周期相等的后台任务,如跨进程的通信
①当首次使用bindService()启动一个Service时,系统会实例化一个Service实例,并调用其**onCreate()和onBind()**方法,然后调用者就可以通过返回的IBinder对象和Service进行交互了,此后如果我们再次使用bindService绑定Service,系统不会创建新的Sevice实例,也不会再调用onBind()方法,只会直接把IBinder对象返回给调用方
②如果我们解除与服务的绑定,只需调用unbindService(),此时onUnbind和onDestory方法将会被调用
③bindService启动的Service服务是与调用者(Activity)相互关联的,可以理解为 “一条绳子上的蚂蚱”,要死一起死,在bindService后,一旦调用者(Activity)销毁,那么Service也立即终止
定义Service服务
class TestService2 : Service(){
private var TAG = "TestService2"
private var count = 0
private var quit = false
override fun onCreate() {
super.onCreate()
Log.e(TAG,"onCreate")
Thread(Runnable {
while(true){
if(quit)
break
Thread.sleep(1000)
count++
}
}).start()
}
private val binder = MyBinder()
inner class MyBinder : Binder(){
fun getCount() : Int{
return count
}
}
override fun onBind(p0: Intent?): IBinder? {
Log.e(TAG,"onBind")
return binder
}
override fun onUnbind(intent: Intent?): Boolean {
Log.e(TAG,"onUnbind")
quit = true
return super.onUnbind(intent)
}
override fun onDestroy() {
Log.e(TAG,"onDestroy")
super.onDestroy()
}
}
AndroidManifest.xml中注册服务
<application>
<service android:name=".TestService2"/>
</application>
在Activity中bindService启动服务
class TestServiceActivity : Activity(){
private lateinit var binding : ActivityTestServiceBinding
private var connection: ServiceConnection?=null
private var myBinder: TestService2.MyBinder? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityTestServiceBinding.inflate(layoutInflater)
setContentView(binding.root)
connection = object:ServiceConnection{
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
Log.e("TestService2","--------------Service Connected--------------")
myBinder = service as TestService2.MyBinder
}
override fun onServiceDisconnected(p0: ComponentName?) {
Log.e("TestService2","--------------Service Disconnected--------------")
}
}
val intent = Intent(this , TestService2::class.java)
bindService(intent , connection!! , Context.BIND_AUTO_CREATE)
binding.startService.setOnClickListener {
Log.e("TestService2","*********** getCount:=${myBinder?.getCount()} ***********")
}
binding.stopService.setOnClickListener {
unbindService(connection!!)
}
}
override fun onDestroy() {
super.onDestroy()
unbindService(connection!!)
}
}
日志输出与结果分析
可以在onCreate() ,onBind() 开启耗时任务如线程
2021-11-13 11:25:33.264 5247-5247/com.example.componentlearn E/TestService2: onCreate
2021-11-13 11:25:33.266 5247-5247/com.example.componentlearn E/TestService2: onBind
2021-11-13 11:25:34.069 5247-5247/com.example.componentlearn E/TestService2: --------------Service Connected--------------
2021-11-13 11:25:34.976 5247-5247/com.example.componentlearn E/TestService2: *********** getCount:=1 ***********
2021-11-13 11:25:36.415 5247-5247/com.example.componentlearn E/TestService2: *********** getCount:=3 ***********
2021-11-13 11:25:37.134 5247-5247/com.example.componentlearn E/TestService2: *********** getCount:=3 ***********
2021-11-13 11:25:38.562 5247-5247/com.example.componentlearn E/TestService2: *********** getCount:=5 ***********
2021-11-13 11:25:43.267 5247-5247/com.example.componentlearn E/TestService2: onUnbind
2021-11-13 11:25:43.267 5247-5247/com.example.componentlearn E/TestService2: onDestroy
使用BindService绑定Service,依次调用onCreate(),onBind()方法, 我们可以在onBind()方法中返回自定义的IBinder对象;再接着调用的是 ServiceConnection的onServiceConnected()方法该方法中可以获得 IBinder对象,从而进行相关操作;当Service解除绑定后会自动调用 onUnbind和onDestroyed方法,当然绑定多客户端情况需要解除所有 的绑定才会调用onDestoryed方法进行销毁哦
2.Android 8.0及以上不允许后台启动Service服务
Android 8.0 还对特定函数做出了以下变更:
- 如果针对 Android 8.0 的应用尝试在不允许其创建后台服务的情况下使用
startService() 函数,则该函数将引发一个 IllegalStateException 。 - 新的
Context.startForegroundService() 函数将启动一个前台服务。现在,即使应用在后台运行,系统也允许其调用 Context.startForegroundService() 。不过,应用必须在创建服务后的五秒内调用该服务的 startForeground() 函数。
软件不可见后台超过60s不可启动service
2021-11-14 14:24:34.213 3846-3846/com.example.componentlearn E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.componentlearn, PID: 3846
java.lang.IllegalStateException: Not allowed to start service Intent { cmp=com.example.componentlearn/.TestService2 }: app is in background uid UidRecord{89692e9 u0a121 LAST bg:+1m7s709ms idle change:cached procs:1 seq(0,0,0)}
at android.app.ContextImpl.startServiceCommon(ContextImpl.java:1715)
AndroidManifest.xml声明权限
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
服务启动兼容写法
TestServiceActivity 的 onCreate()
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
startForegroundService(intent)
}else{
startService(intent)
}
TestService2中的onCreate
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val notification = Notification.Builder(applicationContext, "channel_id").build()
startForeground(1,notification)
}
Bug记录
- 跳转intent的class写成service 应该是activity的class
- bindService()无法绑定上服务。 原因:TestService2的
onBind() 返回的是null应该是binder
|