UI 冻结的最可能原因是您正在从某个速度较慢的来源(例如网络或数据库)检索数据。
如果是这种情况,解决方案是在后台线程上检索数据。这些天来,我建议使用 Kotlin Coroutines 和 Kotlin Flow。
你的DB会返回一个Flow,每当DB中的数据发生变化时,它的内容就会自动更新,你可以在ViewModel中观察数据的更新。
从您的数据库中获取一个 Flow 对象:
@Dao
interface MyDataDao {
@Query("SELECT * FROM mydata")
fun flowAll(): Flow<List<MyDataEntity>>
}
Fragment 会观察 LiveData 并在列表适配器发生变化时更新它:
class MyScreenFragment(): Fragment() {
override fun onCreate() {
viewModel.myDataListItems.observe(viewLifecycleOwner) { listItems ->
myListAdapter.setItems(listItems)
myListAdapter.notifyDataSetChanged()
}
}
}
在 ViewModel 中:
@ExperimentalCoroutinesApi
class MyScreenViewModel(
val myApi: MyApi,
val myDataDao: MyDataDao
) : ViewModel() {
val myDataListItems = MutableLiveData<List<MyDataListItem>>()
init {
// launch a coroutine in the background to retrieve our data
viewModelScope.launch(context = Dispatchers.IO) {
// trigger the loading of data from the network
// which you should then save into the database,
// and that will trigger the Flow to update
myApi.fetchMyDataAndSaveToDB()
collectMyData()
}
}
/** begin collecting the flow of data */
private fun collectMyData() {
flowMyData.collect { myData ->
// update the UI by posting the list items to a LiveData
myDataListItems.postValue(myData.asListItems())
}
}
/** retrieve a flow of the data from database */
private fun flowMyData(): Flow<List<MyData>> {
val roomItems = myDataDao.flowAll()
return roomItems.map { it.map(MyDataEntity::toDomain) }
}
/** convert the data into list items */
private fun MyData.asListItems(): MyDataListItem {
return this.map { MyDataListItem(it) }
}
}
如果您不知道如何在房间数据库中定义对象,我会给您以下提示:
// your data representation in the DB, this is a Room entity
@Entity(tableName = "mydata")
class MyDataEntity(
@PrimaryKey
val id: Int,
val date: String,
// ...
)
// Domain representation of your data
data class MyData(
val id: Int,
val date: SomeDateType,
// ...
)
// Map your DB entity to your Domain representation
fun MyDataEntity.toDomain(): MyData {
return MyData(
id = id,
date = SomeDateFormatter.format(date),
// ...
)
}