已在onCreate调用bindLauncher仍触发BindLauncherException问题排查
为什么部分设备会触发BindLauncherException?
可能的触发原因
旧委托实例被延迟任务持有
当Activity因配置变化(如屏幕旋转)或系统回收被销毁时,旧的ActivityPermissionLauncher实例会在ON_DESTROY回调中清空_activity引用。如果此时有未完成的异步任务、延迟操作(比如Handler.postDelayed)或点击事件持有了旧的委托实例引用,后续调用updatePermissionsStatus时,就会因为_activity为空触发异常。生命周期执行顺序异常(厂商定制系统)
部分Android厂商的定制系统可能存在生命周期回调顺序不符合标准的情况,比如在bindLauncher执行完成前,就触发了调用updatePermissionsStatus的逻辑。进程恢复时的状态不一致
当应用进程被系统杀死后恢复,虽然Activity会重建并调用onCreate执行bindLauncher,但极端情况下可能存在委托实例状态未同步的问题,导致_activity为空。
解决方案
1. 安全处理空实例,避免抛出异常
将updatePermissionsStatus改为先检查_activity是否为空,为空则直接返回(或记录日志),而非抛出异常。这能覆盖Activity已销毁等合法场景:
class ActivityPermissionLauncher : PermissionLauncher<FragmentActivity>, LifecycleEventObserver { private var _activity: FragmentActivity? = null override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) { when (event) { Lifecycle.Event.ON_DESTROY -> { _activity?.lifecycle?.removeObserver(this) _activity = null } else -> {} } } override fun updatePermissionsStatus() { val currentActivity = _activity ?: return // 使用currentActivity执行权限状态更新逻辑 } override fun bindLauncher(launcher: FragmentActivity) { // 先移除旧的观察者,避免重复监听 _activity?.lifecycle?.removeObserver(this) _activity = launcher launcher.lifecycle.addObserver(this) } }
2. 区分开发/生产环境的异常提醒
如果需要保留对开发者的提醒,可在Debug环境下抛出异常,Release环境下静默处理:
private val activity get() = _activity ?: run { if (BuildConfig.DEBUG) { throw BindLauncherException() } else { null } } override fun updatePermissionsStatus() { val currentActivity = activity ?: return // 执行权限更新逻辑 }
3. 清理延迟任务与引用
在Activity的onDestroy中,取消所有可能持有委托实例的异步任务、延迟操作,移除控件的监听引用:
class PrivacyAndTermActivity : FragmentActivity(), PermissionLauncher<FragmentActivity> by ActivityPermissionLauncher() { private val handler = Handler(Looper.getMainLooper()) private var pendingRunnable: Runnable? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) bindLauncher(this) myButton.setOnClickListener { pendingRunnable = Runnable { updatePermissionsStatus() } handler.postDelayed(pendingRunnable!!, 1000) } } override fun onDestroy() { super.onDestroy() pendingRunnable?.let { handler.removeCallbacks(it) } myButton.setOnClickListener(null) } }
内容的提问来源于stack exchange,提问作者Saman Sattari
相关产品推荐
相关产品推荐

