Home > Enterprise >  Error initializing ViewModel (Runtime error) - Kotlin
Error initializing ViewModel (Runtime error) - Kotlin

Time:11-03

There is something wrong with the viewModel, somewhere... Everytime i run the app it crashes almost instantly.

The idea of this app is to use clean architecture, consume an API and show it in a recyclerView. For this i use:

  • Retrofit
  • Room Database
  • Dao
  • Repository
  • MVVM Architecture

However, i am fairly new to programming and MVVM, and i'm self taught, so I apologise if the error is too obvious, i've been at for some time already, and i need to fix this in order to move forward! So Thank you all for your time in advance...

Here's the code: Main Activity:

class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding
    private val model: HarryPotterViewModel by viewModels()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        CoroutineScope(Dispatchers.IO).launch {

            val charactersList =  model.fetchCharactersApiData()

            initRecyclerView(charactersList)

            Toast.makeText(this@MainActivity, "Characters stored?", Toast.LENGTH_SHORT).show()
        }
    }

    fun initRecyclerView(charactersList: List<Characters>) {
        binding.charactersRecyclerView.layoutManager = GridLayoutManager(this, 3)
        binding.charactersRecyclerView.adapter = CharactersAdapter(charactersList)
    }
}

View Model:

class HarryPotterViewModel(application: Application) : AndroidViewModel(application) {

//    val hpDao by lazy { HarryPotterDatabase(getApplication()).hpDao() }
    private val db by lazy { HarryPotterDatabase.buildDatabase(application) }
    private val repository: HarryPotterRepository = HarryPotterRepository(db)

    val characterList: MutableLiveData<List<Characters>> by lazy {
        MutableLiveData<List<Characters>>()
    }
    val getCharactersData = repository.getCharactersData

    suspend fun fetchCharactersApiData(): List<Characters> {

        // Store characters list from the Api call inside a val
        val apiCharacters = repository.fetchData()

        // Store the list inside the database we created for the app
        insertCharactersData(apiCharacters)

        return apiCharacters
    }

    fun insertCharactersData(characters: List<Characters>) {
        viewModelScope.launch(Dispatchers.IO) {
            repository.insertCharacters(characters)
        }
    }
}

Just in case, i'll also put the code for the Database, DAO & repository, in case i left something important too.

@Database(entities = [Characters::class], version = 1, exportSchema = false)
abstract class HarryPotterDatabase: RoomDatabase() {

    abstract fun hpDao(): HarryPotterDao

    companion object {

        @Volatile
        private var INSTANCE: HarryPotterDatabase? = null

        private val lock = Any()

        operator fun invoke(context: Context) = INSTANCE ?: synchronized(lock) {
                INSTANCE ?: buildDatabase(context).also {
                    INSTANCE = it
                }
            }

        fun buildDatabase(context: Context) = Room.databaseBuilder(
            context.applicationContext,
            HarryPotterDatabase::class.java,
            "harry_potter_database"
        ).build()
    }
}

DAO

@Dao
interface HarryPotterDao {

    @Query("SELECT * FROM characters_table ORDER BY name ASC")
    fun getAllCharactersAtoZ(): LiveData<List<Characters>>

    @Insert(onConflict = OnConflictStrategy.IGNORE)
    fun insertCharactersToDB(characters: List<Characters>)
}

Repository

class HarryPotterRepository(private val db: HarryPotterDatabase) {

    val getCharactersData: LiveData<List<Characters>> = db.hpDao().getAllCharactersAtoZ()
    private val apiCall: Networking = Networking()

    suspend fun fetchData(): List<Characters> {
        return apiCall.retrieveCharacters()
    }

    suspend fun insertCharacters(characters: List<Characters>) {
        db.hpDao().insertCharactersToDB(characters)
    }
}

And of course, the error Logcat:

2021-11-03 01:26:54.139 1403-1619/com.example.myapplication E/AndroidRuntime: FATAL EXCEPTION: DefaultDispatcher-worker-2
    Process: com.example.myapplication, PID: 1403
    java.lang.RuntimeException: Cannot create an instance of class com.example.harryPotterApp.viewmodels.HarryPotterViewModel
        at androidx.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.kt:236)
        at androidx.lifecycle.SavedStateViewModelFactory.create(SavedStateViewModelFactory.java:113)
        at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.kt:169)
        at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.kt:139)
        at com.example.harryPotterApp.view.activities.MainActivity$model$2.invoke(MainActivity.kt:22)
        at com.example.harryPotterApp.view.activities.MainActivity$model$2.invoke(MainActivity.kt:21)
        at kotlin.SynchronizedLazyImpl.getValue(LazyJVM.kt:74)
        at com.example.harryPotterApp.view.activities.MainActivity.getModel(MainActivity.kt:21)
        at com.example.harryPotterApp.view.activities.MainActivity.access$getModel(MainActivity.kt:19)
        at com.example.harryPotterApp.view.activities.MainActivity$onCreate$1.invokeSuspend(MainActivity.kt:36)
        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
        at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
        at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:571)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:678)
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:665)
     Caused by: java.lang.reflect.InvocationTargetException
        at java.lang.reflect.Constructor.newInstance0(Native Method)
        at java.lang.reflect.Constructor.newInstance(Constructor.java:343)
        at androidx.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.kt:228)
        at androidx.lifecycle.SavedStateViewModelFactory.create(SavedStateViewModelFactory.java:113) 
        at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.kt:169) 
        at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.kt:139) 
        at com.example.harryPotterApp.view.activities.MainActivity$model$2.invoke(MainActivity.kt:22) 
        at com.example.harryPotterApp.view.activities.MainActivity$model$2.invoke(MainActivity.kt:21) 
        at kotlin.SynchronizedLazyImpl.getValue(LazyJVM.kt:74) 
        at com.example.harryPotterApp.view.activities.MainActivity.getModel(MainActivity.kt:21) 
        at com.example.harryPotterApp.view.activities.MainActivity.access$getModel(MainActivity.kt:19) 
        at com.example.harryPotterApp.view.activities.MainActivity$onCreate$1.invokeSuspend(MainActivity.kt:36) 
        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) 
        at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106) 
        at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:571) 
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750) 
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:678) 
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:665) 
     Caused by: java.lang.RuntimeException: cannot find implementation for com.example.harryPotterApp.repository.roomdb.database.HarryPotterDatabase. HarryPotterDatabase_Impl does not exist
        at androidx.room.Room.getGeneratedImplementation(Room.java:97)
        at androidx.room.RoomDatabase$Builder.build(RoomDatabase.java:1358)
        at com.example.harryPotterApp.repository.roomdb.database.HarryPotterDatabase$Companion.buildDatabase(HarryPotterDatabase.kt:32)
        at com.example.harryPotterApp.viewmodels.HarryPotterViewModel$db$2.invoke(HarryPotterViewModel.kt:17)
        at com.example.harryPotterApp.viewmodels.HarryPotterViewModel$db$2.invoke(HarryPotterViewModel.kt:17)
        at kotlin.SynchronizedLazyImpl.getValue(LazyJVM.kt:74)
        at com.example.harryPotterApp.viewmodels.HarryPotterViewModel.getDb(HarryPotterViewModel.kt:17)
        at com.example.harryPotterApp.viewmodels.HarryPotterViewModel.<init>(HarryPotterViewModel.kt:19)
        at java.lang.reflect.Constructor.newInstance0(Native Method) 
        at java.lang.reflect.Constructor.newInstance(Constructor.java:343) 
        at androidx.lifecycle.ViewModelProvider$AndroidViewModelFactory.create(ViewModelProvider.kt:228) 
        at androidx.lifecycle.SavedStateViewModelFactory.create(SavedStateViewModelFactory.java:113) 
        at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.kt:169) 
        at androidx.lifecycle.ViewModelProvider.get(ViewModelProvider.kt:139) 
        at com.example.harryPotterApp.view.activities.MainActivity$model$2.invoke(MainActivity.kt:22) 
        at com.example.harryPotterApp.view.activities.MainActivity$model$2.invoke(MainActivity.kt:21) 
        at kotlin.SynchronizedLazyImpl.getValue(LazyJVM.kt:74) 
        at com.example.harryPotterApp.view.activities.MainActivity.getModel(MainActivity.kt:21) 
        at com.example.harryPotterApp.view.activities.MainActivity.access$getModel(MainActivity.kt:19) 
        at com.example.harryPotterApp.view.activities.MainActivity$onCreate$1.invokeSuspend(MainActivity.kt:36) 
        at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33) 
        at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106) 
        at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:571) 
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:750) 
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:678) 
        at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:665)

CodePudding user response:

Do you add the following code in your build.gradle file?

 // To use Kotlin annotation processing tool (kapt)
    kapt("androidx.room:room-compiler:$roomVersion")

It seems like the HarryPotterDatabase_Impl is not generated by kapt yet

CodePudding user response:

Instantiating ViewModel using Delegate property (by viewModels()) cannot be done for ViewModel with argument, you need to implement ViewModelProvider.Factory for creating HarryPotterViewModel since HarryPotterViewModel have argument (application).

  • Related