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
相关产品推荐
相关产品推荐

