I have problem with my project when try add firebaseFirestore in constructor of my repository, "error: cannot find symbol
return DaggerBraziliexApplication_HiltComponents_SingletonC.builder()"
and with that I can't build the project, how should I proceed with the injection in this case?
Interface
interface NewsRepository {
suspend fun fetchNewsFromApi(
query: String,
date: String,
apiKey: String
): Flow<Result<List<NewsModelView>>>
}
RepositoryImpl
class NewsRepositoryImpl #Inject constructor(
private val apiService: NewsService,
private val newsMapper: NewsMapper,
private val firestore: FirebaseFirestore
): NewsRepository {
override suspend fun fetchNewsFromApi(
query: String,
date: String,
apiKey: String
): Flow<Result<List<NewsModelView>>> {
//TODO Implement API Request
}
private fun saveDataInFirestore(modelView: List<NewsModelView>, date: String) {
//TODO use firebaseFirestore do save response
}
}
Hilt Module:
#Module
#InstallIn(SingletonComponent::class)
object NetworkModule {
private const val baseUrl = "https://newsapi.org/v2/"
#Provides
#Singleton
#Named("news_api")
fun provideRetrofit(): Retrofit{
return Retrofit.Builder()
.baseUrl(baseUrl)
.addConverterFactory(GsonConverterFactory.create())
.build()
}
#Provides
#Singleton
fun provideNewsService(#Named("news_api") retrofit: Retrofit): NewsService{
return retrofit.create(NewsService::class.java)
}
#Provides
#Singleton
fun provideFirestore() = FirebaseFirestore.getInstance()
}
#Module
#InstallIn(ViewModelComponent::class)
abstract class RemoteModule {
#Binds
#ViewModelScoped
abstract fun bindRemoteRepository(remoteRepository: NewsRepositoryImpl): NewsRepository
}
Related
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 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
) {
....
}
AppModule.kt
#Module
#InstallIn(SingletonComponent::class)
object AppModule {
#Provides
#Singleton
fun provideTaskDatabase(
app: Application,
callback: RoomDatabase.Callback
) = Room.databaseBuilder(app,TaskDatabase::class.java,"task_db")
.fallbackToDestructiveMigration()
.addCallback(callback)
.build()
#Provides
#Singleton
fun provideTaskDao(appDatabase: TaskDatabase): TaskDao {
return appDatabase.getDao()
}
#ApplicationScope
#Provides
#Singleton
fun provideApplicationScope() = CoroutineScope(SupervisorJob())
#Retention(AnnotationRetention.RUNTIME)
#Qualifier
annotation class ApplicationScope
}
TaskViewModel.kt
#HiltViewModel
class TaskViewModel #Inject constructor(
private val taskDao: TaskDao
): ViewModel() {
val searchQuery = MutableStateFlow("")
val sortOrder = MutableStateFlow(SortOrder.BY_DATE)
val hideComplited = MutableStateFlow(false)
#ExperimentalCoroutinesApi
private val taskFlow = combine(
searchQuery,
sortOrder,
hideComplited){
query,sortorder,hidecomplited ->
Triple(query,sortorder,hidecomplited)
}
.flatMapLatest {(query,sortorder,hidecomplicated) ->
taskDao.getTasks(query,sortorder,hidecomplicated)
}
#ExperimentalCoroutinesApi
val tasks = taskFlow.asLiveData()
}
enum class SortOrder{ BY_DATE,BY_NAME}
Callback.class
#Database(entities = [Task::class],version = 1)
abstract class TaskDatabase: RoomDatabase() {
abstract fun getDao(): TaskDao
class CallBack #Inject constructor(
private val database: Provider<TaskDatabase>,
#AppModule.ApplicationScope private val applicationScope: CoroutineScope
): RoomDatabase.Callback(){
override fun onCreate(db: SupportSQLiteDatabase) {
super.onCreate(db)
val dao = database.get().getDao()
applicationScope.launch {
dao.insert(Task("Go to shopping"))
dao.insert(Task("Go to job"))
dao.insert(Task("playing football",important = true))
dao.insert(Task("Programming",checked = true))
dao.insert(Task("buy something"))
}
}
}
}
build
C:\Users\User\AndroidStudioProjects\ToDoList\app\build\generated\source\kapt\debug\com\example\todolist\TodoApplication_HiltComponents.java:127: error: [Dagger/MissingBinding] androidx.room.RoomDatabase.Callback cannot be provided without an #Provides-annotated method.
public abstract static class SingletonC implements TodoApplication_GeneratedInjector,
^
androidx.room.RoomDatabase.Callback is injected at
com.example.todolist.di.AppModule.provideTaskDatabase(�, callback)
com.example.todolist.room.TaskDatabase is injected at
com.example.todolist.di.AppModule.provideTaskDao(appDatabase)
com.example.todolist.room.TaskDao is injected at
com.example.todolist.ui.task.TaskViewModel(taskDao)
com.example.todolist.ui.task.TaskViewModel is injected at
com.example.todolist.ui.task.TaskViewModel_HiltModules.BindsModule.binds(vm)
#dagger.hilt.android.internal.lifecycle.HiltViewModelMap java.util.Map<java.lang.String,javax.inject.Provider<androidx.lifecycle.ViewModel>> is requested at
dagger.hilt.android.internal.lifecycle.HiltViewModelFactory.ViewModelFactoriesEntryPoint.getHiltViewModelMap() [com.example.todolist.TodoApplication_HiltComponents.SingletonC ? com.example.todolist.TodoApplication_HiltComponents.ActivityRetainedC ? com.example.todolist.TodoApplication_HiltComponents.ViewModelC]warning: The following options were not recognized by any
For you use callback: RoomDatabase.Callback as parameter in fun provideTaskDatabase you need to tell dagger how to create such object.
#Provides
fun provideCallback() = object : RoomDatabase.Callback() {
//override and implement methods
}
I have 3 repositories:
interface MainRepository {
...
}
interface LocalRepository {
...
}
interface WebRepository {
...
}
Each Repository has it's own implementation:
#Singleton
class MainRepositoryImpl #Inject constructor(
private val localRepository: LocalRepository,
private val webRepository: WebRepository
) : MainRepository {
...
}
#Singleton
class LocalRepositoryImpl #Inject constructor(
private val localMapper: LocalMapper
private val popularMovieDao: PopularMovieDao
) : LocalRepository {
...
}
#Singleton
class WebRepositoryImpl #Inject constructor(
private val webMapper: WebMapper,
private val popularMovieApi: PopularMovieApi
) : WebRepository {
...
}
As you can see, MainRepository needs to have both other repositories injected into it, however,I can't really figure out how to do it.
Of course I can inject it with type of LocalRepositoryImpl or WebRepositoryImpl but I want to inject it with type of LocalRepository or WebRepository for more generalized approach.
Here is the module that I tried writing:
#InstallIn(ApplicationComponent::class)
#Module
object Module {
#Singleton
#Provides
fun provideWebRepository(): WebRepository {
return WebRepositoryImpl(mapper = WebMapper(), popularMovieApi = PopularMovieApi.getInstance())
}
#Singleton
#Provides
fun provideLocalRepository(): LocalRepository {
return LocalRepositoryImpl(mapper = LocalMapper(), // Here I can't really
// figure out how to get #Dao since it requires DB
// which requires context and etc
// which makes me think that I've got completely wrong approach to this)
}
}
My Module of LocalData:
#InstallIn(ApplicationComponent::class)
#Module
object LocalDataSourceModule {
#Singleton
#Provides
fun provideMainDatabase(#ApplicationContext context: Context): MainDatabase = MainDatabase.getInstance(context)
#Provides
fun providePopularMovieDao(mainDatabase: MainDatabase): PopularMovieDao = mainDatabase.popularMovieDao()
}
My Module of WebData:
#InstallIn(ApplicationComponent::class)
#Module
object RemoteDataSourceModule {
#Singleton
#Provides
fun providePopularMovieApi(): PopularMovieApi = PopularMovieApi.getInstance()
}
How do I properly Inject Implementations that I have (LocalRepositoryImpl & WebRepositoryImpl) while maintaining types of interfaces(LocalRepository & `WebRepository)??
Use #Binds. Instead of your object Module use following module:
#InstallIn(ApplicationComponent::class)
#Module
interface Module {
#Binds
fun bindWebRepository(repository: WebRepositoryImpl): WebRepository
#Binds
fun bindLocalRepository(repository: LocalRepositoryImpl): LocalRepository
}
It tells Dagger that if you need WebRepository dependency then it must provide WebRepositoryImpl and the same for LocalRepository and LocalRepositoryImpl.
What is the use case for #Binds vs #Provides annotation in Dagger2
Your Repositories
interface MainRepository {
...
}
interface LocalRepository {
...
}
interface WebRepository {
...
}
Implementation (No #Inject or #Singleton here!)
class MainRepositoryImpl constructor(
private val localRepository: LocalRepository,
private val webRepository: WebRepository
) : MainRepository {
...
}
class LocalRepositoryImpl constructor(
private val localMapper: LocalMapper
private val popularMovieDao: PopularMovieDao
) : LocalRepository {
...
}
class WebRepositoryImpl constructor(
private val webMapper: WebMapper,
private val popularMovieApi: PopularMovieApi
) : WebRepository {
...
}
Di.Module (Repository Module)
#Module
#InstallIn(ApplicationComponent::class)
object RepositoryModule {
#Singleton
#Provides
fun provideMainRepository(
localRepository: LocalRepository,
webRepository: WebRepository
): MainRepository = MainRepositoryImpl(localRepository, webRepository)
#Singleton
#Provides
fun provideLocalRepository(
localMapper: LocalMapper,
popularMovieDao: PopularMovieDao
): LocalRepository = LocalRepositoryImpl(localMapper, popularMovieDao)
#Singleton
#Provides
fun provideWebRepository(
webMapper: WebMapper,
popularMovieApi: PopularMovieApi
): WebRepository = WebRepositoryImpl(webMapper, popularMovieApi)
Try this and tell me if it worked. Since you have provided all your Repositories with #Provides, Dagger-Hilt knows how to create them. Are you using Room for your localDatabase? If yes, then creating the Database like you did might not be right. If you're not using Room that you should start to, as it makes your life easier.
Here is the right way to create a room database with dagger hilt:
Entity Module
#Entity(tableName = "exampleTableName")
data class ExampleEntity(
#PrimaryKey(autoGenerate = true)
val id: Int,
// ... whatever you need
val header: String = "",
val title: String = "",
val description: String = "",
)
ExampleDatabase
#Database(entities = [ExampleEntity::class], version = 1)
abstract class ExampleDatabase : RoomDatabase() {
abstract fun exampleDao(): ExampleDao
companion object {
const val DATABASE_NAME = "example_db"
}
}
DAO
#Dao
interface DocumentDao {
#Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert(exampleEntity: List<ExampleEntity>)
#Query("SELECT * FROM exampleTableName")
suspend fun getList(): List<ExampleEntity>
}
Room Di.Module
#Module
#InstallIn(ApplicationComponent::class)
object RoomModule {
#Singleton
#Provides
fun provideExampleDB(#ApplicationContext context: Context): ExampleDatabase = Room.databaseBuilder(
context,
ExampleDatabase::class.java,
ExampleDatabase.DATABASE_NAME,
).fallbackToDestructiveMigration().build()
#Singleton
#Provides
fun provideExampleDAO(exampleDatabase: ExampleDatabase): ExampleDao = exampleDatabase.exampleDao()
}
How do I add a Repository to the Dagger Room Module? My Repository uses application when I try to add this class to a Module I get an error. How do I properly embed this repository in Dagger so that later I can do an injection in my ViewModel class? Why the first two functions are normally implemented in the Room Module and the last one is not. Any help
My Repository:
class ContactRepository (application: Application) {
private var contactDao: ContactDao
private var allContacts: MutableLiveData<ArrayList<Contact>>
companion object {
#Volatile
private var INSTANCE: ContactRepository? = null
fun getInstance(application: Application): ContactRepository {
return INSTANCE ?: getInstance(application)
}
}
init {
val database: ContactDatabase? = ContactDatabase.getInstance(application.applicationContext)
contactDao = database!!.contactDao()
allContacts = contactDao.getAllContact()
}
fun insert(contact: Contact) {
InsertContactAsyncTask().execute(contact)
}
fun updateAll(contactsModel: List<ContactsModel>) {
}
fun update(contact: Contact) {
}
fun delete(contact: Contact) {
}
fun getAllContact(): MutableLiveData<ArrayList<Contact>> {
return allContacts
}
class InsertContactAsyncTask : AsyncTask<Contact, Unit, Unit>() {
private val contactDao: ContactDao? = null
override fun doInBackground(vararg param: Contact) {
contactDao?.insert(param[0])
}
}
}
RoomModule:
#Module
class RoomModule {
private lateinit var contactDatabase: ContactDatabase
fun RoomModule(application: Application) {
contactDatabase = Room.databaseBuilder<ContactDatabase>(application, ContactDatabase::class.java, "contact_database")
.build()
}
#Singleton
#Provides
fun providesRoomDatabase(): ContactDatabase {
return contactDatabase
}
#Singleton
#Provides
fun providesContactDao(contactDatabase: ContactDatabase): ContactDao {
return contactDatabase.contactDao()
}
#Provides
fun providesContactRepository(application: Application): ContactRepository {
return ContactRepository(application)
}
}
Exception:
public abstract interface ApplicationComponent {
^
android.app.Application is injected at com.infernal93.phonebookappmvvmanddagger.di.modules.RoomModule.providesContactRepository(application)
com.infernal93.phonebookappmvvmanddagger.room.ContactRepository is injected at
com.infernal93.phonebookappmvvmanddagger.viewmodels.ContactsViewModel(contactRepository)
com.infernal93.phonebookappmvvmanddagger.viewmodels.ContactsViewModel is injected at
com.infernal93.phonebookappmvvmanddagger.di.modules.ViewModelModule.bindViewModel(viewModel)
java.util.Map<java.lang.Class<? extends androidx.lifecycle.ViewModel>,javax.inject.Provider<androidx.lifecycle.ViewModel>> is injected at
com.infernal93.phonebookappmvvmanddagger.viewmodels.ViewModelFactory(viewModelsMap)
AppComponent:
#Singleton
#Component(modules = [ContextModule::class, NetworkModule::class, RoomModule::class])
interface ApplicationComponent {
fun inject(activity: ContactListActivity)
fun inject(activity: AddContactActivity)
fun inject(app: Application)
}
ViewNodel:
class ContactsViewModel #Inject constructor(private val contactRepository: ContactRepository) : ViewModel() {
fun getAllContacts(): MutableLiveData<ArrayList<Contact>>{
val contacts = contactRepository.getAllContact()
return contacts
}
#Singleton
#Component(modules = [ContextModule::class, NetworkModule::class, RoomModule::class])
interface ApplicationComponent {
fun inject(app: App)
fun inject(activity: ContactListActivity)
fun inject(activity: AddContactActivity)
fun inject(viewModel: ContactsViewModel)
fun inject(apiRepository: ApiRepository)
// For fix Room compile bugs
fun contactDatabase(): ContactDatabase
fun contactDao(): ContactDao
fun contactRepository(): RoomRepository
}