Android绑定服务中用mBound检测绑定状态的方案是否为不良实践?
mBound标记服务绑定状态会在异常生命周期下出问题? 这个问题戳中了官方绑定服务教程里一个很容易踩的坑!先给你理清楚来龙去脉:
首先,官方常规的做法是维护一个mBound布尔值,在发起绑定后把它设为true,解绑时设为false,以此判断是否需要执行解绑操作。类似这样的代码:
private var mBound = false private lateinit var mService: MyService private val connection = object : ServiceConnection { override fun onServiceConnected(className: ComponentName, service: IBinder) { val binder = service as MyService.LocalBinder mService = binder.getService() mBound = true } override fun onServiceDisconnected(arg0: ComponentName) { mBound = false } } override fun onStart() { super.onStart() Intent(this, MyService::class.java).also { intent -> bindService(intent, connection, Context.BIND_AUTO_CREATE) } mBound = true // 这里就是问题所在! } override fun onStop() { super.onStop() if (mBound) { unbindService(connection) mBound = false } }
当onStop()在onStart()后立即调用时,会发生什么?
核心问题在于:bindService()是异步操作!你调用bindService()只是向系统发起了绑定请求,真正的绑定完成、onServiceConnected()回调触发,需要一点时间。
如果onStop()在bindService()调用后、onServiceConnected()触发前就执行了(比如Activity刚启动就被系统后台回收,或者用户快速切换应用),此时mBound已经被你在onStart()里提前设为true,onStop()就会执行unbindService(connection)——但这时候系统还没完成绑定,这个ServiceConnection根本没被注册到系统里!
直接后果就是抛出IllegalArgumentException,错误信息大概是Service not registered: xxx,这会导致你的应用崩溃。
为什么这是不良实践?
这种写法混淆了两个完全不同的状态:
- “发起了绑定请求”≠“绑定已经成功完成”
mBound应该只用来标记已经成功绑定的状态,而不是“我发起了请求”的状态。提前在onStart()里设为true,本质上是用一个变量承担了两个职责,必然会在异常场景下出问题。
正确的做法是什么?
优化思路很简单:只在ServiceConnection的回调里更新mBound,同时额外维护一个标记追踪绑定请求的状态:
private var mBound = false private var isBindingRequested = false // 追踪是否发起过绑定请求 private lateinit var mService: MyService private val connection = object : ServiceConnection { override fun onServiceConnected(className: ComponentName, service: IBinder) { val binder = service as MyService.LocalBinder mService = binder.getService() mBound = true isBindingRequested = false // 绑定完成,重置请求标记 } override fun onServiceDisconnected(arg0: ComponentName) { mBound = false isBindingRequested = false } } override fun onStart() { super.onStart() Intent(this, MyService::class.java).also { intent -> // bindService返回true表示请求已成功发送 isBindingRequested = bindService(intent, connection, Context.BIND_AUTO_CREATE) } // 这里绝对不要提前设mBound = true! } override fun onStop() { super.onStop() if (mBound) { unbindService(connection) mBound = false } // 如果绑定请求已经发送但还没完成,重置标记避免后续错误 isBindingRequested = false }
另外还要注意:正常解绑服务时,onServiceDisconnected()不会被调用,所以一定要在unbindService()后手动把mBound设为false,避免状态不一致。
内容的提问来源于stack exchange,提问作者Afshin

