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

如何将DataStore凭证读写逻辑迁移到ViewModel以消除冗余代码

迁移DataStore逻辑到ViewModel的实现方案

核心思路

将DataStore的读写操作全部下沉到数据层UserRepository,所有用户相关的业务逻辑(自动登录、凭证存储、状态管理)全部收敛到ViewModel层,Activity仅负责UI交互和状态监听,彻底剥离Activity中的数据处理逻辑。

步骤1:重构UserRepository,集成DataStore操作

Repository作为数据层统一出入口,负责管理本地+远程的用户数据,首先将DataStore的相关操作封装到这里:

// 先定义DataStore存储的常量Key
val USER = stringSetPreferencesKey("user_info")

class UserRepository(private val userDataStore: DataStore<Preferences>) {
    // 原有远程登录逻辑保持不变
    suspend fun auth(user: User): Resource<Token> {
        // 原有接口请求逻辑
    }

    // 新增:保存用户凭证到本地DataStore
    suspend fun saveUserCredential(user: User) {
        userDataStore.edit { ds ->
            ds[USER] = setOf(user.username, user.password)
        }
    }

    // 新增:从DataStore读取本地存储的用户凭证
    fun getStoredUser(): Flow<User?> {
        return userDataStore.data.map { prefs ->
            prefs[USER]?.takeIf { it.size == 2 }?.let { usrSet ->
                User(usrSet.first(), usrSet.last())
            }
        }
    }
}

步骤2:重构ViewModel,内置自动登录与凭证保存逻辑

ViewModel初始化时自动触发本地凭证读取、尝试自动登录,登录成功后自动存储凭证,不需要上层Activity通知:

class UserViewModel(
    private val userRepository: UserRepository
) : ViewModel() {
    private val _token = MutableStateFlow<Resource<Token>>(Resource.Loading())
    val token: StateFlow<Resource<Token>> = _token
    private val _user = MutableStateFlow<Resource<User>>(Resource.Loading())
    val user: StateFlow<Resource<User>> = _user

    init {
        // 初始化时自动读取本地凭证尝试登录
        viewModelScope.launch {
            userRepository.getStoredUser().collect { storedUser ->
                storedUser?.let {
                    login(it)
                } ?: run {
                    _user.value = Resource.Error("No user yet")
                }
            }
        }
    }

    fun login(user: User) = viewModelScope.launch {
        _token.value = Resource.Loading()
        val authResult = userRepository.auth(user)
        _token.value = authResult
        if (authResult is Resource.Success) {
            _user.value = Resource.Success(user)
            // 登录成功直接保存凭证到本地,无需Activity参与
            userRepository.saveUserCredential(user)
        }
    }
    // 其他原有逻辑保持不变
    ...
}

如果没有使用Hilt等依赖注入框架,需要自定义ViewModel工厂来传入Repository实例,示例如下:

class UserViewModelFactory(private val repository: UserRepository) : ViewModelProvider.Factory {
    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        if (modelClass.isAssignableFrom(UserViewModel::class.java)) {
            @Suppress("UNCHECKED_CAST")
            return UserViewModel(repository) as T
        }
        throw IllegalArgumentException("Unknown ViewModel class")
    }
}

步骤3:精简MainActivity,仅保留UI逻辑

删除所有DataStore相关的定义和收集逻辑,仅监听ViewModel的状态更新UI即可:

class MainActivity : ComponentActivity() {
    // 扩展属性移到Application层级或者在这里定义即可,不需要参与业务逻辑
    private val Context.userPreferencesDataStore: DataStore<Preferences> by preferencesDataStore(
        name = "user"
    )

    private val viewModel: UserViewModel by viewModels {
        UserViewModelFactory(UserRepository(applicationContext.userPreferencesDataStore))
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        lifecycleScope.launchWhenStarted {
            viewModel.user.collect { userState ->
                // 仅处理UI逻辑:登录成功跳主页/加载中展示加载框/错误时提示用户跳登录页
                when(userState) {
                    is Resource.Success -> { /* 登录成功UI逻辑 */ }
                    is Resource.Error -> { /* 错误提示+跳登录页逻辑 */ }
                    is Resource.Loading -> { /* 加载态UI逻辑 */ }
                }
            }
        }
        // 登录页点击登录时,直接调用viewModel.login(用户输入的User对象)即可
    }
    // 原有的useUser方法全部删除
    ....
}

优化收益

  • 符合单一职责原则:Activity仅处理UI交互,数据逻辑全部收敛到数据层和ViewModel,内聚性大幅提升
  • 消除冗余逻辑:删除了Activity中两个不必要的Flow收集逻辑,减少冗余协程创建和性能开销
  • 可测试性提升:ViewModel和Repository不直接依赖Activity上下文,可直接进行单元测试

注意:不要在DataStore中明文存储用户密码,建议搭配Jetpack Security等加密方案存储敏感凭证,避免数据泄露风险


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.05 13:24:01