Inject retrofit android kotlin - android

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

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.
}

Related

Dagger/MissingBinding issue, unable to inject in BaseApplication

I have implemented Hilt on my app. I am getting this error for the first time. Does anyone have an idea how to go about it?
weatherstackapp/utils/BaseApplication_HiltComponents.java:128: error:
[Dagger/MissingBinding]
com.malinikali.weatherstackapp.utils.BaseApplication cannot be provided
without an #Inject constructor or an #Provides-annotated method.
public abstract static class SingletonC implements
BaseApplication_GeneratedInjector,
^
Below is my BaseApplication class
#HiltAndroidApp
class BaseApplication : Application()
Below is my AppModule class
#Module
#InstallIn(SingletonComponent::class)
object AppModule {
private val client = OkHttpClient.Builder().apply {
addInterceptor(ApiInterceptor())
}.build()
#Singleton
#Provides
fun provideContext(application: BaseApplication): Context {
return application.applicationContext
}
#Provides
fun providesBaseUrl() = Constants.BASE_URL
#Provides
#Singleton
fun provideRetrofitInstance(BASE_URL:String):ApiService =
Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl(BASE_URL)
.client(client)
.build()
.create(ApiService::class.java)
}
Hilt injects Application and #ApplicationContext Context by default. If this is the only binding that uses BaseApplication, you can simply change the parameter to one of these default bindings instead:
#Singleton
#Provides
fun provideContext(application: Application): Context {
return application.applicationContext
}
If you really need a BaseApplication binding, you can create a #Provides method based on one of the included bindings:
#Provides
fun provideBaseApplication(application: Application): BaseApplication {
return application as BaseApplication
}

Hilt - Should I #Provides my Base URL or API Key?

I am new to Hilt and have negligible experience in Dagger 2 as well.
I am now doing a simple sample project for my self-learning, and trying to apply Hilt.
I use Retrofit to call APIs.
So, I am trying to refactor my code into using Hilt:
#Module
#InstallIn(SingletonComponent::class)
object WeatherAPIModule {
private const val BASE_URL = "https://api.openweathermap.org/data/2.5/"
#Provides
fun provideHttpLoggingInterceptor(): HttpLoggingInterceptor {
return HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY
}
}
#Provides
fun provideOkHttpClient(loggingInterceptor: HttpLoggingInterceptor): OkHttpClient {
return OkHttpClient.Builder()
.addInterceptor(loggingInterceptor)
.build()
}
#Provides
fun provideGsonConverterFactory(): GsonConverterFactory {
return GsonConverterFactory.create()
}
#Provides
fun provideRetrofit(okHttpClient: OkHttpClient, gsonConverterFactory: GsonConverterFactory): Retrofit {
return Retrofit.Builder()
.baseUrl(BASE_URL)
.client(okHttpClient)
.addConverterFactory(gsonConverterFactory)
.build()
}
#Provides
fun provideWeatherService(retrofit: Retrofit): OpenWeatherService {
return retrofit.create(OpenWeatherService::class.java)
}
val weatherApiKey by lazy {
ApiKeyStore.getWeatherApiKey()
}
}
My question is, Should I use #Provides on Base URL and API Key as well?
Why do I ask
According to my understanding, if I have a class like this:
class SomeClass #Inject constructor() {
#Inject lateinit var someString: String
...
}
Hilt will try to find modules that provides String, and inject that String here. But clearly that's not what I want.
What should be the best way to do this?
Especially for weatherApiKey. I think I should not have leave it as an ordinary function, because that would lose the point of using Hilt at all.

Cannot be provided without an #Inject constructor or an #Provides-annotated method in dagger kotlin

I am converting one my project into kotlin source code.
While building the dagger I am encountered with this problem
cannot be provided without an #Inject constructor or an #Provides-annotated method.
I have 2 modules as below
#Module
class AppClient {
#Provides
#Singleton
fun provideHttpCache(application: Application): Cache {
val cacheSize = 10 * 1024 * 1024
return Cache(application.cacheDir, cacheSize.toLong())
}
#Provides
#Singleton
fun provideGson(): Gson {
val gsonBuilder = GsonBuilder()
gsonBuilder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
// gsonBuilder.excludeFieldsWithoutExposeAnnotation();
return gsonBuilder.create()
}
#Provides
#Singleton
fun provideOkhttpClient(cache: Cache): OkHttpClient {
val interceptor = HttpLoggingInterceptor()
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
val client = OkHttpClient.Builder()
client.cache(cache)
client.addInterceptor(interceptor)
return client.build()
}
#Provides
#Singleton
fun provideRetrofit(gson: Gson, okHttpClient: OkHttpClient): Retrofit {
return Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create(gson))
.baseUrl(NetworkConstant.BASE_URL)
.client(okHttpClient)
.build()
}
#Provides
#Singleton
fun provideApiCall(retrofit: Retrofit): NetworkCall {
return retrofit.create(NetworkCall::class.java)
}
#Provides
#Singleton
fun provideSharedPreference(application: Application): SharedPreferences {
return application.applicationContext.getSharedPreferences(
application.getString(R.string.shared_pref_name),
MODE_PRIVATE
)
}
#Provides
#Singleton
fun provideSharedPreferenceEditor(sharedPreferences: SharedPreferences): SharedPreferences.Editor {
return sharedPreferences.edit()
}
#Provides
#Singleton
fun provideAppPresenter(
sharedPreferences: SharedPreferences, editor: SharedPreferences.Editor,
apiCall: NetworkCall): AppPresenter {
return AppPresenter(sharedPreferences, editor, apiCall)
}
}
And the second module
#Module
class AppModule(val application: MyApplication) {
#Provides
#Singleton
internal fun provideApplication(): MyApplication {
return application
}
}
Application class
class MyApplication : Application() {
lateinit var mApiComponent: AppComponent
override fun onCreate() {
super.onCreate()
mApiComponent = DaggerAppComponent.builder()
.appModule(AppModule(this))
.appClient(AppClient())
.build()
}
fun getAppComponent(): AppComponent {
return mApiComponent
}
}
And the component interface
#Singleton
#Component(modules = [AppModule::class, AppClient::class])
interface AppComponent {
fun inject(activity: LoginActivity)
}
Can someone help out as to what's wrong here?
It seems I was passing the wrong application class. So silly of me

How should I use Dagger2 with Retrofit?

I'm working in a project and I need to inject Retrofit object with Dagger 2. I search info but I only have been able to do a few steps and now I don't know how to continue:
My component:
#Singleton
#Component(modules = arrayOf(NetworkModule::class))
interface NetworkComponent {
fun inject(foo: TheApplication)
}
My module:
#Module
class NetworkModule {
#Provides
#Singleton
fun provideRetrofit(): Retrofit {
return Retrofit.Builder()
.baseUrl("https://api.chucknorris.io/")
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build()
}
}
And now I see I have to make a Class who extends from Application for init the Component and make an #Inject next:
class TheApplication: Application() {
override fun onCreate() {
super.onCreate()
val net: NetworkModule = Dagger...
}
}
Theoretically when I put the Dagger... it should be DaggerNetworkComponent but I rebuild project and still missing.
Anyone can explain to me for what is the Application.class made for and how I can continue?
Try this
AppComponent.kt
#Component(modules = [NetworkModule::class])
#Singleton
interface AppComponent {
fun inject(app: MyApp)
}
NetworkModule.kt
#Module
class NetworkModule {
#Singleton
#Provides
fun provideRetrofit(okHttpClient: OkHttpClient): Retrofit {
return Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl("YOUR_BASE_URL")
.client(okHttpClient)
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build()
}
#Singleton
#Provides
fun provideApi(retrofit: Retrofit): WebApi {
return retrofit.create(WebApi::class.java)
}
#Singleton
#Provides
fun provideOkHttpClient(
interceptors: ArrayList<Interceptor>
): OkHttpClient {
val clientBuilder = OkHttpClient.Builder()
.followRedirects(false)
interceptors.forEach {
clientBuilder.addInterceptor(it)
}
return clientBuilder.build()
}
#Singleton
#Provides
fun provideInterceptors(): ArrayList<Interceptor> {
val interceptors = arrayListOf<Interceptor>()
val loggingInterceptor = HttpLoggingInterceptor().apply {
level = if (BuildConfig.DEBUG) {
HttpLoggingInterceptor.Level.BODY
} else {
HttpLoggingInterceptor.Level.NONE
}
}
interceptors.add(loggingInterceptor)
return interceptors
}
}
MyApp.kt
class MyApp : Application() {
companion object {
lateinit var instance: MyApp
private set
}
lateinit var appComponent: AppComponent
private set
override fun onCreate() {
super.onCreate()
instance = this
initComponent()
}
private fun initComponent() {
appComponent = DaggerAppComponent.builder()
.build()
appComponent.inject(this)
}
}
AndroidManifest.xml
<application
android:name=".MyApp"
....

Provide application context with dagger 2

Good day all,
I would like to provide application context for my AppModule class.
I would like to have a PrefsHelper be provided through out the application like I do with my ApiService class.
The code for my AppModule:
#Module
#Suppress("unused")
object AppModule {
#Provides
#Reusable
#JvmStatic
internal fun provideApiService(retrofit: Retrofit): ApiService {
return retrofit.create(ApiService::class.java)
}
/**
* Provides the Retrofit object.
* #return the Retrofit object
*/
#Provides
#Reusable
#JvmStatic
internal fun provideRetrofitInterface(): Retrofit {
val interceptor: HttpLoggingInterceptor = HttpLoggingInterceptor().apply {
this.level = HttpLoggingInterceptor.Level.BODY
}
val client: OkHttpClient = OkHttpClient.Builder().apply { this.addInterceptor(interceptor) }.build()
return Retrofit.Builder()
.baseUrl(BuildConfig.BASE_URL)
.client(client)
.addCallAdapterFactory(RxJava2CallAdapterFactory.createWithScheduler(Schedulers.io()))
.addConverterFactory(GsonConverterFactory.create())
.build()
}
The way I have seen it done before (In Java) is create a constructor and pass in the application context in that way. Kotlin doesnt allow that with an object
How can I have the context be provided in this class allowing me to provide PrefsHelper?
You could also use the BindsInstance annotation in your AppComponent.
So your AppComponent would look something like this:
#Singleton
#Component(modules = YOUR_MODULES)
interface AppComponent {
//Whatever injections you have
#Component.Builder
interface Builder {
fun build(): AppComponent
#BindsInstance
fun application(Application application): Builder
}
}
Then you just add the new methods to your AppComponent creation in your Application class.
DaggerAppComponent.builder().application(this).build()
Change your AppModule to something like this:
#Module
class AppModule(private val application: Application) {
#Singleton
#Provides
internal fun provideApplication(): Application = application
#Singleton
#Provides
internal fun providePrefs(application: Application): YourPref {
return YourPref(application)
}
}

Categories

Resources