You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 10:00:09