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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 17:01:08