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).