无法设置拦截器变量值:AppInterceptor的accessToken始终为null求助
问题:Interceptor中accessToken设置后仍为null的问题
调用setAccessToken(accessToken: String?)后,日志能正确打印令牌,但intercept函数中获取到的accessToken始终为null。相关代码如下:
AppInterceptor代码
@Singleton class AppInterceptor @Inject constructor() : Interceptor { private var accessToken: String? = null fun setAccessToken(accessToken: String?) { this.accessToken = accessToken Log.d("Tag", "Access Setting: ${this.accessToken}") } override fun intercept(chain: Interceptor.Chain): Response { val request: Request = chain.request() val requestBuilder: Request.Builder = request.newBuilder() accessToken?.let { requestBuilder.addHeader("authorization", "Bearer $it") } return chain.proceed(requestBuilder.build()) } }
ViewModel中调用代码
@HiltViewModel class ApiViewModel @Inject constructor( private val apiHelper: APIHelper, private val oktaManager: OktaManager, private val interceptor: AppInterceptor ) : AndroidViewModel() { fun getResult() { viewModelScope.launch(Dispatchers.IO) { interceptor.setAccessToken(oktaManager.gettingOktaToken()) try { apiHelper.getResults() } catch (e: Exception) { Log.e("error", e.toString()) } } } }
Retrofit构建器代码
@Module @InstallIn(SingletonComponent::class) object RetrofitBuilder { private const val BASE_URL = "https://myUrl" private val interceptor = run { val httpLoggingInterceptor = HttpLoggingInterceptor() httpLoggingInterceptor.apply { httpLoggingInterceptor.level = HttpLoggingInterceptor.Level.BODY } } @Singleton @Provides fun provideOkHTTPClient(): OkHttpClient { return OkHttpClient.Builder() .addInterceptor(interceptor) .apply { addInterceptor(AppInterceptor()) } .addInterceptor(interceptor) .build() } @Singleton @Provides fun provideObjectMapper(): ObjectMapper { return jacksonObjectMapper().registerModule(JavaTimeModule()) .configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false) .configure(SerializationFeature.WRITE_DATE_TIMESTAMPS_AS_NANOSECONDS, false) } @Singleton @Provides fun provideRetrofitInstance( client: OkHttpClient, objectMapper: ObjectMapper ): Retrofit { return Retrofit.Builder() .client(client) .baseUrl(BASE_URL) .addConverterFactory(JacksonConverterFactory.create(objectMapper)) .build() } @Singleton @Provides fun provideApiService(retrofit: Retrofit): IApiService { return retrofit.create(IApiService::class.java) } }
问题原因
核心问题是存在两个独立的AppInterceptor实例:
- 一个是Hilt根据
@Singleton注解注入到ViewModel中的单例实例 - 另一个是在
provideOkHTTPClient方法中通过new AppInterceptor()手动创建的全新实例
ViewModel中调用setAccessToken修改的是第一个实例的token,但OkHttpClient实际使用的是第二个未被赋值的实例,因此intercept中拿到的始终是null。
解决方案
修改RetrofitBuilder模块,通过Hilt注入AppInterceptor单例实例,而非手动创建:
@Singleton @Provides fun provideOkHTTPClient(appInterceptor: AppInterceptor): OkHttpClient { return OkHttpClient.Builder() .addInterceptor(interceptor) .addInterceptor(appInterceptor) // 使用Hilt注入的单例实例 .addInterceptor(interceptor) .build() }
额外优化建议
- 避免重复添加日志拦截器:当前代码中前后两次添加了同一个HttpLoggingInterceptor,可简化为一次:
@Singleton @Provides fun provideOkHTTPClient(appInterceptor: AppInterceptor): OkHttpClient { return OkHttpClient.Builder() .addInterceptor(interceptor) .addInterceptor(appInterceptor) .build() }
- 线程安全优化:由于
setAccessToken可能在主线程调用,intercept在IO线程执行,建议用AtomicReference保证线程安全:
@Singleton class AppInterceptor @Inject constructor() : Interceptor { private val accessToken = AtomicReference<String?>() fun setAccessToken(accessToken: String?) { this.accessToken.set(accessToken) Log.d("Tag", "Access Setting: ${this.accessToken.get()}") } override fun intercept(chain: Interceptor.Chain): Response { val request: Request = chain.request() val requestBuilder: Request.Builder = request.newBuilder() accessToken.get()?.let { requestBuilder.addHeader("authorization", "Bearer $it") } return chain.proceed(requestBuilder.build()) } }
内容的提问来源于stack exchange,提问作者Hocine Elhadj
相关产品推荐
相关产品推荐

