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

Kotlin中无法创建SharedPrefViewModel实例问题求助

问题:无法创建SharedPrefViewModel实例的原因及解决办法

java.lang.RuntimeException: Cannot create an instance of class com.meetme.viewmodel.SharedPrefViewModel

相关代码

SharedPrefViewModel类

class SharedPrefViewModel(context: Context): ViewModel() {
    private var sharedPrefRepository: SharedPrefRepository = SharedPrefRepository(context.applicationContext)
 
    fun putString(name: String) {
        sharedPrefRepository.putString(name)
    }
    fun getString() : String? {
        return sharedPrefRepository.getString() // 错误发生在此处
    }
}

SharedPrefRepository类

class SharedPrefRepository(context: Context) {
    private val pref: SharedPreferences =
        context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE)

    var name: String?
    get() = pref.getString(PREF_LOGGED_IN, "")
    set(value) = pref.edit().putString(PREF_LOGGED_IN, value).apply()
    
    fun putString(name: String) {
        this.name = name
    }

    fun getString() : String? {
        return this.name
    }
}

保存名称的Composable

@Composable
fun BasicInfoScreen(navController: NavHostController) {
    var context = LocalContext.current.applicationContext
    val sharedPrefViewModel = SharedPrefViewModel(context)

    Column(
        modifier = Modifier
            .fillMaxSize()
            .background(Color.White)
            .padding(16.dp)
    ) {
        ...
        Button(
            onClick = {
                sharedPrefViewModel.putString("default") // 在此处调用
                navController.navigate(RootGraphItem.Location.route)
            },
            modifier = Modifier
                .fillMaxWidth()
                .height(50.dp)
                .background(Color(0xFF0090FF)),
            colors = ButtonDefaults.buttonColors(backgroundColor = Color(0xFF0090FF))
        ) {
            Text(text = "Next", style = TextStyle(color = Color.White))
        }
    }
}

获取偏好值的Composable

@Composable
fun LocationScreen(navController: NavHostController) {
    val sharedPrefViewModel: SharedPrefViewModel = viewModel()

    println(sharedPrefViewModel.getString()) // 获取名称

    Column(
        modifier = Modifier
            .fillMaxSize()
            .background(Color.White)
            .padding(16.dp)
    ) {
       ...
    }
}

问题原因

  1. ViewModel实例化逻辑冲突:

    • 在BasicInfoScreen中手动通过构造函数创建SharedPrefViewModel实例,违背Jetpack ViewModel的管理规范;
    • 在LocationScreen中使用viewModel()方法获取实例时,系统会尝试调用ViewModel的无参构造函数,但你的SharedPrefViewModel仅提供带Context参数的构造函数,导致实例化失败抛出异常。
  2. ViewModel持有Context的风险:
    ViewModel直接持有Context可能引发内存泄漏,不符合组件设计原则。

解决步骤

步骤1:修改ViewModel继承类

将SharedPrefViewModel改为继承AndroidViewModel,它自带Application上下文,无需手动传递:

class SharedPrefViewModel(application: Application) : AndroidViewModel(application) {
    private var sharedPrefRepository: SharedPrefRepository = SharedPrefRepository(application)
 
    fun putString(name: String) {
        sharedPrefRepository.putString(name)
    }
    fun getString() : String? {
        return sharedPrefRepository.getString()
    }
}

步骤2:统一ViewModel获取方式

所有Composable中都使用viewModel()方法获取ViewModel实例,禁止手动构造:
修改BasicInfoScreen中的代码:

@Composable
fun BasicInfoScreen(navController: NavHostController) {
    val sharedPrefViewModel: SharedPrefViewModel = viewModel()

    Column(
        modifier = Modifier
            .fillMaxSize()
            .background(Color.White)
            .padding(16.dp)
    ) {
        ...
        Button(
            onClick = {
                sharedPrefViewModel.putString("default")
                navController.navigate(RootGraphItem.Location.route)
            },
            modifier = Modifier
                .fillMaxWidth()
                .height(50.dp)
                .background(Color(0xFF0090FF)),
            colors = ButtonDefaults.buttonColors(backgroundColor = Color(0xFF0090FF))
        ) {
            Text(text = "Next", style = TextStyle(color = Color.White))
        }
    }
}

步骤3:优化Repository(可选)

去掉冗余的包装方法,直接使用属性访问简化代码:

class SharedPrefRepository(context: Context) {
    private val pref: SharedPreferences =
        context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE)

    var name: String?
        get() = pref.getString(PREF_LOGGED_IN, "")
        set(value) = pref.edit().putString(PREF_LOGGED_IN, value).apply()
}

对应ViewModel可同步简化:

class SharedPrefViewModel(application: Application) : AndroidViewModel(application) {
    private val sharedPrefRepository = SharedPrefRepository(application)
 
    fun saveName(name: String) {
        sharedPrefRepository.name = name
    }
    fun getName() : String? {
        return sharedPrefRepository.name
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 10:12:48