我想我已经发现我需要在 onSaveInstanceState 的某个地方保存一些东西,但我不知道什么以及如何保存。我猜这与数据库有关,因为错误中提到了 room。
我的应用程序正在使用 NavGraph 来映射屏幕,所以我不知道是否应该一一保存我的所有
Fragment
,或者有一些我可以使用的 NavGraph
相关解决方案。对于 ViewModel
,我已经使用了类似的东西:
private val actDevInfVM: ActuatorDeviceInfoViewModel by viewModels {
ActuatorDeviceInfoViewModel.ActuatorDeviceInfoViewModelFactory((ctx.application as MyApp).actuatorDeviceInfoRepo)
}
将
ViewModel
加载到 Fragment
或 Activity
上。我尝试将其替换为:
private val actDevInfVM: ActuatorDeviceInfoViewModel by navGraphViewModels(R.id.main_nav_graph) {
defaultViewModelProviderFactory
}
但是我遇到了一组不同的错误,但对于另一个
ViewModel
来说似乎是这样。我收到如下错误:
无法创建类的实例 com.my.package.name.viewmodel.SensorViewModel
我的
ViewModel
看起来像:
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"
}
}
然后在
MyApp
,我有这个代码:
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()
被调用于 MainActivity
s onDestroy()
那么这就是
AppDatabase
的样子:
@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
}
}
}
}
不确定这是否回答了您的问题,但为了避免屏幕旋转时重新加载活动,请在清单中添加以下内容:
<activity>
(your activity)
android:configChanges="keyboardHidden|orientation|screenSize">
</activity>
当屏幕方向改变时执行某些操作:
@Override public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
Toast.makeText(this, "config changed", Toast.LENGTH_SHORT).show();
// Checks the orientation of the screen
if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
Toast.makeText(this, "landscape", Toast.LENGTH_SHORT).show();
} else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT){
Toast.makeText(this, "portrait", Toast.LENGTH_SHORT).show();
} }