Android中使用Flow与Coroutine实现编辑后列表更新的方案
解决方案:实现可自动同步更新的SimpleRepository
要解决更新后Flow无法获取新数据的问题,核心是让getAllItems()和getItem()返回的Flow具备可观察性——当服务器数据更新时,能自动触发Flow发射最新内容。以下是两种实用实现方案:
方案一:本地缓存(Room)+ 服务器同步(推荐架构)
基于MVVM单一数据源原则,用Room作为本地缓存,其返回的Flow会自动监听数据库变化,更新本地数据后直接触发UI刷新。
1. 定义Room实体与DAO
@Entity(tableName = "items") data class Item( @PrimaryKey val id: String, val name: String, val title: String, val subtitle: String, val description: String ) @Dao interface ItemDao { @Query("SELECT * FROM items") fun getAllItems(): Flow<List<Item>> @Query("SELECT * FROM items WHERE id = :id") fun getItem(id: String): Flow<Item> @Update suspend fun updateItem(item: Item) @Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun insertOrReplaceAll(items: List<Item>) }
2. 实现SimpleRepoImpl
class SimpleRepoImpl( private val api: YourRetrofitApi, // 你的Retrofit请求接口 private val itemDao: ItemDao ) : SimpleRepo { // 初始化时同步服务器数据到本地(可根据需求调整时机) init { refreshItemsFromServer() } override fun getAllItems(): Flow<List<Item>> { // 直接返回Room的可观察Flow,数据库更新时自动发射新数据 return itemDao.getAllItems() } override fun getItem(id: String): Flow<Item> { return itemDao.getItem(id) } override fun updateItemName(id: String, newName: String): Flow<Boolean> { return flow { // 1. 调用服务器接口完成更新 val success = api.updateItemName(id, newName).execute().isSuccessful if (success) { // 2. 更新本地缓存,触发Flow发射新数据 val currentItem = itemDao.getItem(id).first() itemDao.updateItem(currentItem.copy(name = newName)) } emit(success) }.flowOn(Dispatchers.IO) } // 其他update方法逻辑与updateItemName一致,仅修改对应字段 override fun updateItemTitle(id: String, newTitle: String): Flow<Boolean> { return flow { val success = api.updateItemTitle(id, newTitle).execute().isSuccessful if (success) { val currentItem = itemDao.getItem(id).first() itemDao.updateItem(currentItem.copy(title = newTitle)) } emit(success) }.flowOn(Dispatchers.IO) } override fun updateItemSubtitle(id: String, newSubtitle: String): Flow<Boolean> { // 复制updateItemTitle逻辑,修改subtitle字段 } override fun updateItemDescription(id: String, newDescription: String): Flow<Boolean> { // 复制updateItemTitle逻辑,修改description字段 } // 从服务器全量刷新本地数据(可用于下拉刷新等场景) private fun refreshItemsFromServer() { CoroutineScope(Dispatchers.IO).launch { val serverItems = api.getAllItems().execute().body() ?: emptyList() itemDao.insertOrReplaceAll(serverItems) } } }
方案二:SharedFlow触发刷新(无本地缓存)
若无需本地缓存,可通过SharedFlow发送刷新信号,让Flow在更新后重新拉取服务器数据。
class SimpleRepoImpl( private val api: YourRetrofitApi ) : SimpleRepo { // 定义刷新触发器,初始值触发首次数据拉取 private val refreshTrigger = MutableSharedFlow<Unit>(replay = 1) init { refreshTrigger.tryEmit(Unit) } override fun getAllItems(): Flow<List<Item>> { // 监听刷新信号,每次触发都重新请求服务器 return refreshTrigger.flatMapLatest { flow { val items = api.getAllItems().execute().body() ?: emptyList() emit(items) } }.flowOn(Dispatchers.IO) } override fun getItem(id: String): Flow<Item> { return refreshTrigger.flatMapLatest { flow { val item = api.getItem(id).execute().body() ?: throw IllegalArgumentException("Item不存在") emit(item) } }.flowOn(Dispatchers.IO) } override fun updateItemName(id: String, newName: String): Flow<Boolean> { return flow { val success = api.updateItemName(id, newName).execute().isSuccessful if (success) { // 发送刷新信号,触发Flow重新拉取数据 refreshTrigger.emit(Unit) } emit(success) }.flowOn(Dispatchers.IO) } // 其他update方法逻辑一致,成功后发送刷新信号 override fun updateItemTitle(id: String, newTitle: String): Flow<Boolean> { return flow { val success = api.updateItemTitle(id, newTitle).execute().isSuccessful if (success) { refreshTrigger.emit(Unit) } emit(success) }.flowOn(Dispatchers.IO) } // updateItemSubtitle、updateItemDescription逻辑与上述一致 }
关键注意事项
- 所有网络操作需放在
Dispatchers.IO线程,避免阻塞主线程。 - 方案一中Room操作均为suspend函数,需在协程中调用。
- 方案二中
flatMapLatest会在新信号到来时取消旧请求,避免重复请求浪费资源。
内容的提问来源于stack exchange,提问作者LunaVulpo
相关产品推荐
相关产品推荐

