I am new to Android dev and need to build my first project for university. The university was using the old XML version with Java, but I wanted to learn Compose so I learnt Kotlin.
Now after getting everything setup, I am trying to use hiltViewModel() method to inject the view model inside the composable function and I am getting an error. I watched this tutorial:
I am also getting an error: [Dagger/MissingBinding] exception. ac.gre.mxpenseresit.data.repository.TripRepository cannot be provided without an @Inject constructor or an @Provides-annotated method
Here is the code for the data layer:
Database class:
@Database(
entities = [
Trip::class,
Expense::class,
],
version = 1
)
abstract class MExpenseDb : RoomDatabase() {
abstract val tripDao: TripDao
}
Dao:
@Dao
interface TripDao {
/**
* Gets all trips
*/
@Query("SELECT * FROM Trip")
fun getTrips(): Flow<List<Trip>>
/**
* Gets trips based on a named search,
* This functionality can be extended later
*/
@Query("SELECT * FROM Trip t WHERE t.name LIKE :name")
suspend fun getTripsBySearchName(name: String)
/**
* Gets trip by Id
*/
@Query("SELECT * FROM Trip WHERE Trip.id = :id")
suspend fun getTripById(id: Long)
/**
*
*/
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun saveTrip(trip: Trip): Long
@Delete
suspend fun deleteTrip(trip: Trip)
}
Trip Repository Contract (Interface):
interface TripRepositoryContract {
fun getTrips(): Flow<List<Trip>>
suspend fun getTripById(id: Long): Trip?
suspend fun getTripsBySearchName(keyword: String): List<Trip>
suspend fun saveTripLocal(trip: Trip)
suspend fun saveTripRemote(trip: Trip)
suspend fun deleteTrip(trip: Trip)
}
Trip Repository implementation:
class TripRepository (val tripDao: TripDao) : TripRepositoryContract {
override fun getTrips(): Flow<List<Trip>> {
return tripDao.getTrips();
}
override suspend fun getTripById(id: Long): Trip? {
TODO("Not yet implemented")
}
override suspend fun getTripsBySearchName(keyword: String): List<Trip> {
TODO("Not yet implemented")
}
override suspend fun saveTripLocal(trip: Trip) {
TODO("Not yet implemented")
}
override suspend fun saveTripRemote(trip: Trip) {
TODO("Not yet implemented")
}
override suspend fun deleteTrip(trip: Trip) {
TODO("Not yet implemented")
}
}
Now the hiltViewModel() method works correctly, but I am getting a MExpenseDb_impl class not found
I looked at this stack overflow question: Android room persistent: AppDatabase_Impl does not exist
And it says to use the kept dependency, I already have the same thing with annotationProcessing so I'm not sure if this is an issue
All the tutorials online are either too long & irrelevant or too vague and I am trying to gain a deeper understanding about how this works. Any advice would be appreciated
CodePudding user response:
To provide the TripRepository
you need to create a class with Hilt's Bind functions. Example:
@Module
@InstallIn(SingletonComponent::class)
interface RepositoryModule {
@Binds
@Singleton
fun bindTripRepository(
repository: TripRepository // here the class
): TripRepositoryContract // here the interface that the class implements
}
It is also necessary to modify the TripRepository. You must add the @Inject constructor()
(even if your class doesn't use any dependencies) so that Hilt can create the class.
In your case it will look like this:
class TripRepository @Inject constructor(
val tripDao: TripDao
) : TripRepositoryContract {
// ...
}
The last change is in your MainModule:
@Module
@InstallIn(SingletonComponent::class)
object MainModule {
// providing the db implementation normally
@Provides
@Singleton
fun provideDb(application: Application): MExpenseDb {
return Room.databaseBuilder(
application,
MExpenseDb::class.java,
"MExpenseDb"
).build()
}
// providing your dao interface to be injected into TripRepository
@Provides
@Singleton
fun provideTripDao(
mExpenseDb: MExpenseDb
): TripDao = mExpenseDb.tripDao
}
Note that I changed from ActivityComponent
to SingletonComponent
in the hilt's modules, in both cases we want them to be singleton throughout the project, not just a component created for activity (which can also be singleton).
See components life cycles here.
I also recommend upgrading the Hilt version in your project, because you are using a very old one. The newest version is 2.42
.
I think this video can help you better understand some things about Hilt. And there is also this repository of a project that uses Hilt together with Room that can be useful for you to consult.
Important edit:
In the TripListVm
you are using the private val tripRepository: TripRepository
(your class that implements the TripRepositoryContract), it is not recommended to directly inject the implementation class, instead you should inject the interface (TripRepositoryContract) and let Hilt take care of providing the implementation of it. Because that's what we taught Hilt to do in the RepositoryModule
.
So to make it ideal, the TripListVm
would look like this:
@HiltViewModel
class TripListVm @Inject constructor(
private val tripRepositoryContract: TripRepositoryContract
) : ViewModel() {
// ...
}