Jetpack Compose中实现勾选“保持登录”维持会话的问题
实现“保持登录”功能的完整方案
一、核心问题分析
你之前的代码未实现状态持久化和启动时自动校验逻辑:勾选状态没存到偏好设置,退出应用后状态丢失;也没在应用启动时检查保存的登录凭证,导致无法自动恢复会话。下面分步骤解决:
二、第一步:实现偏好设置的持久化存储(用Jetpack DataStore)
DataStore是官方推荐的SharedPreferences替代方案,支持协程和Flow,更适合Jetpack Compose场景。
1. 创建偏好封装类
import androidx.datastore.core.DataStore import androidx.datastore.preferences.core.Preferences import androidx.datastore.preferences.core.booleanPreferencesKey import androidx.datastore.preferences.core.edit import androidx.datastore.preferences.core.stringPreferencesKey import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map class UserPreferences(private val dataStore: DataStore<Preferences>) { companion object { // 存储"保持登录"状态的Key private val KEEP_SIGNED_IN = booleanPreferencesKey("keep_signed_in") // 存储用户登录凭证(比如Token)的Key private val USER_TOKEN = stringPreferencesKey("user_token") } // 获取"保持登录"状态的Flow val keepSignedInState: Flow<Boolean> = dataStore.data .map { it[KEEP_SIGNED_IN] ?: false } // 更新"保持登录"状态 suspend fun updateKeepSignedIn(isKeep: Boolean) { dataStore.edit { it[KEEP_SIGNED_IN] = isKeep } } // 存储登录Token suspend fun saveUserToken(token: String) { dataStore.edit { it[USER_TOKEN] = token } } // 获取存储的Token val userToken: Flow<String?> = dataStore.data .map { it[USER_TOKEN] } }
三、第二步:改造ViewModel,整合状态管理与自动登录逻辑
ViewModel需要处理:同步UI状态到偏好、登录时保存凭证、启动时校验自动登录条件。
import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.launch class LoginViewModel( private val userPreferences: UserPreferences ) : ViewModel() { // UI层观察的"保持登录"状态 private val _keepSignedIn = MutableStateFlow(false) val keepSignedIn: StateFlow<Boolean> = _keepSignedIn init { // 启动时从偏好加载已保存的状态 viewModelScope.launch { userPreferences.keepSignedInState.collect { _keepSignedIn.value = it } } } // 复选框状态变更时调用 fun onKeepSignedInChanged(newValue: Boolean) { viewModelScope.launch { // 更新UI状态 _keepSignedIn.value = newValue // 同步到偏好设置 userPreferences.updateKeepSignedIn(newValue) } } // 登录核心函数 fun login(username: String, password: String) { viewModelScope.launch { // 1. 调用登录API获取Token(示例逻辑) val token = api.login(username, password) // 2. 如果勾选"保持登录",就把Token存到偏好 if (_keepSignedIn.value) { userPreferences.saveUserToken(token) } else { // 未勾选则清空之前的Token userPreferences.saveUserToken("") } // 3. 登录成功后跳转主界面 navigateToHome() } } // 应用启动时检查是否自动登录 fun checkAutoLogin() { viewModelScope.launch { val savedToken = userPreferences.userToken.firstOrNull() val isKeepSignedIn = userPreferences.keepSignedInState.firstOrNull() ?: false // 同时满足"保持登录已勾选"和"Token存在且有效"才自动登录 if (isKeepSignedIn && !savedToken.isNullOrEmpty()) { // 验证Token有效性(示例逻辑) val isTokenValid = api.validateToken(savedToken) if (isTokenValid) { navigateToHome() } else { // Token无效,清空偏好并回到登录页 userPreferences.saveUserToken("") userPreferences.updateKeepSignedIn(false) } } } } // 退出登录逻辑 fun logout() { viewModelScope.launch { userPreferences.saveUserToken("") userPreferences.updateKeepSignedIn(false) _keepSignedIn.value = false navigateToLogin() } } // 导航逻辑(可根据你的项目用SharedFlow或其他方式实现) private fun navigateToHome() { /* ... */ } private fun navigateToLogin() { /* ... */ } }
四、第三步:调整Compose界面代码
让复选框与ViewModel的状态双向绑定:
@Composable fun KeepMeSignedInCheckBox( checked: Boolean, onCheckedChange: (Boolean) -> Unit ) { Row( verticalAlignment = Alignment.CenterVertically, modifier = Modifier.clickable { onCheckedChange(!checked) } ) { Checkbox(checked = checked, onCheckedChange = onCheckedChange) Text(text = "保持登录") } } // 在登录页面调用 @Composable fun LoginScreen(viewModel: LoginViewModel = viewModel()) { val keepSignedIn by viewModel.keepSignedIn.collectAsState() // 其他登录UI元素(账号输入框、密码输入框、登录按钮)... KeepMeSignedInCheckBox( checked = keepSignedIn, onCheckedChange = viewModel::onKeepSignedInChanged ) }
五、第四步:应用启动时触发自动登录校验
在启动页或MainActivity中调用checkAutoLogin(),判断直接进主界面还是登录页:
class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { val loginViewModel = viewModel<LoginViewModel>() val navController = rememberNavController() // 启动时检查自动登录 LaunchedEffect(Unit) { loginViewModel.checkAutoLogin() } // 监听导航事件(示例) LaunchedEffect(Unit) { // 假设ViewModel用SharedFlow发送导航指令 loginViewModel.navigationEvents.collect { event -> when (event) { is NavigationEvent.ToHome -> navController.navigate("home") { popUpTo("login") { inclusive = true } } is NavigationEvent.ToLogin -> navController.navigate("login") } } } // 你的导航图配置... } } }
关键注意事项
- 依赖注入:不要用
instance()直接获取偏好实例,推荐用Hilt注入UserPreferences,提升可测试性和代码维护性。 - 敏感信息加密:Token属于敏感信息,建议用
EncryptedDataStore或EncryptedSharedPreferences加密存储,避免明文泄露。 - Token过期处理:自动登录时必须校验Token有效性,过期则清空偏好并引导用户重新登录。
内容的提问来源于stack exchange,提问作者Suraya
相关产品推荐
相关产品推荐

