主线程死锁引发Firebase Lazy.get ANR问题排查求助
死锁原因分析
从Crashlytics日志可以明确死锁的循环等待关系:
- 主线程(tid=1)在执行
ComponentRuntime.doInitializeEagerComponents时,等待锁<0x0967c3b7>,该锁被后台线程(tid=29,DefaultDispatcher-worker-2)持有。 - 后台线程在调用
FirebaseApp.getInstance时,等待锁<0x055a4724>,该锁被主线程持有。
问题根源在于你的FirebaseAuthAsFlow是Kotlin object单例,它的firebaseAuth成员变量会在第一次访问时(后台协程线程中)触发初始化逻辑,调用FirebaseAuth.getInstance()。而此时主线程正处于Firebase组件的初始化流程(FirebaseApp.initializeAllApis)中,两者互相等待对方持有的锁,最终导致死锁引发ANR。
解决方案
1. 主线程提前初始化FirebaseAuth
在Application的onCreate方法中,完成FirebaseApp.initializeApp后,立即在主线程调用FirebaseAuth.getInstance(),提前完成FirebaseAuth的初始化,避免后台线程触发初始化流程。
修改后的Application代码:
override fun onCreate() { super.onCreate() FirebaseApp.initializeApp(this) // 主线程提前初始化FirebaseAuth,规避后台线程初始化引发的锁竞争 FirebaseAuth.getInstance() // 推荐使用AndroidX提供的applicationScope替代自定义CoroutineScope applicationScope.launch(Dispatchers.Default) { FirebaseAuthAsFlow.getFirebaseUser().collect { it?.let { Log.d("userId", "${it.uid}") } } } }
注意:使用
applicationScope需要添加依赖androidx.lifecycle:lifecycle-process:2.6.2,它的生命周期与Application一致,避免内存泄漏风险。
2. 延迟初始化FirebaseAuth实例
将FirebaseAuthAsFlow中的firebaseAuth改为lazy委托初始化,确保只有在需要时才获取实例,同时因为已经在主线程提前初始化过,这里不会触发新的初始化逻辑。
修改后的FirebaseAuthAsFlow代码:
object FirebaseAuthAsFlow { private val firebaseAuth by lazy { FirebaseAuth.getInstance() } fun getFirebaseUser(): Flow<FirebaseUser?> = callbackFlow { val authStateListener = FirebaseAuth.AuthStateListener { trySend(it.currentUser).isSuccess } firebaseAuth.addAuthStateListener(authStateListener) awaitClose { firebaseAuth.removeAuthStateListener(authStateListener) } } }
额外优化点
- 替换
trySendBlocking为trySend:trySendBlocking可能在主线程回调时阻塞主线程,改用trySend并处理发送结果,避免不必要的阻塞风险。
内容的提问来源于stack exchange,提问作者Chetan Ansel
相关产品推荐
相关产品推荐

