Problem with Injection of DAO
RuralNative opened this issue · 1 comments
First off, I will provide the codes for my Room Database, DAO, Repository, DI, ViewModel, and Composable
Database:
`package com.ruralnative.handsy.data
import android.content.Context
import androidx.room.Room
import androidx.room.RoomDatabase
import androidx.room.Database
import com.ruralnative.handsy.data.dao.AlphabetLessonDao
import com.ruralnative.handsy.data.dao.PhrasesLessonDao
import com.ruralnative.handsy.data.dao.UserDao
import com.ruralnative.handsy.data.entities.AlphabetLesson
import com.ruralnative.handsy.data.entities.PhrasesLesson
import com.ruralnative.handsy.data.entities.User
import dagger.Binds
import javax.inject.Inject
import javax.inject.Singleton
@database(
entities = [
User::class,
AlphabetLesson::class,
PhrasesLesson::class
],
version = 1,
exportSchema = false
)
abstract class AppDatabase : RoomDatabase() {
abstract fun userDao(): UserDao
abstract fun alphabetLessonDao(): AlphabetLessonDao
abstract fun phrasesLessonDao(): PhrasesLessonDao
}`
UserDAO:
``
package com.ruralnative.handsy.data.dao
import androidx.room.Dao
import androidx.room.Delete
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import androidx.room.Update
import com.ruralnative.handsy.data.entities.PhrasesLesson
import com.ruralnative.handsy.data.entities.User
import kotlinx.coroutines.flow.Flow
@dao
interface UserDao {
@Query("SELECT * from user")
fun selectAllUsers(): Flow<List<User>>
@Query("SELECT * from user WHERE id = :userID")
fun selectUserById(userID: Int): Flow<User>
@Query("SELECT COUNT(*) from user")
fun countUsers(): Flow<Int>
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertUser(user: User)
@Update
suspend fun updateUser(user: User)
@Query("UPDATE user SET user_name = :userName WHERE id = :userID")
suspend fun updateUserName(userName: String?, userID: Int)
@Query("UPDATE user SET is_new_user = :boolValue WHERE id = :userID")
suspend fun updateUserStatus(boolValue: Int, userID: Int)
@Query("UPDATE user SET progression_level = :userLevel WHERE id = :userID")
suspend fun updateUserProgressionLevel(userLevel: Int, userID: Int)
@Delete
suspend fun deleteUser(user: User)
}
``
Repository:
`package com.ruralnative.handsy.data.repository
import androidx.annotation.WorkerThread
import com.ruralnative.handsy.data.dao.UserDao
import com.ruralnative.handsy.data.entities.User
import com.ruralnative.handsy.di.qualifiers.UserDAO
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.flow
import javax.inject.Inject
import javax.inject.Singleton
class UserRepository @Inject constructor(
private val dao: UserDao
) {
val allUsers: Flow<List> = dao.selectAllUsers()
fun getUserByID(userID: Int): Flow<User> {
return dao.selectUserById(userID)
}
fun isThereNoUser(): Flow<Boolean> = flow {
val numberOfUsers: Int? = dao.countUsers().firstOrNull()
var isUserEmpty = true
if (numberOfUsers == 0) {
isUserEmpty = true
} else if (numberOfUsers != 0) {
isUserEmpty = false
}
emit(isUserEmpty)
}
suspend fun insertUser(user: User) {
dao.insertUser(user)
}
suspend fun insertNewUser(id: Int, name: String) {
val user = User(
id,
name,
0,
1
)
dao.insertUser(user)
}
suspend fun updateUser(user: User) {
dao.updateUser(user)
}
suspend fun updateUserNameWithID(userName: String?, userID: Int) {
dao.updateUserName(userName, userID)
}
suspend fun updateUserStatusWithID(boolValue: Int, userID: Int) {
dao.updateUserStatus(boolValue, userID)
}
suspend fun updateUserLevelWithID(userLevel: Int, userID: Int) {
dao.updateUserProgressionLevel(userLevel, userID)
}
suspend fun deleteUser(user: User) {
dao.deleteUser(user)
}
}`
ViewModel:
`package com.ruralnative.handsy.ui.entryUI
import android.util.Log
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.ruralnative.handsy.data.repository.UserRepository
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import javax.inject.Inject
@HiltViewModel
class EntryViewModel @Inject constructor(
private val repository: UserRepository
): ViewModel() {
fun checkUserCountAndNavigate(
navigateToInitial: () -> Unit,
navigateToMain: () -> Unit
) {
Log.d("ENTRY_SCREEN", "checkUserCountAndNavigate() EXECUTED")
viewModelScope.launch {
delay(5000)
val isThereNoUser: Boolean = repository.isThereNoUser().first()
if (!isThereNoUser) {
navigateToInitial()
} else {
navigateToMain()
}
}
}
}`
DatabaseModule (Hilt):
`package com.ruralnative.handsy.di
import android.content.Context
import androidx.room.Room
import com.ruralnative.handsy.data.AppDatabase
import com.ruralnative.handsy.data.dao.AlphabetLessonDao
import com.ruralnative.handsy.data.dao.PhrasesLessonDao
import com.ruralnative.handsy.data.dao.UserDao
import com.ruralnative.handsy.data.repository.AlphabetLessonRepository
import com.ruralnative.handsy.data.repository.PhrasesLessonRepository
import com.ruralnative.handsy.data.repository.UserRepository
import com.ruralnative.handsy.di.qualifiers.AlphabetDAO
import com.ruralnative.handsy.di.qualifiers.AlphabetRepo
import com.ruralnative.handsy.di.qualifiers.Database
import com.ruralnative.handsy.di.qualifiers.PhrasesDAO
import com.ruralnative.handsy.di.qualifiers.PhrasesRepo
import com.ruralnative.handsy.di.qualifiers.UserDAO
import com.ruralnative.handsy.di.qualifiers.UserRepo
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
@module
@Installin(SingletonComponent::class)
object DatabaseModule {
@Singleton
@Provides
@Database
fun provideLocalDatabase(
@ApplicationContext context: Context
): AppDatabase {
return Room.databaseBuilder(
context.applicationContext,
AppDatabase::class.java,
"app_database.db"
)
.fallbackToDestructiveMigration()
.createFromAsset("database.db")
.build()
}
@Singleton
@Provides
@UserDAO
fun provideUserDao(
@Database appDatabase: AppDatabase
): UserDao {
return appDatabase.userDao()
}
@Singleton
@Provides
@AlphabetDAO
fun provideAlphabetDao(
@Database appDatabase: AppDatabase
): AlphabetLessonDao {
return appDatabase.alphabetLessonDao()
}
@Singleton
@Provides
@PhrasesDAO
fun providePhrasesDao(
@Database appDatabase: AppDatabase
): PhrasesLessonDao {
return appDatabase.phrasesLessonDao()
}
}
`
ViewModelModule:
`package com.ruralnative.handsy.di
import com.ruralnative.handsy.data.dao.AlphabetLessonDao
import com.ruralnative.handsy.data.dao.PhrasesLessonDao
import com.ruralnative.handsy.data.dao.UserDao
import com.ruralnative.handsy.data.repository.AlphabetLessonRepository
import com.ruralnative.handsy.data.repository.PhrasesLessonRepository
import com.ruralnative.handsy.data.repository.UserRepository
import com.ruralnative.handsy.di.qualifiers.AlphabetDAO
import com.ruralnative.handsy.di.qualifiers.AlphabetRepo
import com.ruralnative.handsy.di.qualifiers.PhrasesDAO
import com.ruralnative.handsy.di.qualifiers.PhrasesRepo
import com.ruralnative.handsy.di.qualifiers.UserDAO
import com.ruralnative.handsy.di.qualifiers.UserRepo
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.components.ViewModelComponent
import dagger.hilt.android.scopes.ViewModelScoped
@module
@Installin(ViewModelComponent::class)
object ViewModelModule {
@Provides
@ViewModelScoped
@UserRepo
fun provideUserRepository(
@UserDAO dao: UserDao
): UserRepository {
return UserRepository(dao)
}
@Provides
@ViewModelScoped
@AlphabetRepo
fun provideAlphabetRepository(
@AlphabetDAO dao: AlphabetLessonDao
): AlphabetLessonRepository {
return AlphabetLessonRepository(dao)
}
@Provides
@ViewModelScoped
@PhrasesRepo
fun providePhrasesRepository(
@PhrasesDAO dao: PhrasesLessonDao
): PhrasesLessonRepository {
return PhrasesLessonRepository(dao)
}
}`
Here is the error:
`F:\Programming\Handsy\app\build\generated\hilt\component_sources\debug\com\ruralnative\handsy\Application_HiltComponents.java:141: error: [Dagger/MissingBinding] com.ruralnative.handsy.data.dao.UserDao cannot be provided without an @Provides-annotated method.
public abstract static class SingletonC implements Application_GeneratedInjector,
^
Missing binding usage:
com.ruralnative.handsy.data.dao.UserDao is injected at
com.ruralnative.handsy.data.repository.UserRepository(dao)
com.ruralnative.handsy.data.repository.UserRepository is injected at
com.ruralnative.handsy.ui.initialScreens.UserIntroViewModel(repository)
com.ruralnative.handsy.ui.initialScreens.UserIntroViewModel is injected at
com.ruralnative.handsy.ui.initialScreens.UserIntroViewModel_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.ruralnative.handsy.Application_HiltComponents.SingletonC ? com.ruralnative.handsy.Application_HiltComponents.ActivityRetainedC ? com.ruralnative.handsy.Application_HiltComponents.ViewModelC]`
Already stuck with this error for 3 weeks. I tried everything, nothing at all helped. Please help
Generally questions like this are better for StackOverflow.
However, I believe the issue is you are providing your UserDAO
using a qualifier here:
@Singleton
@Provides
@UserDAO
fun provideUserDao(
@Database appDatabase: AppDatabase
): UserDao {
return appDatabase.userDao()
}
but are injecting it without the @UserDao
qualifier here:
class UserRepository @Inject constructor(
private val dao: UserDao
) {
How to fix it is a bit up to you. You could either add a qualifier where you use it in UserRepository
or you could just remove the qualifier from the @Provides
. The reason I mention the latter is that qualifiers are used for differentiating bindings, so it'd usually make sense to use one if you had two different UserDaos, like @Red UserDao
and @Blue UserDao
. Something like @UserDAO UserDao
doesn't really differentiate much, so you might not need the qualifier in the first place if you only plan on having one UserDao
binding.