Android Ktor登录流程中Bearer Token引发NullPointerException问题求助
我在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

