I think I already figured out that I need to save something on onSaveInstanceState somewhere, but I don't know what and how. I'm guessing it's related to the database because Room was mentioned on the error.
My app is using NavGraph to map the screens, so I don't know if I should save all my Fragments one by one or there's some NavGraph related solution that I could use. And for the ViewModels, I'm already using something that looks like:
private val actDevInfVM: ActuatorDeviceInfoViewModel by viewModels {
ActuatorDeviceInfoViewModel.ActuatorDeviceInfoViewModelFactory((ctx.application as MyApp).actuatorDeviceInfoRepo)
}
to load the ViewModels on a Fragment or an Activity, I tried replacing it with:
private val actDevInfVM: ActuatorDeviceInfoViewModel by navGraphViewModels(R.id.main_nav_graph) {
defaultViewModelProviderFactory
}
But I got a different set of errors, but for another ViewModel it seems. I got an error that looks like:
Cannot create an instance of class com.my.package.name.viewmodel.SensorViewModel
My ViewModels looks like:
class ActuatorDeviceInfoViewModel(private val repo: ActuatorDeviceInfoRepo) : ViewModel(),
IViewModel<ActuatorDeviceInfo> {
private val _items = MutableStateFlow<List<ActuatorDeviceInfo>>(listOf())
override val items: StateFlow<List<ActuatorDeviceInfo>> = _items
fun fetchAll() {
viewModelScope.launch(Dispatchers.Main) {
repo.getAllSub
.flowOn(Dispatchers.IO)
.catch { exception -> exception.localizedMessage?.let { Log.e("TAG", it) } }
.stateIn(viewModelScope, SharingStarted.Eagerly, emptyList())
.collect { _items.value = it }
}
}
fun getAll(): StateFlow<List<ActuatorDeviceInfo>> {
return _items
}
fun getAllLst(): List<ActuatorDeviceInfo> {
return repo.getAllLst()
}
fun getAllWithEdgeDeviceId(edgeDeviceId: String): List<ActuatorDeviceInfo> {
return runBlocking {
repo.getAllWithEdgeDeviceId(edgeDeviceId)
}
}
fun insert(item: ActuatorDeviceInfo) = viewModelScope.launch {
repo.insert(item)
}
override fun insertReturnId(item: ActuatorDeviceInfo): Long {
return runBlocking {
repo.insertReturnId(item)
}
}
override fun update(item: ActuatorDeviceInfo) = viewModelScope.launch {
repo.update(item)
}
override fun insertOrUpdate(item: ActuatorDeviceInfo) = viewModelScope.launch {
repo.insertOrUpdate(item)
}
fun delete(item: ActuatorDeviceInfo) = viewModelScope.launch {
repo.delete(item)
}
class ActuatorDeviceInfoViewModelFactory(private val repo: ActuatorDeviceInfoRepo) :
ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
if (modelClass.isAssignableFrom(ActuatorDeviceInfoViewModel::class.java)) {
@Suppress("UNCHECKED_CAST")
return ActuatorDeviceInfoViewModel(repo) as T
}
throw IllegalArgumentException("Unknown VieModel Class")
}
}
companion object {
const val TAG = "ActuatorDeviceInfoViewModel"
}
}
Then on MyApp, I have this code:
class MyApp : Application() {
private val applicationScope = CoroutineScope(SupervisorJob())
val actuatorDeviceInfoRepo by lazy { ActuatorDeviceInfoRepo(database.actuatorDeviceInfoDao()) }
val sensorRepo by lazy { SensorRepo(database.sensorDao()) }
...
fun dbClose() {
database.close()
}
}
dbClose() is called on MainActivitys onDestroy()
Then this is what AppDatabase looks like:
@Database(
entities = [
ActuatorDeviceInfo::class,
Sensor::class,
... a few more data classes ...
], version = 1, exportSchema = false
)
@TypeConverters(Converters::class)
abstract class AppDatabase : RoomDatabase() {
abstract fun actuatorDeviceInfoDao(): ActuatorDeviceInfoDao
abstract fun sensorDao(): SensorDao
... a few more dao ...
companion object {
@Volatile
private var INSTANCE: AppDatabase? = null
fun getDatabase(context: Context, scope: CoroutineScope): AppDatabase {
val queryInterceptor = LoggingQueryInterceptor()
return INSTANCE ?: synchronized(this) {
val instance = Room.databaseBuilder(
context.applicationContext,
AppDatabase::class.java,
"some_db_name"
)
.addCallback(AppDbCallback(scope))
.setQueryCallback(queryInterceptor, Executors.newSingleThreadExecutor())
.build()
INSTANCE = instance
instance
}
}
}
}
Not sure if this answers your question, but to avoid activity to reload when screen rotates, add following in your Manifest:
To do something when screen orientation changes: