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

DataStore<Preferences>无法提供,求Hilt依赖注入问题解决方案

解决DataStore依赖注入错误:androidx.datastore.core.DataStore<androidx.datastore.preferences.core.Preferences> cannot be provided without an @Provides-annotated method

我在实现DataStore存储token和引导页状态时遇到了上述错误,参照GitHub代码编写后无法解决,相关代码如下:

DataStore Module

import android.content.Context
import androidx.datastore.preferences.preferencesDataStore
import com.seinoindomobil.dev.epod.data.local.datastore.DataStorePreferenceStorage
import com.seinoindomobil.dev.epod.data.local.datastore.DataStorePreferenceStorage.Companion.PREFS_NAME
import com.seinoindomobil.dev.epod.data.local.datastore.PreferenceStorage
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent

@Module
@InstallIn(SingletonComponent::class)
object LocalModule {
    
   private val Context.datastore by preferencesDataStore(PREFS_NAME)

    @Provides
    fun provideDataStore(@ApplicationContext context: Context): PreferenceStorage = DataStorePreferenceStorage(context.datastore)

}

RepositoryModule

import com.seinoindomobil.dev.epod.data.local.datastore.PreferenceStorage
import com.seinoindomobil.dev.epod.data.remote.LoginApi
import com.seinoindomobil.dev.epod.data.remote.repository.LoginRepositoryImpl
import com.seinoindomobil.dev.epod.data.remote.repository.OnBoardingRepositoryImpl
import com.seinoindomobil.dev.epod.domain.repository.LoginRepository
import com.seinoindomobil.dev.epod.domain.repository.OnBoardingRepository
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton

@Module
@InstallIn(SingletonComponent::class)
object RepositoryModule {

    @Provides
    @Singleton
    fun provideLoginRepository(
        api: LoginApi,
        preferenceStorage: PreferenceStorage
    ): LoginRepository {
        return LoginRepositoryImpl(api, preferenceStorage)
    }

    @Provides
    @Singleton
    fun provideOnBoardingRepository(preferenceStorage: PreferenceStorage): OnBoardingRepository {
        return OnBoardingRepositoryImpl(preferenceStorage)
    }
}

DataStoreInterface

interface PreferenceStorage {
    suspend fun setOnboardingState(isCompleted :Boolean)
    val getOnboardState :Flow<Boolean>

    suspend fun setUserToken(token:String)
    val getUserToken :Flow<String>
    suspend fun deleteToken()

}

DataStorePreference

class DataStorePreferenceStorage @Inject constructor(
    private val dataStore: DataStore<Preferences>
) : PreferenceStorage {


    object PreferenceKeys {
        val IS_ONBOARD_COMPLETED = booleanPreferencesKey("IS_ONBOARD_COMPLETED")
        val USER_TOKEN = stringPreferencesKey("user_token")
    }

    companion object {
        const val PREFS_NAME = "my_datastore"
    }

    override suspend fun setOnboardingState(isCompleted: Boolean) {
        dataStore.setValue {
            it[PreferenceKeys.IS_ONBOARD_COMPLETED] = isCompleted
        }
    }

    override val getOnboardState: Flow<Boolean>
        get() = dataStore.getValue {
            it[PreferenceKeys.IS_ONBOARD_COMPLETED] ?: false
        }

    override suspend fun setUserToken(token: String) {
       dataStore.setValue {
           it[PreferenceKeys.USER_TOKEN] = token
       }
    }

    override val getUserToken: Flow<String>
        get() = dataStore.getValue {
            it[PreferenceKeys.USER_TOKEN] ?: ""
        }

    override suspend fun deleteToken() {
        dataStore.setValue {
            it.remove(USER_TOKEN)
        }
    }
}

AuthInterceptor

class AuthInterceptor @Inject constructor(
    private val tokenManager: DataStorePreferenceStorage,
): Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        val token = runBlocking {
            tokenManager.getUserToken.first()
        }
        val request = chain.request().newBuilder()
        request.addHeader("Authorization", "Bearer $token")
        return chain.proceed(request.build())
    }
}

AuthAuthenticator

class AuthAuthenticator @Inject constructor (
    private val tokenManager: DataStorePreferenceStorage,
): Authenticator {

    override fun authenticate(route: Route?, response: Response): Request? {
        val token = runBlocking {
            tokenManager.getUserToken.first()
        }
        return runBlocking {
            val newToken = getNewToken(token)

            if (!newToken.isSuccessful || newToken.body() == null) { //无法刷新token,重启登录流程
                tokenManager.deleteToken()
            }

            newToken.body()?.let {
                tokenManager.setUserToken(it.result.signature?.access_token.toString())
                response.request.newBuilder()
                    .header("Authorization", "Bearer ${it.result.signature?.access_token}")
                    .build()
            }
        }
    }

    private suspend fun getNewToken(refreshToken: String?): retrofit2.Response<LoginDTO> {
        val loggingInterceptor = HttpLoggingInterceptor()
        loggingInterceptor.level = HttpLoggingInterceptor.Level.BODY
        val okHttpClient = OkHttpClient.Builder().addInterceptor(loggingInterceptor).build()

        val retrofit = Retrofit.Builder()
            .baseUrl("http://192.168.203.64:5555/authenticate/")
            .addConverterFactory(GsonConverterFactory.create())
            .client(okHttpClient)
            .build()
        val service = retrofit.create(LoginApi::class.java)
        return service.refreshToken("Bearer $refreshToken")
    }
}

问题原因

错误核心是Hilt无法找到DataStore<Preferences>的提供方法:DataStorePreferenceStorage的构造函数用@Inject标注,要求注入DataStore<Preferences>,但当前LocalModule只提供了PreferenceStorage实例,没有单独提供DataStore<Preferences>的依赖,导致Hilt无法完成注入链。

修复方案

1. 修改LocalModule,添加DataStore的提供方法

调整LocalModule,先提供DataStore<Preferences>实例,再基于它创建PreferenceStorage:

@Module
@InstallIn(SingletonComponent::class)
object LocalModule {
    private val Context.datastore by preferencesDataStore(DataStorePreferenceStorage.PREFS_NAME)

    @Provides
    fun provideDataStore(@ApplicationContext context: Context): DataStore<Preferences> {
        return context.datastore
    }

    @Provides
    fun providePreferenceStorage(dataStore: DataStore<Preferences>): PreferenceStorage {
        return DataStorePreferenceStorage(dataStore)
    }
}

2. 拦截器和认证器注入接口而非具体实现

将AuthInterceptor和AuthAuthenticator中的依赖从DataStorePreferenceStorage改为PreferenceStorage接口,符合依赖倒置原则,同时避免直接依赖具体实现:

修改后的AuthInterceptor

class AuthInterceptor @Inject constructor(
    private val tokenManager: PreferenceStorage,
): Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        val token = runBlocking {
            tokenManager.getUserToken.first()
        }
        val request = chain.request().newBuilder()
        request.addHeader("Authorization", "Bearer $token")
        return chain.proceed(request.build())
    }
}

修改后的AuthAuthenticator

class AuthAuthenticator @Inject constructor (
    private val tokenManager: PreferenceStorage,
): Authenticator {

    override fun authenticate(route: Route?, response: Response): Request? {
        val token = runBlocking {
            tokenManager.getUserToken.first()
        }
        return runBlocking {
            val newToken = getNewToken(token)

            if (!newToken.isSuccessful || newToken.body() == null) { // 无法刷新token,重启登录流程
                tokenManager.deleteToken()
            }

            newToken.body()?.let {
                tokenManager.setUserToken(it.result.signature?.access_token.toString())
                response.request.newBuilder()
                    .header("Authorization", "Bearer ${it.result.signature?.access_token}")
                    .build()
            }
        }
    }

    private suspend fun getNewToken(refreshToken: String?): retrofit2.Response<LoginDTO> {
        val loggingInterceptor = HttpLoggingInterceptor()
        loggingInterceptor.level = HttpLoggingInterceptor.Level.BODY
        val okHttpClient = OkHttpClient.Builder().addInterceptor(loggingInterceptor).build()

        val retrofit = Retrofit.Builder()
            .baseUrl("http://192.168.203.64:5555/authenticate/")
            .addConverterFactory(GsonConverterFactory.create())
            .client(okHttpClient)
            .build()
        val service = retrofit.create(LoginApi::class.java)
        return service.refreshToken("Bearer $refreshToken")
    }
}

说明

  • 调整后Hilt可以完整构建依赖链:先创建DataStore<Preferences>,再用它实例化DataStorePreferenceStorage,最后将PreferenceStorage接口提供给所有需要的组件。
  • 注入接口而非具体实现,不仅解决了当前错误,还提升了代码的可测试性和扩展性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 05:20:50