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

Android Retrofit2使用SharedPreferences中Bearer Token授权遇空指针问题咨询

问题分析与解决方案

核心错误原因

你把RetrofitInstance定义为继承Application的object单例,这完全不符合Android组件的初始化逻辑:

  • Android的Application子类必须在AndroidManifest.xml中注册,且由系统在应用启动时自动初始化
  • object是Kotlin的饿汉式单例,其实例化时机不受Android系统控制,导致applicationContext还未初始化就被调用,触发空指针异常

修正后的实现方案

步骤1:创建全局Application类

先定义一个自定义的Application子类,用来提供全局可用的Context:

class MyApp : Application() {
    companion object {
        lateinit var instance: MyApp
            private set
    }

    override fun onCreate() {
        super.onCreate()
        instance = this
    }
}

别忘了在AndroidManifest.xml的application标签中注册这个类:

<application
    android:name=".MyApp"
    <!-- 其他属性保持不变 -->
>

步骤2:重构Retrofit单例

将RetrofitInstance改成普通的object,通过全局Application实例获取Context来初始化SharedPreferences:

object RetrofitInstance {
    private const val API_BASE_URL = BuildConfig.API_BASE_URL

    private val sharedPreferences: SharedPreferences by lazy {
        MyApp.instance.getSharedPreferences(
            MyApp.instance.getString(R.string.preference_file_key),
            Context.MODE_PRIVATE
        )
    }

    private val httpBuilder: OkHttpClient.Builder
        get() {
            val httpClient = OkHttpClient.Builder()
                .addInterceptor(Interceptor { chain ->
                    val original = chain.request()
                    
                    val bearerToken = sharedPreferences.getString(
                        MyApp.instance.getString(R.string.preference_token_data_key),
                        null
                    )

                    val requestBuilder = original.newBuilder()
                        .header("Accept", "application/json")
                    
                    // 仅当token不为空时添加Authorization头,避免无效请求
                    bearerToken?.takeIf { it.isNotBlank() }?.let {
                        requestBuilder.header("Authorization", "Bearer $it")
                    }

                    return@Interceptor chain.proceed(requestBuilder.build())
                })

            return httpClient
        }

    private val retrofit by lazy {
        Retrofit.Builder()
            .baseUrl(API_BASE_URL)
            .client(httpBuilder.build())
            .addConverterFactory(
                MoshiConverterFactory.create(
                    Moshi.Builder().add(KotlinJsonAdapterFactory()).build()
                )
            )
            .addCallAdapterFactory(ResultCallAdapterFactory())
            .build()
    }

    val api: TestService by lazy {
        retrofit.create(TestService::class.java)
    }
}

关键优化点

  1. 安全获取Context:通过全局MyApp.instance获取Application Context,确保Context始终有效且不为空
  2. 避免无效请求头:添加token前先判断是否非空非空白,防止发送Bearer null这种无效的Authorization头
  3. 符合Android组件规则:不再让Retrofit单例继承Application,遵循Android的组件初始化逻辑

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 13:45:15