使用协程时FirebaseMessagingService.onMessageReceived是否需要runBlocking
问题解答
1. runBlocking是必须使用的吗?
不是必须的。onMessageReceived默认运行在主线程,如果你不需要等待同步任务完成再结束该方法,直接调用someSyncer.sync()将任务抛到自定义协程作用域执行即可,完全不需要引入runBlocking。只有当你需要阻塞当前线程,等待同步任务执行完毕再返回onMessageReceived时,才需要用到runBlocking。
2. 我能否不做重构,在保留自定义作用域运行逻辑的同时让runBlocking正常生效?
可以,仅需对SomeSyncer做无侵入的极小改动即可,原有逻辑完全不用调整:
将sync()方法的返回值改为执行任务的Job:
fun sync(): Job { return scope.launch { _state.value = RUNNING doSync() _state.value = IDLE } }
需要阻塞等待时,在FcmService中这样调用即可:
override fun onMessageReceived(remoteMessage: RemoteMessage) { runBlocking { someSyncer.sync().join() } }
其他场景原有调用sync()的逻辑不受任何影响,不需要额外适配。
3. 如果直接使用第一种写法,将任务抛到自定义作用域执行,是否会破坏wakelocks?(同步任务最多耗时1-2秒)
存在任务中断的风险。Firebase SDK会在onMessageReceived执行结束后自动释放持有的wakelock,如果你把任务丢到自定义作用域后直接返回方法,很可能同步任务还没执行完,wakelock就被释放,设备进入休眠导致任务中断。
针对1-2秒的短耗时同步任务,有两种稳妥的解决方案:
- 若要继续使用自定义作用域执行的方案,在调用
sync()前自行申请一个短时长的wakelock,同步任务执行完成后再手动释放 - 直接使用上述
runBlocking + join的写法,阻塞等待任务完成再返回onMessageReceived,靠Firebase自身持有的wakelock保证任务执行完成,1-2秒的主线程阻塞不会触发ANR(Android主线程阻塞5秒以上才会触发ANR)
4. 是否需要对外暴露suspend函数的公共API?我不太想做这个改动,因为除了该场景外没有其他使用需求。
不需要。上述让sync()返回Job的改动非常轻量,也不会破坏原有API的调用方式,完全不需要对外暴露suspend函数就能满足当前场景需求。如果后续没有其他协程调用的场景,维持现有普通函数的API设计即可。
内容的提问来源于stack exchange,提问作者urSus
相关产品推荐
相关产品推荐

