You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Kotlin Flow问题:ViewModel中collect代码块未执行

ViewModel中Flow的collect代码块未执行问题排查

我无法理解为何ViewModel中的collect代码块未执行,此前项目中从未出现该问题。函数本身运行正常,但无法收集返回结果。尝试使用onEach+launchIn的方式收集Flow,结果依旧无效,日志仅输出collect外部的内容。

相关代码

ViewModel代码

fun sendOtp(activity: Activity) {
    viewModelScope.launch {
        Timber.tag("View Model - Outside Collect").i("Outside Collect")
        authUseCase.sendOtp(activity, _authState.value.phoneNumber).collect { result ->
            Timber.tag("View Model - Inside Collect").i("$result")
            when (result) {
                is Resource.Success ->
                    Timber.tag("VIEW MODEL - SUCCESS").i("${result.message} | ${result.data}")

                is Resource.Error ->
                    Timber.tag("VIEW MODEL - ERROR").e("${result.message} | ${result.data}")

                is Resource.Loading ->
                    Timber.tag("VIEW MODEL - LOADING").d("${result.message} | ${result.data}")
            }
        }
    }
}

UseCase代码

class SendOtpImpl @Inject constructor(
    private val authRepository: AuthRepository
): SendOtp {
    override operator fun invoke(activity: Activity, phoneNumber: String): Flow<Resource<String>> = flow {
        authRepository.sendOtp(activity, phoneNumber).onStart {
            emit(Resource.Loading("OTP Code will be sent to +$phoneNumber"))
        }.collect()
    }
} 

Repository代码

override fun sendOtp(activity: Activity, phoneNumber: String): Flow<Resource<String>> = flow {
    val options = _authBuilder
        .setActivity(activity)
        .setPhoneNumber("$phoneNumber")
        .build()

    PhoneAuthProvider.verifyPhoneNumber(options)
    emit(_callbacksState.value)
    Timber.tag("Repository").d("${_callbacksState.value.data}")
}

尝试的另一种收集方式

authUseCase.sendOtp(activity, _authState.value.phoneNumber).onEach { result ->
    when (result) {
        is Resource.Success ->
            Timber.tag("VIEW MODEL VERIFY - SUCCESS").i("${result.message} | ${result.data}")
        is Resource.Error ->
            Timber.tag("VIEW MODEL VERIFY - ERROR").e("${result.message} | ${result.data}")
        is Resource.Loading ->
            Timber.tag("VIEW MODEL VERIFY - LOADING").d("${result.message} | ${result.data}")
    }
}.launchIn(viewModelScope)

问题根源

1. UseCase的Flow未传递数据

SendOtpImpl中创建的Flow仅内部收集了Repository的Flow,但未将收集到的结果发射到外层Flow。这导致UseCase返回的Flow全程没有输出任何数据,ViewModel的collect代码块自然不会触发。

2. Repository的异步逻辑处理错误

PhoneAuthProvider.verifyPhoneNumber是异步操作,调用后立刻emit(_callbacksState.value)时,回调尚未执行,此时_callbacksState大概率是初始状态。且如果_callbacksState不是可观察的Flow(如StateFlow),后续回调更新的值也无法被上层感知。


修复方案

修复UseCase

使用emitAll替代内部collect,将Repository Flow的所有发射值传递到外层:

class SendOtpImpl @Inject constructor(
    private val authRepository: AuthRepository
): SendOtp {
    override operator fun invoke(activity: Activity, phoneNumber: String): Flow<Resource<String>> = 
        authRepository.sendOtp(activity, phoneNumber)
            .onStart { emit(Resource.Loading("OTP Code will be sent to +$phoneNumber")) }
}

修复Repository

将_callbacksState改为MutableStateFlow,在PhoneAuth回调中更新它,并通过emitAll暴露状态变化:

private val _callbacksState = MutableStateFlow<Resource<String>>(Resource.Idle)
val callbacksState: StateFlow<Resource<String>> = _callbacksState

override fun sendOtp(activity: Activity, phoneNumber: String): Flow<Resource<String>> = flow {
    val options = _authBuilder
        .setActivity(activity)
        .setPhoneNumber("$phoneNumber")
        .setCallbacks(object : PhoneAuthProvider.OnVerificationStateChangedCallbacks() {
            override fun onVerificationCompleted(credential: PhoneAuthCredential) {
                _callbacksState.value = Resource.Success("Verification completed", credential.smsCode)
            }

            override fun onVerificationFailed(e: FirebaseException) {
                _callbacksState.value = Resource.Error(e.message ?: "Verification failed", null)
            }

            override fun onCodeSent(verificationId: String, token: PhoneAuthProvider.ForceResendingToken) {
                _callbacksState.value = Resource.Success("Code sent", verificationId)
            }
        })
        .build()

    PhoneAuthProvider.verifyPhoneNumber(options)
    emitAll(callbacksState)
}

验证ViewModel逻辑

修复上述问题后,ViewModel的两种收集方式均可正常触发,无需额外修改。

内容的提问来源于stack exchange,提问作者Raffly

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 14:22:10