如何在Dagger Hilt中为Retrofit配置Bearer Token请求头
问题描述
我想在Retrofit中结合Dagger Hilt使用Bearer请求头,但不知道怎么把后续获取到的token传递给OkHttpClient的Interceptor。我参考了相关方案但没解决问题,以下是我的代码:
依赖模块代码
@Provides @Singleton fun provideOkHttpClient(token:String) = OkHttpClient.Builder() .addInterceptor(Interceptor(){ var newRequest = it.request().newBuilder() .addHeader("Authorization","Bearer $token") .build() it.proceed(newRequest) }) @Provides @Singleton fun provideRetrofit(okHttpClient: OkHttpClient):ApiService = Retrofit.Builder() .addConverterFactory(GsonConverterFactory.create()) .baseUrl(BASE_URL) .client(okHttpClient) .build() .create(ApiService::class.java)
VerifyCodeFragment代码
@AndroidEntryPoint class VerifyCodeFragment:Fragment() { private val verifyViewModel:VerifyCodeViewModel by viewModels() override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { val binding:FragmentVerifyCodeBinding = DataBindingUtil.inflate(inflater,R.layout.fragment_verify_code, container, false) val mobile = requireArguments().getString("mobile","0") val nationalCode = requireArguments().getString("national_code","-1") val token = requireArguments().getString("token","-2") var verifyCode = 0 verifyViewModel.getVerifyCode(mobile, token) verifyViewModel.code.observe(viewLifecycleOwner){ if(it.data != null){ verifyCode = it.data.data verifyViewModel.sendVerifyCode(mobile,verifyCode,token) } } verifyViewModel.res.observe(viewLifecycleOwner){ when(it.status){ Status.SUCCESS ->{ val token:String = it.data?.data?.access_token!! Toast.makeText(requireContext(),token,Toast.LENGTH_SHORT).show() } Status.ERROR -> { Toast.makeText(requireContext(),it.message,Toast.LENGTH_SHORT).show() } Status.LOADING -> { } } } Toast.makeText(requireContext(),"$mobile $nationalCode",Toast.LENGTH_SHORT).show() Toast.makeText(requireContext(),token,Toast.LENGTH_SHORT).show() return binding.root } }
ViewModel代码
@HiltViewModel class VerifyCodeViewModel @Inject constructor( private val repository: MainRepository ):ViewModel() { private val _res = MutableLiveData<Resource<Result>>() private val _code = MutableLiveData<Resource<GetCode>>() val res:LiveData<Resource<Result>> get() = _res val code:LiveData<Resource<GetCode>> get() = _code fun getVerifyCode(mobile:String,token:String) = viewModelScope.launch { _code.postValue(Resource.loading(null)) repository.getCode(mobile,token).let { if(it.isSuccessful){ _code.postValue(Resource.success(it.body())) }else{ _code.postValue(Resource.error(it.errorBody().toString(),null)) } } } fun sendVerifyCode(mobile:String,code:Int,token:String) = viewModelScope.launch { _res.postValue(Resource.loading(null)) repository.getToken(mobile,code,token).let { if(it.isSuccessful){ _res.postValue(Resource.success(it.body())) }else{ _res.postValue(Resource.error(it.errorBody().toString(),null)) } } } }
我的核心问题:怎么把Fragment里获取到的access_token传递到provideOkHttpClient方法里?
解决方案
你的问题核心在于:OkHttpClient是@Singleton单例,Hilt在App启动时就会初始化它,但此时你要的token还没从接口获取到,所以直接把token作为构造参数传给provideOkHttpClient根本行不通。正确的做法是用一个可动态更新的token存储类,让Interceptor每次请求时都从这个类里拿最新的token。
步骤1:创建Token管理类
这个类用来存储和更新token,用Hilt的@Singleton保证全局唯一:
@Singleton class TokenManager @Inject constructor() { private var authToken: String? = null fun setToken(token: String) { authToken = token } fun getToken(): String? { return authToken } }
步骤2:修改OkHttpClient的依赖提供方法
把原来的token: String参数换成TokenManager,然后在Interceptor里动态获取token:
@Provides @Singleton fun provideOkHttpClient(tokenManager: TokenManager) = OkHttpClient.Builder() .addInterceptor { chain -> val originalRequest = chain.request() val newRequest = originalRequest.newBuilder().apply { // 每次请求时从TokenManager拿最新的token tokenManager.getToken()?.let { token -> addHeader("Authorization", "Bearer $token") } }.build() chain.proceed(newRequest) } .build() // 补充你原代码遗漏的build()方法 @Provides @Singleton fun provideRetrofit(okHttpClient: OkHttpClient): ApiService = Retrofit.Builder() .addConverterFactory(GsonConverterFactory.create()) .baseUrl(BASE_URL) .client(okHttpClient) .build() .create(ApiService::class.java)
步骤3:在获取到token的地方更新TokenManager
在你的VerifyCodeFragment里,当获取到access_token后,注入TokenManager并调用setToken方法:
@AndroidEntryPoint class VerifyCodeFragment:Fragment() { private val verifyViewModel:VerifyCodeViewModel by viewModels() // 用Hilt注入TokenManager @Inject lateinit var tokenManager: TokenManager override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { // ... 原有代码不变 ... verifyViewModel.res.observe(viewLifecycleOwner){ when(it.status){ Status.SUCCESS ->{ val accessToken:String = it.data?.data?.access_token!! Toast.makeText(requireContext(),accessToken,Toast.LENGTH_SHORT).show() // 更新TokenManager里的token tokenManager.setToken(accessToken) } // ... 其他状态处理不变 ... } } // ... 原有代码不变 ... return binding.root } }
额外提醒
如果你有不需要携带Bearer头的接口(比如获取验证码、登录接口),可以在ApiService的方法上用@Headers指定单独的请求头,或者在Interceptor里判断请求URL来决定是否添加Authorization头。
内容的提问来源于stack exchange,提问作者Hooman Hooshyar
相关产品推荐
相关产品推荐

