Android主线程同步获取Firebase RemoteConfig如何避免死锁?
在Android中同步获取Firebase RemoteConfig并避免死锁
问题分析
你当前的实现用runBlocking在Application的onCreate中阻塞主线程,尝试同步获取RemoteConfig,但这会导致死锁:Firebase的fetchAndActivate()内部的Task完成回调依赖主线程的消息循环处理,而runBlocking已经占用了主线程,无法处理回调消息,最终await()永远无法等到任务完成,形成死锁。同时,这种阻塞主线程的方式还可能触发ANR(应用无响应)。
推荐解决方案:使用启动屏(Splash Activity)
不要在Application的onCreate中阻塞主线程,而是创建一个启动屏Activity,在后台异步完成RemoteConfig初始化,完成后再启动主Activity。这种方式既避免了主线程阻塞,又能确保主Activity启动前完成必要的配置初始化。
示例代码:
class SplashActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_splash) lifecycleScope.launch { // 初始化RemoteConfig val remoteConfig = Firebase.remoteConfig // 配置RemoteConfig参数(可选,设置最小拉取间隔) val configSettings = FirebaseRemoteConfigSettings.Builder() .setMinimumFetchIntervalInSeconds(3600) .build() remoteConfig.setConfigSettingsAsync(configSettings).await() // 同步拉取并激活配置 val isSuccess = remoteConfig.fetchAndActivate().await() // 完成依赖注入初始化 initDependencyInjection(remoteConfig) // 启动主Activity并关闭启动屏 startActivity(Intent(this@SplashActivity, MainActivity::class.java)) finish() } } private fun initDependencyInjection(remoteConfig: FirebaseRemoteConfig) { // 这里执行依赖注入相关的初始化逻辑 } }
配置注意事项
- 在
AndroidManifest.xml中将SplashActivity设置为启动Activity:
<activity android:name=".SplashActivity" android:theme="@style/SplashTheme"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".MainActivity" />
- 给
SplashActivity设置一个简洁的启动主题,避免界面闪烁。
备选方案:使用CountDownLatch(不推荐)
如果必须在Application的onCreate中处理,可通过子线程执行RemoteConfig拉取,并用CountDownLatch同步等待,但仍有ANR风险,仅作应急方案:
class MyApplication : Application() { lateinit var remoteConfig: FirebaseRemoteConfig override fun onCreate() { super.onCreate() val latch = CountDownLatch(1) // 在子线程执行RemoteConfig初始化 Thread { remoteConfig = Firebase.remoteConfig remoteConfig.fetchAndActivate().addOnCompleteListener { task -> if (task.isSuccessful) { initDependencyInjection() } latch.countDown() // 通知主线程任务完成 } }.start() try { // 设置超时时间,避免无限等待(建议不超过5秒) latch.await(5, TimeUnit.SECONDS) } catch (e: InterruptedException) { e.printStackTrace() } } private fun initDependencyInjection() { // 依赖注入初始化逻辑 } }
死锁原因详解
你的原代码中,runBlocking在主线程执行,它会占用主线程并阻塞消息循环。而Firebase的fetchAndActivate()返回的Task,其完成回调默认会在调用线程(主线程)的Looper中执行,但主线程被runBlocking占用,无法处理回调消息,导致await()永远等待Task完成,最终形成死锁。
内容的提问来源于stack exchange,提问作者Baterka
相关产品推荐
相关产品推荐

