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

Android Ktor登录流程中Bearer Token引发NullPointerException问题求助

问题:Ktor登录后获取用户偏好时Token为空导致未授权异常

我在Android中使用Ktor实现登录流程,用户登录后会从服务器获取用户偏好设置,项目采用模块化设计的MVI架构,状态事件可从Fragment或ViewModel触发。

流程步骤:

  • 点击登录按钮
  • 触发“Login”事件
  • 执行“Login”用例
  • 将Token保存到Encrypted Shared Preferences
  • 将LoginState的isLoggedIn布尔值改为true
  • 在收集Login State的Fragment中监听此变化
  • 触发“GetPreferences”事件
  • 通过从服务器获取用户偏好设置来执行“GetPreferences”用例

现在出现未授权异常:服务构建器中的token为空,但本不应为空。调试发现构建器在操作前已初始化,可能获取了token的空值且未更新新token。在首页触发“GetPreferences”事件无此问题,但我希望先获取偏好设置再跳转首页。

异常日志

2022-09-21 14:00:17.216 19987-19987/? W/System.err: java.lang.NullPointerException
2022-09-21 14:00:17.216 19987-19987/? W/System.err: at com.example.workoutplans_datasource.network.WorkoutPlanService$Factory$build$1$3$1$1.invokeSuspend(WorkoutPlanService.kt:65)

WorkoutPlanService的Factory代码

companion object Factory {
    fun build(
       sessionManager: SessionManager
    ): WorkoutPlanService {

        val token = sessionManager.getToken()

        return WorkoutPlanServiceImpl(
            httpClient = HttpClient(Android) {

                install(Logging) {
                    level = LogLevel.ALL
                }

                install(HttpTimeout) {
                    requestTimeoutMillis = 15000L
                    connectTimeoutMillis = 15000L
                    socketTimeoutMillis = 15000L
                }

                install(Auth) {
                    bearer {
                        loadTokens {
                            BearerTokens(token!!, token)
                        }
                    }
                }

                install(JsonFeature) {
                    serializer = KotlinxSerializer(
                        kotlinx.serialization.json.Json {
                            ignoreUnknownKeys =
                                true // if the server returns extra fields, ignore them
                        }
                    )
                }
            }
        )
    }
}

问题根源

构建WorkoutPlanService时,代码一次性获取sessionManager.getToken()的值并存在局部变量token中,后续即使sessionManager里的Token更新,这个局部变量也不会同步。如果服务在登录前就初始化,拿到的就是空Token,登录后触发GetPreferences时,依然会使用这个旧的空值,导致NPE。

修复方案

修改Auth插件的loadTokens逻辑,每次需要Token时都从sessionManager实时获取,而非缓存初始值:

install(Auth) {
    bearer {
        loadTokens {
            val currentToken = sessionManager.getToken()
            // 增加非空判断,避免直接抛NPE,可根据业务抛出合适的异常
            checkNotNull(currentToken) { "Token not found, user not logged in" }
            BearerTokens(currentToken, currentToken)
        }
    }
}

同时要确保sessionManager.getToken()能正确读取Encrypted Shared Preferences中的最新Token,确认登录时保存Token的逻辑是同步完成的,避免出现读取时Token还未写入的情况。

如果服务是单例模式,登录后无需重新构建服务,因为loadTokens会在每次请求时实时拉取最新的Token值,这样登录完成后触发GetPreferences就能拿到刚保存的有效Token了。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 02:20:37