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

Android单例SessionManager如何处理进程死亡致令牌丢失问题

问题根因

SessionManager单例的生命周期和应用进程完全绑定,不管是应用崩溃、系统后台回收进程还是用户手动杀应用,整个进程的内存空间都会被完全回收,内存中存储的MutableLiveData实例、持有的AuthToken数据、甚至单例对象本身都会被销毁。下次启动应用是全新的进程实例,自然读不到之前内存里的令牌数据,这和LiveData本身的实现无关,核心问题是认证令牌只做了内存存储,没有持久化到本地。

处理方案
  • 令牌持久化:拿到有效AuthToken(登录成功、刷新令牌成功)时,除了更新内存中的LiveData,必须同步将令牌加密后持久化到本地。你已经注入了CacheManager,直接基于它实现存储即可,敏感认证令牌优先使用加密存储方案(如EncryptedSharedPreferences),不要明文存储避免泄漏。
  • 启动态恢复:给SessionManager增加初始化入口,在应用启动、进程创建完成的第一时间(比如Application的onCreate中),从本地持久化层读取之前存储的有效令牌,读取成功后直接更新到_sessionState中,完成登录态恢复。
  • 状态一致性处理:登出、令牌过期失效时,要同步清除本地持久化的令牌数据,再更新LiveData为未登录状态,避免本地残留脏数据。
  • 有效期校验:从本地读取缓存令牌时,必须先校验令牌是否过期,过期令牌直接清除,不要返回给业务层使用,避免无效请求。
改造后参考实现
@Singleton
class SessionManager
@Inject
constructor(
    private val sessionRepository: SessionRepository,
    private val cacheManager: CacheManager
) {

    private val sessionScope = CoroutineScope(Dispatchers.Main.immediate)

    private val _sessionState = MutableLiveData<Resource<AuthToken>>()
    val sessionState: LiveData<Resource<AuthToken>>
        get() = _sessionState

    /**
     * 应用启动时调用,恢复本地持久化的登录态
     */
    fun init() {
        sessionScope.launch {
            val cachedToken = cacheManager.get<AuthToken>(KEY_AUTH_TOKEN)
            when {
                cachedToken != null && !cachedToken.isExpired() -> {
                    _sessionState.value = Resource.Success(cachedToken)
                }
                else -> {
                    cacheManager.remove(KEY_AUTH_TOKEN)
                    _sessionState.value = Resource.Error("未登录")
                }
            }
        }
    }

    /**
     * 登录成功、令牌刷新成功后调用,更新状态并持久化
     */
    fun updateAuthToken(token: AuthToken) {
        sessionScope.launch {
            cacheManager.put(KEY_AUTH_TOKEN, token)
            _sessionState.value = Resource.Success(token)
        }
    }

    /**
     * 登出、令牌失效时调用,清除内存和本地缓存
     */
    fun clearSession() {
        sessionScope.launch {
            cacheManager.remove(KEY_AUTH_TOKEN)
            _sessionState.value = Resource.Error("会话已失效")
        }
    }

    private companion object {
        const val KEY_AUTH_TOKEN = "sp_key_auth_token"
    }
}

注意:不要尝试通过静态变量持有、进程保活等内存层面的方案解决该问题,只要进程被系统回收,内存数据必然丢失,本地持久化是唯一可靠的解决路径。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 15:27:14