I'm testing hilt with a simple project, what I want to achieve is to generate an instance of my MainViewModel with Hilt this is what I have done so far
MainActivity
#AndroidEntryPoint
class MainActivity : AppCompatActivity() {
...
}
MainFragment
#AndroidEntryPoint
class MainFragment : Fragment(),MainAdapter.OnTragoClickListener {
private val viewModel by activityViewModels<MainViewModel>()
...
}
MainViewModel
class MainViewModel #ViewModelInject constructor(private val repo:Repo):ViewModel(){
...
}
RepoImpl
class RepoImpl #Inject constructor(private val dataSource: DataSource): Repo {
...
}
DataSourceImpl
class DataSourceImpl #Inject constructor(private val tragosDao: TragosDao): DataSource{
...
}
Now , this is the architecture the app follows, here Repo and DataSource are simple interfaces that I use.
So after this I generate all that hilt requires to generate the instances
BaseApplication
#HiltAndroidApp
class BaseApplication: Application() {
}
AppModule
#Module
#InstallIn(ApplicationComponent::class)
object AppModule {
#Singleton
#Provides
fun provideRoomInstance(
#ApplicationContext context: Context
) = Room.databaseBuilder(
context,
AppDatabase::class.java,
"tabla_tragos")
.build()
#Singleton
#Provides
fun provideTragosDao(db: AppDatabase) = db.tragoDao()
}
The module above provides tragoDao() so I can access it in my DataSourceImpl, since I need an unique instance of this database I use #Singleton on its provide
Then I just create another module that will let hilt know about the implementations of the interfaces above
#Module
#InstallIn(ActivityRetainedComponent::class)
abstract class ActivityModule {
#Binds
abstract fun bindDataSource(dataSource:DataSourceImpl): DataSource
#Binds
abstract fun bindRepo(repo: RepoImpl): Repo
}
Since I need an instance of the MainViewModel , I scope this module with ActivityRetainedComponent
After compiling the app I get this error
java.lang.RuntimeException: Cannot create an instance of class
com.g.tragosapp.ui.viewmodel.MainViewModel
Dependencies
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
apply plugin: 'dagger.hilt.android.plugin'
dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.3.0'
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
//Navigation Components
implementation "androidx.navigation:navigation-fragment-ktx:2.3.0"
implementation "androidx.navigation:navigation-ui-ktx:2.3.0"
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
//ViewModel y LiveData
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
// KTX - Viewmodel Y Livedata
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0'
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.3.0-alpha05"
implementation "androidx.fragment:fragment-ktx:1.2.5"
implementation "androidx.activity:activity-ktx:1.1.0"
//utils
implementation 'com.github.bumptech.glide:glide:4.11.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'
//Corutinas
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.3"
//Retrofit
implementation 'com.squareup.retrofit2:retrofit:2.6.0'
implementation 'com.google.code.gson:gson:2.8.5'
implementation 'com.squareup.retrofit2:converter-gson:2.5.0'
implementation 'com.squareup.okhttp3:logging-interceptor:3.12.0'
implementation 'com.github.chrisbanes:PhotoView:2.3.0'
//Room
implementation 'androidx.room:room-ktx:2.2.5'
implementation "androidx.room:room-runtime:2.2.5"
kapt "androidx.room:room-compiler:2.2.5"
//Hilt
implementation "com.google.dagger:hilt-android:2.28-alpha"
kapt "com.google.dagger:hilt-android-compiler:2.28-alpha"
implementation 'androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha02'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}
I have also added
implementation "androidx.fragment:fragment-ktx:1.2.5"
implementation "androidx.activity:activity-ktx:1.1.0"
implementation "androidx.core:core:1.3.1"
which has not made any difference
class RepoImpl
Should be
#Singleton class RepoImpl
And same for DataSourceImpl
Then change #InstallIn(ActivityRetainedComponent::class) to #InstallIn(SingletonComponent::class) (used to be ApplicationComponent)
And also make sure to have all these deps (at the time of writing):
buildscript {
ext {
dagger_version = '2.41'
}
dependencies {
classpath "com.google.dagger:hilt-android-gradle-plugin:$dagger_version"
}
and
apply plugin: 'dagger.hilt.android.plugin'
apply plugin: 'kotlin-kapt'
implementation "com.google.dagger:dagger:$dagger_version"
kapt "com.google.dagger:dagger-compiler:$dagger_version"
implementation "com.google.dagger:hilt-android:$dagger_version"
kapt "com.google.dagger:hilt-android-compiler:$dagger_version"
kaptTest "com.google.dagger:hilt-android-compiler:$dagger_version"
kaptAndroidTest "com.google.dagger:hilt-android-compiler:$dagger_version"
kapt 'androidx.hilt:hilt-compiler:1.0.0'
Related
I am trying to get dagger component dependency in my AndroidTest package
Dependencies
implementation "com.google.dagger:dagger:$dagger_version"
kapt "com.google.dagger:dagger-compiler:$dagger_version"
debugImplementation 'androidx.test:runner:1.5.1'
debugImplementation 'androidx.test:rules:1.5.0'
debugImplementation "androidx.test:core-ktx:1.5.0"
kaptAndroidTest "com.google.dagger:dagger-compiler:$dagger_version"
Code in AndroidTest package
Component
#Singleton
#Component
interface TestUserComponent{
fun inject(myActivityTest: MyActivityTest)
}
Test file
class MyActivityTest {
lateinit var mTestUserComponent: TestUserComponent
#Before
fun setUp() {
mTestUserComponent = DaggerTestUserComponent.builder().build()
}
}
From above code I am not getting autogenerated file "DaggerTestUserComponent"
I have spent the hole week trying to add hilt dependency injection to my sample note application, android studio have been throwing on me error after an error.It got me mad, any way, in AppModule i have been trying to inject my room database to app repository and then my app repo to my use cases class and at the end injecting use cases class to my sharedViewModel
so this is my AppModule object:
#Module
#InstallIn(SingletonComponent::class)
object AppModule {
#Provides
#Singleton
fun provideNoteDatabase(app: Application): NoteDatabase {
return Room.databaseBuilder(
app,
NoteDatabase::class.java,
NoteDatabase.DATABASE_NAME
).build()
}
#Provides
#Singleton
fun provideNoteRepository(db: NoteDatabase): NotesRepo {
return RepoImplementation(db.noteDao())
}
#Provides
#Singleton
fun provideNoteUseCase(repo: NotesRepo): NoteUseCase {
return NoteUseCase(
getNotesUseCase = GetNotesUseCase(repo),
deleteNoteUseCase = DeleteNoteUseCase(repo),
updateNoteUseCase = UpdateNoteUseCase(repo),
insertNoteUseCase = InsertNoteUseCase(repo)
)
}
}
and this my Application class:
#HiltAndroidApp
class Application : Application()
my edit fragment:
#AndroidEntryPoint
class EditFragment : Fragment() {
private var _binding: FragmentEditBinding? = null
private val binding get() = _binding!!
private val viewModel: SharedViewModel by activityViewModels()
//...
}
my other fragment:
#AndroidEntryPoint
class MainFragment : Fragment() {
private var _binding: FragmentMainBinding? = null
private val binding get() = _binding!!
private val viewModel: SharedViewModel by activityViewModels()
//...
}
by the way also my MainActivity is annotated with #AndroidEntryPoint
my famous viewModel :
#HiltViewModel
class SharedViewModel #Inject constructor(private val noteUseCase: NoteUseCase) :
ViewModel() {...}
this is project level build.gradle:
buildscript {
repositories {
google()
mavenCentral()
}
dependencies {
def nav_version = "2.5.2"
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version"
classpath 'com.google.dagger:hilt-android-gradle-plugin:2.44'
}
}
plugins {
id 'com.android.application' version '7.3.0' apply false
id 'com.android.library' version '7.3.0' apply false
id 'org.jetbrains.kotlin.android' version '1.7.10' apply false
}
and module level build.gradle:
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
id 'kotlin-android'
id 'kotlin-kapt'
id "androidx.navigation.safeargs"
id 'com.google.dagger.hilt.android'
}
android {
compileSdk 32
defaultConfig {
applicationId "com.example.stayin"
minSdk 21
targetSdk 32
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
buildFeatures {
dataBinding true
viewBinding true
}
namespace 'com.example.stayin'
}
dependencies {
implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.appcompat:appcompat:1.4.2'
implementation 'com.google.android.material:material:1.6.1'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
def lifecycle_version = "2.4.1"
implementation "androidx.lifecycle:lifecycle-extensions:2.2.0"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycle_version"
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
// coroutines for getting off the UI thread
def coroutines = "1.6.0"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines"
//shared preferences dependency
implementation 'androidx.preference:preference-ktx:1.2.0'
// Room dependency
def room_version = "2.4.3"
implementation "androidx.room:room-runtime:$room_version"
kapt "androidx.room:room-compiler:$room_version"
// Kotlin Extensions and Coroutines support for Room
implementation "androidx.room:room-ktx:$room_version"
//navigation component dependency
implementation "androidx.navigation:navigation-fragment-ktx:2.5.2"
implementation "androidx.navigation:navigation-ui-ktx:2.5.2"
//Dagger - Hilt
implementation 'com.google.dagger:hilt-android:2.44'
kapt 'com.google.dagger:hilt-compiler:2.44'
// For instrumentation tests
androidTestImplementation 'com.google.dagger:hilt-android-testing:2.44'
kaptAndroidTest 'com.google.dagger:hilt-compiler:2.44'
// For local unit tests
testImplementation 'com.google.dagger:hilt-android-testing:2.44'
kaptTest 'com.google.dagger:hilt-compiler:2.44'
implementation "androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha03"
}
if can any one help me to find what is wrong and explained why, i will be so thankful towards him. i rally need to pass this so i can level up in my career.
Remove below deprecated dependency:
implementation "androidx.hilt:hilt-lifecycle-viewmodel:1.0.0-alpha03"
(It was deprecated since dagger-2.34 version)
proof:
https://github.com/google/dagger/releases/tag/dagger-2.34
Also Try to upgrade your lifecycle version as below:
def lifecycle_version = "2.5.1"
add below lines after dependency section in build.gradle(app):
kapt { correctErrorTypes true }
follow official documentation:
https://developer.android.com/training/dependency-injection/hilt-android
https://dagger.dev/hilt/view-model.html
I am new to android testing and I donĀ“t know how to solve this problem.
Trying to run the following test file from my project:
#RunWith(AndroidJUnit4::class)
class AppDatabaseTest {
private lateinit var userDAO: UserDAO // custom DAO interface
private lateinit var db: AppDatabase
#Before
fun createDb() {
db = Room.inMemoryDatabaseBuilder(
ApplicationProvider.getApplicationContext(),
AppDatabase::class.java
).allowMainThreadQueries().build()
userDAO = db.userDAO()
}
#After
#Throws(IOException::class)
fun closeDb() {
db.close()
}
#Test
#Throws(Exception::class)
fun insertAndReadUser() {
val user = User(
1,
"123",
"Test Name",
"link.to/name/image#from_the.web"
)
userDAO.insert(user)
val inserted = userDAO.getByUID(1)
assertThat(inserted).isNotNull()
}
}
Throws the following error on the run log:
java.lang.RuntimeException: Delegate runner androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner for AndroidJUnit4 could not be found.
Caused by: java.lang.ClassNotFoundException: androidx.test.internal.runner.junit4.AndroidJUnit4ClassRunner
Here are the project dependencies:
dependencies {
def room_version = "2.2.6"
implementation "androidx.room:room-runtime:$room_version"
kapt "androidx.room:room-compiler:$room_version"
implementation "androidx.room:room-ktx:$room_version"
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.3.2'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'com.google.android.material:material:1.2.1'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.2'
implementation 'androidx.navigation:navigation-ui-ktx:2.3.2'
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.2.0'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.2.0'
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.2'
implementation 'androidx.navigation:navigation-ui-ktx:2.3.2'
testImplementation 'com.google.truth:truth:1.1'
testImplementation 'junit:junit:4.13.1'
testImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}
What could be causing this error and how to fix it?
Android Studio 4.1.1
I managed to find the issue.
In fact, I had put the testing file on the wrong folder, the [test] folder instead of [androidTest] folder. Moving the file to the correct folder managed to solve the issue.
ApiModule.kt
#Module
class ApiModule {
private val BASE_URL = "https://raw.githubusercontent.com"
#Provides
fun providesCountriesApi() : CountriesApi{
return Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build()
.create(CountriesApi::class.java)
}
#Provides
fun provideCountriesService(): CountriesService {
return CountriesService()
}
}
ApiComponent.kt
#Component(modules = [ApiModule::class])
interface ApiComponent {
fun inject(service: CountriesService)
fun inject(viewModel: ListViewModel)
}
CountriesService.kt
class CountriesService {
#Inject
lateinit var api: CountriesApi
init {
DaggerApiComponent.create().inject(this)
}
fun getCountries(): Single<List<Country>> {
return api.getCountries()
}
}
CountriesApi.kt
interface CountriesApi {
#GET("DevTides/countries/master/countriesV2.json")
fun getCountries(): Single<List<Country>>
}
gradle
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android {
compileSdkVersion 29
buildToolsVersion "29.0.0"
defaultConfig {
applicationId "com.demo.kotlinandroidmaster"
minSdkVersion 15
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'androidx.core:core-ktx:1.0.2'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'androidx.appcompat:appcompat:1.0.2'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'androidx.constraintlayout:constraintlayout:1.1.2'
implementation 'com.squareup.retrofit2:retrofit:2.4.0'
implementation 'com.squareup.retrofit2:converter-gson:2.4.0'
implementation "androidx.recyclerview:recyclerview:1.0.0"
implementation "androidx.cardview:cardview:1.0.0"
implementation 'com.google.android.material:material:1.0.0-rc01'
implementation 'com.github.bumptech.glide:glide:3.7.0'
implementation 'info.androidramp:loading-gear:1.0.4'
implementation 'androidx.appcompat:appcompat:1.0.0'
implementation 'com.google.dagger:dagger:2.21'
implementation 'io.reactivex.rxjava2:rxandroid:2.1.0'
implementation 'io.reactivex.rxjava2:rxjava:2.1.0'
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.4.0'
implementation "androidx.room:room-runtime:2.0.0-rc01"
implementation "androidx.room:room-rxjava2:2.0.0-rc01"
implementation "androidx.lifecycle:lifecycle-extensions:2.0.0-rc01"
annotationProcessor "androidx.lifecycle:lifecycle-compiler:2.0.0-rc01"
annotationProcessor "androidx.room:room-compiler:2.0.0-rc01"
annotationProcessor 'com.google.dagger:dagger-compiler:2.21'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}
Dagger component is not getting generated
try doing a build of your project, before running the app.
Dagger components are generated when a Build is done and removed when a Clean is done.
Edit
#Singleton
#Component(
modules = [AppModule::class, AndroidInjectionModule::class,
InjectionBinder::class,
AndroidSupportInjectionModule::class, InjectionBinder::class]
)
interface AppComponent {
#Component.Builder
interface Builder {
#BindsInstance
fun application(app: YourApplication): Builder
fun build(): AppComponent
}
fun inject(app: YourApplication)
}
here's a working example of an AppComponent
You are missing the annotation processor and the dagger compiler dependency.
In your build.gradle add after the other plugins:
apply plugin: 'kotlin-kapt'
and then, as dependency:
kapt 'com.google.dagger:dagger-compiler:2.21'
Side note: you are not using the latest version of Dagger.
I am trying to use Room in my project. Gradle syncing files well, but I get RunitomeException when trying to get database instance.
"Caused by: java.lang.RuntimeException: cannot find implementation for com.fillooow.android.testtochka.BusinessLogic.database.GithubUserSearchDataBase. GithubUserSearchDataBase_Impl does not exist"
I searched this issue and find that solution is to add this lines into build.gradle file:
implementation "android.arch.persistence.room:runtime:1.1.1"
implementation "android.arch.persistence.room:rxjava2:1.1.1"
kapt "android.arch.persistence.room:compiler:1.1.1"
and also aply this plugin
apply plugin: 'kotlin-kapt'
But this is my build.gradle file, and I still have this issue:
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
android {
compileSdkVersion 28
defaultConfig {
applicationId "com.fillooow.android.testtochka"
minSdkVersion 21
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
implementation 'com.android.support:support-v4:28.0.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
implementation 'com.squareup.retrofit2:converter-gson:2.5.0'
implementation 'com.squareup.retrofit2:retrofit:2.5.0'
implementation 'io.reactivex.rxjava2:rxandroid:2.1.0'
implementation 'com.squareup.retrofit2:adapter-rxjava2:2.5.0'
implementation 'com.android.support:recyclerview-v7:28.0.0'
implementation 'com.android.support:design:28.0.0'
implementation 'com.jakewharton.rxbinding2:rxbinding-kotlin:2.0.0'
implementation 'com.google.android.gms:play-services-auth:16.0.1'
implementation 'com.facebook.android:facebook-android-sdk:[4,5)'
implementation 'com.vk:androidsdk:1.6.9'
implementation 'com.android.support:cardview-v7:28.0.0'
implementation 'com.squareup.picasso:picasso:2.71828'
implementation "android.arch.persistence.room:runtime:1.1.1"
implementation "android.arch.persistence.room:rxjava2:1.1.1"
kapt "android.arch.persistence.room:compiler:1.1.1"
}
And this is DataBase class
import android.arch.persistence.room.Room
import android.arch.persistence.room.RoomDatabase
import android.content.Context
abstract class GithubUserSearchDataBase : RoomDatabase(){
abstract fun githubUserSearchDataDao(): GithubUserSearchDataDao
companion object {
private var INSTANCE: GithubUserSearchDataBase? = null
fun getInstance(context: Context): GithubUserSearchDataBase?{
if (INSTANCE == null){
synchronized(GithubUserSearchDataBase::class){
INSTANCE = Room.databaseBuilder(context.applicationContext,
GithubUserSearchDataBase::class.java,
"github.db")
.build()
}
}
return INSTANCE
}
fun destroyInstance(){
INSTANCE = null
}
}
}
Project were cleared and rebuild a lot of times.
So, maybe I missed something?
Your gradle file looks fine. Just be sure to Sync it after you have added the proper imports.
What you are missing is the #Database annotation on top of your Database class.
#Database(entities = [Entity1::class, Entity2::class, Entity3::class, Entity4::class], version = 1)
abstract class GithubUserSearchDataBase : RoomDatabase(){
abstract fun githubUserSearchDataDao(): GithubUserSearchDataDao
companion object {
private var INSTANCE: GithubUserSearchDataBase? = null
fun getInstance(context: Context): GithubUserSearchDataBase?{
if (INSTANCE == null){
synchronized(GithubUserSearchDataBase::class){
INSTANCE = Room.databaseBuilder(context.applicationContext,
GithubUserSearchDataBase::class.java,
"github.db")
.build()
}
}
return INSTANCE
}
fun destroyInstance(){
INSTANCE = null
}
}
}
In the entities attribute of the #Database annotation you must put an array with all the classes of your model annotated with the #Entity annotation. I put there fake names, you should put the proper ones.