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

如何在ViewModel类中传递Activity上下文?DI框架能否避免内存泄漏?

在ViewModel中安全处理Firebase认证的Activity上下文问题

针对你遇到的Firebase signInWithEmailAndPassword()需要Activity上下文,同时要避免ViewModel内存泄漏的问题,以下是几种可行的解决方案:

一、最优方案:ViewModel只负责发起请求,UI层处理回调

ViewModel的核心职责是处理业务逻辑,不应该持有任何UI层的引用(比如Activity上下文)。正确的做法是让ViewModel发起认证请求,通过可观察对象把结果传递给Activity/Fragment,由后者在自身上下文中处理回调。

修改后的ViewModel代码

class LoginViewModel : ViewModel() {
    private val auth = FirebaseAuth.getInstance()
    // 用StateFlow暴露认证结果,UI层可以观察
    private val _signInResult = MutableStateFlow<Result<AuthResult>?>(null)
    val signInResult: StateFlow<Result<AuthResult>?> = _signInResult

    fun signIn(email: String, password: String) {
        auth.signInWithEmailAndPassword(email, password)
            .addOnCompleteListener { task ->
                _signInResult.value = when {
                    task.isSuccessful -> Result.success(task.result!!)
                    else -> Result.failure(task.exception!!)
                }
            }
    }
}

Activity中的实现

class LoginActivity : AppCompatActivity() {
    private val viewModel: LoginViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // 初始化视图...

        lifecycleScope.launch {
            viewModel.signInResult.collect { result ->
                result?.let {
                    // 这里在Activity上下文内处理结果
                    if (it.isSuccess) {
                        // 登录成功,跳转主页等逻辑
                    } else {
                        // 提示登录失败,显示错误信息
                    }
                }
            }
        }
    }
}

这种方式完全规避了ViewModel持有上下文的问题,完全符合MVVM架构设计,也是最推荐的做法。

二、用Dagger/Hilt安全注入上下文(如果必须在ViewModel内处理回调)

如果你的业务逻辑确实需要在ViewModel内处理认证回调,Hilt可以帮你安全地注入Activity上下文,同时避免内存泄漏。关键是要用弱引用包装上下文,或者利用Hilt的生命周期绑定特性。

Hilt实现代码

首先确保你的项目已经配置好Hilt(添加依赖、@HiltAndroidApp、@AndroidEntryPoint注解),然后修改ViewModel:

@HiltViewModel
class LoginViewModel @Inject constructor(
    private val auth: FirebaseAuth,
    @ActivityContext private val context: Context
) : ViewModel() {
    // 用弱引用持有Activity,避免强引用导致内存泄漏
    private val activityRef = WeakReference(context as Activity)

    fun signIn(email: String, password: String) {
        activityRef.get()?.let { activity ->
            auth.signInWithEmailAndPassword(email, password)
                .addOnCompleteListener(activity) { task ->
                    // 在这里处理认证结果
                }
        }
    }
}

当Activity被销毁后,WeakReference不会阻止垃圾回收机制回收Activity实例,因此不会产生内存泄漏。Hilt的@ActivityContext会自动注入当前关联的Activity上下文,并且在Activity销毁时,对应的依赖实例会被清理。

三、关于AndroidViewModel的补充说明

你提到的AndroidViewModel获取ApplicationContext的方案,其实是可以正常运行的——ApplicationContext的生命周期和应用一致,不会因屏幕旋转等配置变更而销毁,用来添加OnCompleteListener完全没问题。但这种方式违背了ViewModel不持有上下文的设计原则,会让ViewModel和UI层产生不必要的耦合,所以只适合临时快速实现,不推荐在正式项目中使用。

代码示例:

class LoginViewModel(application: Application) : AndroidViewModel(application) {
    private val auth = FirebaseAuth.getInstance()

    fun signIn(email: String, password: String) {
        auth.signInWithEmailAndPassword(email, password)
            .addOnCompleteListener(getApplication()) { task ->
                // 处理结果
            }
    }
}

总结

优先选择第一种方案,完全遵循MVVM架构,从根源上避免内存泄漏;如果必须在ViewModel内处理回调,第二种Hilt+弱引用的方式是安全可靠的选择。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.21 05:07:09