I have problem using dagger 2 for updating token in runtime.
So here is the scenario:
I have a screen to Change Password. when i succeed update password, the current jwt token would be invalid, and i need to store new token from update token response, i store that token in SharedPreferences. but the problem is when i store the token. it updated in sharedprefernces, but wont update value in DaggerGraph where i build Retrofit instance (Authorization header).
Below is my code :
AppComponent.kt
#Singleton
#Component(
modules = [StorageModule::class, AppModule::class, ViewModelModule::class]
)
interface AppComponent {
#Component.Factory
interface Factory {
fun create(#BindsInstance context: Context): AppComponent
}
fun inject(activity: SplashActivity)
fun inject(activity: LoginActivity)
fun inject(activity: MainActivity)
fun inject(activity: ChangePasswordActivity)
}
AppModule.kt
#Module
class AppModule {
#Singleton
#Provides
fun provideAuthInterceptor(sharedPreferencesSources: SharedPreferencesSources): Interceptor {
return AuthInterceptor(sharedPreferencesSources.tokenApi())
}
#Singleton
#Provides
fun provideApiService(
authInterceptor: Interceptor
): SharedProductClient {
return Network.retrofitClient(authInterceptor = authInterceptor)
.create(SharedProductClient::class.java)
}
#Singleton
#Provides
fun provideAppRepository(apiService: SharedProductClient): AppRepository {
return AppRepositoryImpl(apiService)
}
#Singleton
#Provides
fun provideAppUseCase(appRepository: AppRepository): AppUseCase {
return AppUseCase(appRepository)
}
#Singleton
#Provides
fun provideAppScheduler(): SchedulerProvider = AppSchedulerProvider()
}
StorageModule.kt
#Module
class StorageModule {
#Singleton
#Provides
fun provideSharedPreferences(context: Context): SharedPreferences {
return context.getSharedPreferences(SharedPrefName, Context.MODE_PRIVATE)
}
#Singleton
#Provides
fun provideSharedPreferencesSource(sharedPrefInstance: SharedPreferences): SharedPreferencesSources {
return SharedPreferencesSourcesImpl(sharedPrefInstance)
}
companion object {
const val SharedPrefName = "share_product_prefs"
}
}
AuthInterceptor.kt
class AuthInterceptor constructor(
private val token: String
) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response = chain.run {
proceed(
request()
.newBuilder()
.addHeader("Accept", "application/json")
.addHeader("Authorization", "Bearer $token")
.build()
)
}
}
Any suggestion would be really help me. Thanks!
This is because you only pass a String instance of the token when creating the AuthInterceptor.
You should provide a way (e.g. an interface) of dynamically obtaining the token from the SharedPreferences when needed.
This is one way of doing this:
Change the token:String to a function type in your AuthInterceptor constructor (and use it when needed):
class AuthInterceptor constructor(
private val tokenProvider: () -> String
) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response = chain.run {
proceed(
request()
.newBuilder()
.addHeader("Accept", "application/json")
.addHeader("Authorization", "Bearer ${tokenProvider.invoke()}")
.build()
)
}
}
When creating your AuthInteceptor build your lambda to dynamically refer to SharedPreferences
#Module
class AppModule {
#Singleton
#Provides
fun provideAuthInterceptor(sharedPreferencesSources: SharedPreferencesSources): Interceptor {
return AuthInterceptor(){ sharedPreferencesSources.tokenApi() }
}
//...
}
This way the tokenProvider will be invoked (SharedPreferences will be accessed) every time you make an api call, instead of just a single time when creating the AuthInterceptor.
Related
Hello folks please help me to debug this dagger2 issue,I am very new to dagger2 and just started with a project.
public abstract interface ApplicationComponent {
^
java.util.Map<java.lang.Class<?
>,javax.inject.Provider<dagger.android.AndroidInjector.Factory<?>>> is injected at
dagger.android.DispatchingAndroidInjector(injectorFactoriesWithClassKeys, �)
dagger.android.DispatchingAndroidInjector<java.lang.Object> is injected at
com.example.dictionary.App.dispatchingServiceInjector
com.example.dictionary.App is injected at
com.example.dictionary.di.components.ApplicationComponent.inject(com.example.dictionary.App)
error: [Dagger/MissingBinding]
java.util.Map<java.lang.String,javax.inject.Provider<dagger.android.AndroidInjector.Factory<?
>>> cannot be provided without an #Provides-annotated method.
My implementations are:
interface ApiService {
companion object {
const val BASE_URL= "https://od-api.oxforddictionaries.com/"
}
#GET("api/v2/entries/en-gb/")
fun getMeaning(#Query("word") word: String): Observable<Response>
}
}
open class RemoteDataSource #Inject constructor(private val apiService: ApiService) {
fun getWordMeaning(word: String) = apiService.getMeaning(word)
}
#ApplicationScope
#Component(modules = [ApplicationModule::class, NetworkModule::class])
interface ApplicationComponent {
fun inject(app: App)
fun inject(mainActivity: MainActivity)
}
#Module
class ApplicationModule(private val application: Application) {
#ApplicationScope
#Provides
fun provideApplicationContext(): Context = application.applicationContext
#ApplicationScope
#Provides
fun provideSharedPreferences(): SharedPreferences =
PreferenceManager.getDefaultSharedPreferences(application)
#ApplicationScope
#Provides
fun providerDisplayMetrics(): DisplayMetrics = application.resources.displayMetrics
}
#Module
class NetworkModule(
private val baseUrl: String,
private val application: Application
) {
companion object {
const val CACHE_SIZE = 10 * 1024 * 1024L // 10 MiB
const val TIME_OUT = 10L // time in minutes to get the response from server
}
#ApplicationScope
#Provides
fun provideGson(): Gson = GsonBuilder().create()
#ApplicationScope
#Provides
fun provideOkHttpCache() = Cache(application.cacheDir, CACHE_SIZE)
#ApplicationScope
#Provides
fun provideOkHttpClient(cache: Cache): OkHttpClient = with(OkHttpClient.Builder()) {
writeTimeout(3, TimeUnit.MINUTES)
.connectTimeout(3, TimeUnit.MINUTES)
.readTimeout(TIME_OUT, TimeUnit.MINUTES)
cache(cache)
addInterceptor(headersInterceptor())
build()
}
#ApplicationScope
#Provides
fun provideRetrofit(gson: Gson, okHttpClient: OkHttpClient): Retrofit = Retrofit.Builder()
.baseUrl(baseUrl)
.client(okHttpClient)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.addConverterFactory(GsonConverterFactory.create(gson))
.build()
#ApplicationScope
#Provides
fun provideApiService(retrofit: Retrofit): ApiService =
retrofit.create(ApiService::class.java)
private var authRequest: String? = null
private fun headersInterceptor() = Interceptor { chain ->
val requestBuilder = chain.request().newBuilder()
requestBuilder.addHeader("Accept", "application/json")
requestBuilder.addHeader("app_id", "SOME APP ID")
requestBuilder.addHeader("app_key", "Some API KEY")
chain.proceed(
requestBuilder.build()
)
}
}
class App : Application(), HasAndroidInjector {
#Inject
lateinit var dispatchingServiceInjector:
DispatchingAndroidInjector<Any>
override fun onCreate() {
super.onCreate()
applicationComponent.inject(this)
RxJavaPlugins.setErrorHandler {
it.printStackTrace()
}
}
override fun androidInjector(): AndroidInjector<Any> {
return dispatchingServiceInjector
}
val applicationComponent: ApplicationComponent by
lazy<ApplicationComponent>(mode = LazyThreadSafetyMode.NONE) {
DaggerApplicationComponent
.builder()
.applicationModule(ApplicationModule(this))
.networkModule(NetworkModule(ApiService.BASE_URL, this))
.build()
}
}
I tried re-building the code, also attempted invalidate cache and restart to re-install dependencies, as dagger2 requires re-building the project every time once a new dependency injection is implemented, followed YouTube tutorials, to implement this so far, but unable to detect the problem, although in the build crash its indicating to provide #Provides, but in my NetworkModule, ApplicationModule I set this #Provides annotation.
Please help to figure out the solution.
As suggested in this answer, just add your App class in the android manifest under the application tag.
<application ...
android:name = ".App" ...>
...
I just got a problem to inject repository in the interceptor to get access token when its needed or rather expired. I just don't understand where I did something wrong. I just didn't find any example how to deal with interceptor and repository. Thinking about that double "Retrofit.Builder" maybe this is a problem. What do you think? And let's the code talk:
#Module
class AppModule {
#Singleton
#Provides
fun provideRefreshTokenService(client: OkHttpClient): RefreshTokenApi {
return Retrofit.Builder()
.baseUrl("https://id.twitch.tv/oauth2/")
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(RefreshTokenApi::class.java)
}
#Singleton
#Provides
fun provideHttpClient(headerInterceptor: HeaderInterceptor): OkHttpClient {
return OkHttpClient.Builder()
.addNetworkInterceptor(headerInterceptor)
.build()
}
#Singleton
#Provides
fun provideRetrofit(client: OkHttpClient): TwichApi {
return Retrofit.Builder()
.baseUrl("https://api.igdb.com/v4/")
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(TwichApi::class.java)
}
}
class TwichHeaderRepository #Inject constructor(
private val refreshTokenApi: RefreshTokenApi)
{
suspend fun fetchRefreshToken(): Response<RefreshToken> {
return withContext(Dispatchers.IO) {
refreshTokenApi.getRefreshToken()
}
}
}
private const val TAG = "AddRepositoryAction"
private const val HEADER_CLIENT_ID = "Client-ID"
private const val HEADER_AUTHORIZATION = "Authorization"
private const val HEADER_ACCEPT = "Accept"
private const val DEFAULT_ACCESS_TOKEN = "mjycvndz4sasons2mg990kqme6vu6d"
private const val UNAUTHORIZED_STATUS_CODE = 401
#Singleton
class HeaderInterceptor #Inject constructor(
private val context: Context,
private val twichHeaderRepository: TwichHeaderRepository
) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val response = chain.proceed(chain.request().newBuilder().apply {
header(HEADER_CLIENT_ID, "i3nzc6w3n0pod39zgsq8h445o2yp6p")
header(HEADER_AUTHORIZATION, "Bearer $DEFAULT_ACCESS_TOKEN")
header(HEADER_ACCEPT, "application/json")
}.build())
val refreshToken = runBlocking { generateAccessToken(twichHeaderRepository) }
if (response.code == UNAUTHORIZED_STATUS_CODE) {
response.close()
val accessToken = ""
return chain.proceed(chain.request().newBuilder().apply {
header(HEADER_CLIENT_ID, "i3nzc6w3n0pod39zgsq8h445o2yp6p")
header(HEADER_AUTHORIZATION, "Bearer $accessToken")
header(HEADER_ACCEPT, "application/json")
}.build())
}
return response
}
}
private suspend fun generateAccessToken(twichHeaderRepository: TwichHeaderRepository): String
{
val responseRefreshToken = twichHeaderRepository.fetchRefreshToken()
return responseRefreshToken.body().toString()
}
interface RefreshTokenApi {
#POST(
...
)
suspend fun getRefreshToken(
): Response<RefreshToken>
}
#Singleton
#Component(modules = [AppModule::class])
interface AppComponent {
#Component.Factory
interface Factory {
fun create(#BindsInstance context: Context): AppComponent
}
fun inject(activity: MainActivity)
fun inject(fragment: AddFragment)
fun inject(interceptor: HeaderInterceptor)
}
class TwichRepository #Inject constructor(
private val twichApi: TwichApi
) {
suspend fun searchGames(title: String): Response<Game> {
return withContext(Dispatchers.IO) { twichApi.getGamesBySearch(title) }
}
}
Error message is
Found a dependency cycle okhttp3.OkHttpClient is injected at
com.example.glc.AppModule.provideRefreshTokenService(client)
com.example.glc.add.RefreshTokenApi is injected at
com.example.glc.add.TwichHeaderRepository(refreshTokenApi)
com.example.glc.add.TwichHeaderRepository is injected at
com.example.glc.add.HeaderInterceptor(�, twichHeaderRepository)
com.example.glc.add.HeaderInterceptor is injected at
com.example.glc.AppModule.provideHttpClient(headerInterceptor)
okhttp3.OkHttpClient is injected at
com.example.glc.AppModule.provideRetrofit(client)
com.example.glc.add.TwichApi is injected at
com.example.glc.add.TwichRepository(twichApi)
com.example.glc.add.TwichRepository is injected at
com.example.glc.add.AddViewModel(twichRepository)
com.example.glc.add.AddViewModel is injected at
com.example.glc.add.AddFragment.addViewModel
com.example.glc.add.AddFragment is injected at
com.example.glc.di.AppComponent.inject(com.example.glc.add.AddFragment)
You use Lazy on TwichHeaderRepository dependency in HeaderInterceptor's constructor to break the dependency cycle.
class HeaderInterceptor #Inject constructor(
private val lazyTwichHeaderRepository: Lazy<TwichHeaderRepository>
) {
// Access the dependency lazily with lazyTwichHeaderRepository.get()
}
I just needed to add #Named to resolve my problem and understand it as Mark said in the comment.
#Module
class AppModule {
#Singleton
#Provides
#Named("oauth2")
fun provideAccessTokenHttpClient() = OkHttpClient.Builder().build()
#Singleton
#Provides
fun provideRefreshTokenService(#Named("oauth2") client: OkHttpClient): RefreshTokenApi {
return Retrofit.Builder()
.baseUrl("https://id.twitch.tv/oauth2/")
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(RefreshTokenApi::class.java)
}
#Singleton
#Provides
#Named("igdb")
fun provideSearchDataHttpClient(headerInterceptor: HeaderInterceptor): OkHttpClient {
return OkHttpClient.Builder()
.addInterceptor(headerInterceptor)
.build()
}
#Singleton
#Provides
fun provideRetrofit(#Named("igdb") client: OkHttpClient): TwichApi {
return Retrofit.Builder()
.baseUrl("https://api.igdb.com/v4/")
.client(client)
.addConverterFactory(GsonConverterFactory.create())
.build()
.create(TwichApi::class.java)
}
}
I am trying to inject retrofit APIServices dependency into the model class. Here is My API Module Source Code:
#Module
#InstallIn(SingletonComponent::class)
object ApiModule {
#Singleton
#Provides
fun providesHttpLoggingInterceptor() = HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY
}
#Singleton
#Provides
fun providesOkHttpClient(httpLoggingInterceptor: HttpLoggingInterceptor): OkHttpClient =
OkHttpClient.Builder()
.addInterceptor(httpLoggingInterceptor)
.build()
#Singleton
#Provides
fun providesRetrofit(okHttpClient: OkHttpClient): Retrofit =
Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(ApiConfig.BASE_URL)
.client(okHttpClient)
.build()
#Singleton
#Provides
#Named("ApiService")
fun providesApiService(retrofit: Retrofit):ApiServices =
retrofit.create(ApiServices::class.java)
}
For User Registration, I am using MVP Architecture Pattern where FragmentRegistration.kt is view layer, RegistrationModel is model layer class
When I inject ApiServices dependency into FragmentRegistration, it works fine. But when I try to inject it into model layer class, which is RegistrationModel, It doesn't work.
RegistrationModel:
class RegistrationModel(
val presenter: RegistrationContract.Presenter
) : RegistrationContract.Model {
#Inject
#Named("ApiService")
lateinit var apiServices: ApiServices
override fun onDataReady(registrationData: RegistrationData) {
val map = mapOf(
"Accept" to "application/json",
"Content-Type" to "application/json"
)
apiServices.userRegistration(map, registrationData)
.enqueue(object : Callback<RegistrationResponse> {
override fun onResponse(
call: Call<RegistrationResponse>,
response: Response<RegistrationResponse>
) {
if (response.isSuccessful) {
Log.d(TAG, "onDataReady: ${response.body().toString()}")
} else {
val apiFailure = APIFailure(
response.code(),
response.message()
)
presenter.onSignupFailure(apiFailure)
Log.d(TAG, "onDataReady: Error ${response.code()}")
Log.d(TAG, "onDataReady: Error Body ${response.errorBody()}")
}
}
override fun onFailure(call: Call<RegistrationResponse>, t: Throwable) {
presenter.onSignupFailure(
APIFailure(-1, t.toString())
)
Log.d(TAG, "onFailure: $t")
}
})
}
companion object {
const val TAG = "RegistrationModel"
}
}
In the above's Code,
#Inject
#Named("ApiService")
lateinit var apiServices: ApiServices
this dependency injection is not working.
You are trying to inject a filed provided by Hilt into a class which is not managed by Hilt. This will not work out of the box. You have to define EntryPoint for you custom class, so the Hilt can perform injection. You can read how to do that here: https://developer.android.com/training/dependency-injection/hilt-android#not-supported
I am trying to implement multiple instance of retrofit for two different base URL`s using Dagger2 and MVVM. I have already wasted a huge amount of time testing different approaches provided here in SO, but still unable to figure it out. I have created new Qualifiers as well and created a new instance but all my request are still made using first instance.Here is my implementation.
Qualifiers
#Retention(AnnotationRetention.BINARY)
#Qualifier
annotation class APIService1
#Retention(AnnotationRetention.BINARY)
#Qualifier
annotation class APIService2
AppModule class
#AssistedModule
#Suppress("unused")
#Module(includes = [ViewModelModule::class, CoreDataModule::class, AssistedViewModelModule::class, AssistedInject_AppModule::class])
class AppModule {
#Singleton
#Provides
fun provideServices(
APIService1 okHttpClient: OkHttpClient, converterFactory: MoshiConverterFactory
) =
provideService(okHttpClient, converterFactory, MyApi::class.java)
#Singleton
#Provides
#APIService2
fun provideMicroServices(
APIService1 okHttpClient: OkHttpClient, converterFactory: MoshiConverterFactory
) =
provideMicroService(okHttpClient, converterFactory, MyApi::class.java)
#APIService1
#Provides
fun providePrivateOkHttpClient(upstreamClient: OkHttpClient): OkHttpClient {
return upstreamClient.newBuilder().build()
}
#Singleton
#Provides
fun provideRemoteDataSource(myApiService: MyApi) = RemoteDataSource(myApiService)
private fun createRetrofit(
okhttpClient: OkHttpClient,
converterFactory: MoshiConverterFactory
): Retrofit {
Retrofit.Builder()
.baseUrl("https://example1.com/api/")
.client(okhttpClient)
.addConverterFactory(converterFactory)
.build()
}
private fun createMicroServiceRetrofit(
okhttpClient: OkHttpClient,
converterFactory: MoshiConverterFactory
): Retrofit {
Retrofit.Builder()
.baseUrl("https://example2.com/api/")
.client(okhttpClient)
.addConverterFactory(converterFactory)
.build()
}
private fun <T> provideService(
okhttpClient: OkHttpClient,
converterFactory: MoshiConverterFactory, clazz: Class<T>
): T {
return createRetrofit(okhttpClient, converterFactory).create(clazz)
}
private fun <T> provideMicroService(
okhttpClient: OkHttpClient,
converterFactory: MoshiConverterFactory, clazz: Class<T>
): T {
return createMicroServiceRetrofit(okhttpClient, converterFactory).create(clazz)
}
}
CoreDataModule class
#Module
class CoreDataModule {
#Provides
fun provideOkHttpClient(): OkHttpClient {
val interceptor = HttpLoggingInterceptor()
interceptor.level = HttpLoggingInterceptor.Level.BODY
val client = OkHttpClient().newBuilder()
.connectTimeout(15, TimeUnit.SECONDS)
.readTimeout(90, TimeUnit.SECONDS)
.writeTimeout(60, TimeUnit.SECONDS)
.retryOnConnectionFailure(false)
return client.build()
}
#Provides
#Singleton
fun provideMoshi(): Moshi = Moshi.Builder().build()
#Provides
#Singleton
fun provideMoshiConverterFactory(): MoshiConverterFactory =
MoshiConverterFactory.create()
}
RemoteDataSource Class
#Keep
class RemoteDataSource #Inject constructor(private val service: MyApi) : BaseDataSource() {
suspend fun getOtp(data: String) = getResult { service.getOtp(data) }
suspend fun getData(data: String) = getResult { service.getData(data) }
}
BaseDataSource Class
abstract class BaseDataSource {
protected suspend fun <T> getResult(call: suspend () -> Response<T>): Result<T> {
val response = call()
if (response.isSuccessful) {
val body = response.body()
if (body != null) return Result.success(body)
}
return error(" ${response.code()} ${response.message()}")
}
}
MyApi Class
interface MyApi {
#POST("Register/Otp")
#FormUrlEncoded
suspend fun getOtp(#Field("data") data: String): Response<OtpResponse>
#POST("Home/Data")
#FormUrlEncoded
suspend fun getData(#Field("data") data: String): Response<DataResponse>
}
This is how i am using it in my ViewModel
One of My ViewModles
class OtpViewModel #Inject constructor(
private val remoteDataSource: RemoteDataSource
) :
ViewModel() {
fun getNewOtp() = liveData {
try {
emit(Resource.loading(data = null))
val response = remoteDataSource.getOtp(params))
emit(Resource.success(data = response))
} catch (e: Exception) {
emit(Resource.error(data = null, message = e.message ?: e.localizedMessage.orEmpty()))
}
}
Create 2 annotations like this:
#Qualifier
#Target(
AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER,
AnnotationTarget.PROPERTY_SETTER, AnnotationTarget.FIELD,
AnnotationTarget.VALUE_PARAMETER
)
#Retention(AnnotationRetention.BINARY)
annotation class ApiService1
#Qualifier
#Target(
AnnotationTarget.FUNCTION, AnnotationTarget.PROPERTY_GETTER,
AnnotationTarget.PROPERTY_SETTER, AnnotationTarget.FIELD,
AnnotationTarget.VALUE_PARAMETER
)
#Retention(AnnotationRetention.BINARY)
annotation class ApiService2
Change your providers with:
#Provides
#Singleton
#ApiService1
fun provideServices(....): MyApi {
return ...
}
#Provides
#Singleton
#ApiService2
fun provideMicroServices(....): MyApi {
return ...
}
Then in your Android classes you can do this:
class MyActivity : FragmentActivity() {
#Inject
#ApiService1
lateinit var retrofit1: MyApi
#Inject
#ApiService2
lateinit var retrofit2: MyApi
....
}
Or in classes:
class Repository #Inject constructor (
#ApiService1 retrofit1: MyApi
) {
....
}
I’m trying to create an basic architecture with Dagger 2 for my study project but I have encountered several problems with it…
The current error daggers tell me
FeedMeApplicationComponent.java:7: error: [Dagger/IncompatiblyScopedBindings] .FeedMeApplicationComponent (unscoped) may not reference scoped bindings:
I only have this problem when I add the ActivityMainModule as a module of the application
and ActivityMainModule contains a sub component only related to the MainActivity.
I don’t understand why I cannot add this module of sub component to the Application Graph :confusing
Those are my Dagger classes…
class FeedMeApplication : DaggerApplication() {
override fun applicationInjector(): AndroidInjector<out DaggerApplication> {
return DaggerFeedMeApplicationComponent.factory().create(this)
}
}
#Component(modules = [AndroidInjectionModule::class, NetworkModule::class, NutritionModule::class, ActivityMainModule::class])
interface FeedMeApplicationComponent : AndroidInjector<FeedMeApplication> {
#Component.Factory
interface Factory {
fun create(#BindsInstance context: Context): FeedMeApplicationComponent
}
override fun inject(instance: FeedMeApplication?)
}
#Module
object NetworkModule {
#Singleton
#Provides
#JvmStatic
fun provideNutritionService(retrofit: Retrofit): NutritionService {
return retrofit.create(NutritionService::class.java)
}
#Singleton
#Provides
#JvmStatic
fun provideRetrofit(okHttpClient: OkHttpClient): Retrofit {
return Retrofit.Builder()
.addConverterFactory(MoshiConverterFactory.create())
.baseUrl("https://api.edamam.com/api")
.client(okHttpClient)
.build()
}
#Singleton
#Provides
#JvmStatic
fun provideOkHttp(): OkHttpClient {
return OkHttpClient()
.newBuilder()
.addInterceptor(ApiInterceptor())
.build()
}
private class ApiInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request().newBuilder()
request
.addHeader("api_id", “abc")
.addHeader("app_key", “123")
return chain.proceed(request.build())
}
}
}
#Module
object NutritionModule {
#Singleton
#Provides
#JvmStatic
fun provideNutritionRepository(nutritionService: NutritionService): NutritionRepository {
return NutritionRepository(nutritionService)
}
}
#Module(subcomponents = [MainActivityComponent::class], includes = [MainModule::class])
abstract class ActivityMainModule {
#Binds
#IntoMap
#ClassKey(MainActivity::class)
abstract fun bindAndroidInjector(factory: MainActivityComponent.Factory): AndroidInjector.Factory<*>
}
#Module
object MainModule {
#Singleton
#Provides
#JvmStatic
fun provideMainViewModelFactory(nutritionRepository: NutritionRepository): MainViewModel.Factory {
return MainViewModel.Factory(nutritionRepository)
}
#Provides
#JvmStatic
fun provideMainViewModel(
viewModelFactory: MainViewModel.Factory,
fragmentActivity: FragmentActivity
): MainViewModel {
return ViewModelProviders.of(fragmentActivity, viewModelFactory)
.get(MainViewModel::class.java)
}
}
#ActivityScope
#Subcomponent
interface MainActivityComponent : AndroidInjector<MainActivity> {
#Subcomponent.Factory
interface Factory : AndroidInjector.Factory<MainActivity> {}
}
class MainActivity : DaggerAppCompatActivity() {
#Inject
lateinit var mainViewModel: MainViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
mainViewModel.liveDataFoodAnalysis.observe(this, Observer { food ->
Log.d("Food answer", food.uri)
})
mainViewModel.getFoodAnalysisResponse("egg")
}
}
You will have to annotate FeedMeApplicationComponent with #Singleton. because both NetworkModule and NutritionModule define #Provides functions scoped with #Singleton, any component that uses these modules must also specify its own scope as #Singleton.
From Dagger docs: -
Since Dagger 2 associates scoped instances in the graph with instances
of component implementations, the components themselves need to
declare which scope they intend to represent. For example, it wouldn’t
make any sense to have a #Singleton binding and a #RequestScoped
binding in the same component because those scopes have different
lifecycles and thus must live in components with different lifecycles.
To declare that a component is associated with a given scope, simply
apply the scope annotation to the component interface.