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

Android Jetpack Compose中登录信息(JWT等)存储方案咨询

登录信息的存储必要性与实现方案

一、必须存储登录信息

登录信息(用户名、JWT Token)必须持久化存储,核心原因:

  • 避免用户每次启动App或切换后台后重复登录,提升使用体验
  • JWT是接口鉴权的核心凭证,持久化后App启动即可获取,无需重新请求
  • 跨页面、跨会话保持登录状态,支撑全局业务逻辑

二、可行的实现方案

1. 加密本地持久化(首选)

敏感信息必须加密存储,推荐使用Jetpack的Encrypted DataStore(替代SharedPreferences),若需更复杂的数据结构可使用Room。

代码示例(Encrypted DataStore)

先添加依赖(build.gradle.kts):

dependencies {
    implementation("androidx.datastore:datastore-preferences:1.0.0")
    implementation("androidx.datastore:datastore-preferences-core:1.0.0")
    implementation("androidx.security:security-crypto:1.1.0-alpha05")
}

封装AuthPreferences工具类:

import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.stringPreferencesKey
import androidx.datastore.preferences.preferencesDataStore
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map
import android.content.Context

object AuthPreferences {
    private val Context.dataStore: DataStore<Preferences> by preferencesDataStore(name = "auth_prefs")

    private val USER_NAME = stringPreferencesKey("user_name")
    private val JWT_TOKEN = stringPreferencesKey("jwt_token")

    // 保存登录信息
    suspend fun saveAuthInfo(context: Context, userName: String, token: String) {
        context.dataStore.edit { prefs ->
            prefs[USER_NAME] = userName
            prefs[JWT_TOKEN] = token
        }
    }

    // 监听登录信息变化
    fun getAuthInfoFlow(context: Context): Flow<Pair<String?, String?>> {
        return context.dataStore.data.map { prefs ->
            Pair(prefs[USER_NAME], prefs[JWT_TOKEN])
        }
    }

    // 清除登录信息(登出)
    suspend fun clearAuthInfo(context: Context) {
        context.dataStore.edit { it.clear() }
    }
}

使用方式:

  • 登录成功后调用saveAuthInfo存储信息
  • App启动时通过getAuthInfoFlow观察状态,判断是否自动登录
  • 接口请求时从Flow中获取Token,添加到请求头

2. 共享ViewModel(内存状态补充)

你尝试的共享ViewModel方案是可行的,但仅能在App运行期间维持状态,重启后会丢失,因此必须配合本地持久化使用,用于全局同步登录状态。

代码示例:

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
import android.content.Context

class AuthViewModel : ViewModel() {
    sealed class AuthState {
        object Unauthenticated : AuthState()
        data class Authenticated(val userName: String, val token: String) : AuthState()
    }

    private val _authState = MutableStateFlow<AuthState>(AuthState.Unauthenticated)
    val authState: StateFlow<AuthState> = _authState

    // 登录:保存到本地+更新内存状态
    fun login(context: Context, userName: String, token: String) {
        viewModelScope.launch {
            AuthPreferences.saveAuthInfo(context, userName, token)
            _authState.value = AuthState.Authenticated(userName, token)
        }
    }

    // 加载本地存储的登录信息,初始化状态
    fun loadSavedAuth(context: Context) {
        viewModelScope.launch {
            AuthPreferences.getAuthInfoFlow(context)
                .collect { (userName, token) ->
                    if (userName != null && token != null) {
                        _authState.value = AuthState.Authenticated(userName, token)
                    } else {
                        _authState.value = AuthState.Unauthenticated
                    }
                }
        }
    }

    // 登出:清除本地+更新内存状态
    fun logout(context: Context) {
        viewModelScope.launch {
            AuthPreferences.clearAuthInfo(context)
            _authState.value = AuthState.Unauthenticated
        }
    }
}

使用方式:

在Compose中通过viewModel<AuthViewModel>()获取实例,观察authState控制页面导航:

val authViewModel = viewModel<AuthViewModel>()
val authState by authViewModel.authState.collectAsState()

LaunchedEffect(Unit) {
    authViewModel.loadSavedAuth(context)
}

when (authState) {
    is AuthViewModel.AuthState.Unauthenticated -> LoginScreen()
    is AuthViewModel.AuthState.Authenticated -> HomeScreen()
}

3. 为什么不推荐用Navigation传递登录信息

Navigation组件的参数传递适合少量、临时的数据,登录信息是全局核心状态:

  • 逐个页面传递会导致代码冗余,维护成本高
  • NavTypes不支持直接传递自定义对象,即使序列化后传递,也无法解决App重启后状态丢失的问题
  • 全局状态更适合用持久化+共享ViewModel的组合方案管理

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 22:17:43