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

Jetpack Compose:如何在非可组合函数(ViewModel)中获取Context

在ViewModel中获取Context的正确方式

ViewModel设计初衷是避免持有UI层引用(比如Activity/Fragment的Context),防止内存泄漏,但如果确实需要Context访问全局资源、SharedPreferences等,推荐以下两种安全方案:

1. 使用AndroidViewModel(推荐)

AndroidViewModel是ViewModel的子类,专为Android环境设计,构造函数接收Application实例——而Application本身就是全局Context,生命周期与应用一致,不会引发内存泄漏。

修改你的LoginViewModel代码,改为继承AndroidViewModel并获取Application Context:

import android.app.Application
import android.content.Context
import androidx.lifecycle.AndroidViewModel

class LoginViewModel(
    application: Application,
    private val _global: GlobalServices = GlobalServices(),
    private val _login: LoginModel = LoginModel(),
    private val _loginService: LoginService = LoginService(),
    private val retrofitService: IRetrofitService = RetrofitService(),
    // 若Storage需要Context,直接传入Application级Context
    private val storage: Storage = Storage(application.applicationContext)
) : AndroidViewModel(application) {

    // 全局Context,随时可调用
    private val appContext: Context get() = getApplication<Application>().applicationContext

    // 原有属性保持不变
    val paddingMain = 10.dp
    val isChecked = mutableStateOf(false)
    val disabled = mutableStateOf(true)
    val iconSize = 150.dp
    val signInFail = mutableStateOf(false)
    val showSuccess = mutableStateOf(false)

    val global = _global
    val login = mutableStateOf(_login, policy = neverEqualPolicy())

    fun setEmail(email: String) {
        login.value.correo = email
        refreshStateOfLogin()
    }

    fun setPassword(ppassword: String) {
        login.value.password = ppassword
        refreshStateOfLogin()
    }

    private fun refreshStateOfLogin() {
        // 示例:用Context访问资源
        val errorMsg = appContext.getString(R.string.login_error)
        // 其他业务逻辑
    }
}

在Compose中创建ViewModel时,viewModel()会自动处理Application参数传递:

@Composable
fun LoginScreen() {
    val viewModel: LoginViewModel = viewModel()
    // 你的Compose UI代码
}

2. 构造函数传入Application级Context(备选)

如果不想使用AndroidViewModel,可以在ViewModel构造中直接传入Application或applicationContext,但必须确保传入的是全局Context,绝对不能传入Activity/Fragment的Context(否则会因持有UI组件引用导致内存泄漏)。

修改后的ViewModel示例:

class LoginViewModel(
    private val appContext: Context,
    private val _global: GlobalServices = GlobalServices(),
    private val _login: LoginModel = LoginModel(),
    private val _loginService: LoginService = LoginService(),
    private val retrofitService: IRetrofitService = RetrofitService(),
    private val storage: Storage = Storage(appContext)
) : ViewModel() {

    // 原有属性和方法保持不变
    // ...
}

在Compose中创建时,从LocalContext获取Application实例:

@Composable
fun LoginScreen() {
    val context = LocalContext.current
    val viewModel: LoginViewModel = remember {
        LoginViewModel(context.applicationContext)
    }
    // 你的Compose UI代码
}

关键注意事项

  • 绝对不要在ViewModel中持有Activity或Fragment的Context,这类Context生命周期与UI组件绑定,ViewModel销毁时机晚于UI组件,会导致内存泄漏。
  • 优先使用AndroidViewModel,它是Jetpack提供的标准方案,能避免手动管理Context生命周期的风险。
  • 若依赖(如Storage)需要Context,尽量在构造时传入Application级Context,保持依赖清晰且安全。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 20:01:07