Search code examples
androidkotlinretrofitdagger-2

Inject retrofit android kotlin


ApiModule.kt

@Module
class ApiModule {

    @Provides
    @Singleton
    fun provideRetrofit(): Retrofit {
        return Retrofit.Builder()
            .baseUrl(BASE_URL)
            .addConverterFactory(GsonConverterFactory.create())
            .build()
    }

    @Provides
    @Singleton
    fun provideUserApi(retrofit: Retrofit): HeroesApi {
        return retrofit.create(HeroesApi::class.java)
    }


    @Provides
    @Singleton
    fun provideApiManager(): ApiManager {
        return ApiManager()
    }
}

ApiManager.kt

class ApiManager {

    @Inject
    lateinit var mRetrofit: Retrofit

    fun getAllHeroes(): MutableLiveData<Result<List<Hero>>> {
        val mHeroesApi = mRetrofit.create(HeroesApi::class.java)
        return NetworkHandler<List<Hero>>().makeCall(mHeroesApi.getAllHeroes())
    }
}

HeroesApi.kt

interface HeroesApi {

    @GET("/marvel")
    fun getAllHeroes(): Call<List<Hero>>

}

Error i am getting

Caused by: kotlin.UninitializedPropertyAccessException: lateinit property mRetrofit has not been initialized
        at com.hardik.repository.network.ApiManager.getAllHeroes(ApiManager.kt:17)
        at com.hardik.repository.Repository.getHeroesFromNetwork(Repository.kt:16)
        at com.hardik.androidtemplate.usecase.GetHeroesUseCase.execute(GetHeroesUseCase.kt:14)
        at com.hardik.androidtemplate.viewmodel.HeroListViewModel.<init>(HeroListViewModel.kt:9)

Let me know for more details


Solution

  • You don't need to inject Retrofit instance in your ApiManager. You already have Provide method for HeroesApi in your ApiModule so you can directly pass it. First change your ApiManager with:

    class ApiManager(private val mHeroesApi: HeroesApi) {
    
        fun getAllHeroes(): MutableLiveData<Result<List<Hero>>> {
            return NetworkHandler<List<Hero>>().makeCall(mHeroesApi.getAllHeroes())
        }
    }
    

    Then change your ApiModule

    @Module
    class ApiModule {
    
       // Rest code same as it is already
    
        @Provides
        @Singleton
        // Since you already have Provide method which provides HerosApi,
        // Dagger will automatically inject this below.
        fun provideApiManager(herosApi: HerosApi): ApiManager {
            return ApiManager(herosApi)
        }
    }
    

    OR

    You can simply change your ApiManager to have HorseApi injected into constructor and you won't need to have Provide method for ApiManager even. For this, change ApiManager with following:

    // Notice the @Inject before constructor
    class ApiManager @Inject constructor(private val mHeroesApi: HeroesApi) {
    
        fun getAllHeroes(): MutableLiveData<Result<List<Hero>>> {
            return NetworkHandler<List<Hero>>().makeCall(mHeroesApi.getAllHeroes())
        }
    }
    

    and then, you can remove Provide method for ApiManager from ApiModule because your ApiManager only needs HerosApi in constructor and you have defined Provide method which returns HerosApi so dagger already knows how to construct your ApiManager.

    So your ApiModule will finally look like this:

    @Module
    class ApiModule {
    
        @Provides
        @Singleton
        fun provideRetrofit(): Retrofit {
            return Retrofit.Builder()
                .baseUrl(BASE_URL)
                .addConverterFactory(GsonConverterFactory.create())
                .build()
        }
    
        @Provides
        @Singleton
        fun provideUserApi(retrofit: Retrofit): HeroesApi {
            return retrofit.create(HeroesApi::class.java)
        }
        // There's no need for providing ApiManager.
    }