android - 如何将顺序协程 block 排入队列

标签 android kotlin android-room kotlin-coroutines

我想要做什么

我有一个应用程序使用 Room with Coroutines 在数据库中保存搜索查询。也可以添加搜索建议,然后我检索这些数据以将它们显示在列表中。我还可以“固定”其中一些建议。

我的数据结构是这样的:

@Entity(
        tableName = "SEARCH_HISTORY",
        indices = [Index(value = ["text"], unique = true)]
)
data class Suggestion(
    @PrimaryKey(autoGenerate = true)
    @ColumnInfo(name = "suggestion_id")
    val suggestionId: Long = 0L,
    val text: String,
    val type: SuggestionType,
    @ColumnInfo(name = "insert_date")
    val insertDate: Calendar
)

enum class SuggestionType(val value: Int) {
    PINNED(0), HISTORY(1), SUGGESTION(2)
}

我已将“文本”字段设为唯一,以避免针对不同状态/类型的重复建议。例如:一个固定项目和之前查询过的文本的建议。

我的 Coroutine 设置如下所示:

private val parentJob: Job = Job()

private val IO: CoroutineContext
    get() = parentJob + Dispatchers.IO

private val MAIN: CoroutineContext
    get() = parentJob + Dispatchers.Main

private val COMPUTATION: CoroutineContext
    get() = parentJob + Dispatchers.Default

而我的 DAO 基本上是这样的:

@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert(obj: Suggestion): Long

@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insert(objList: List<Suggestion>): List<Long>

我还有以下公共(public)函数可以将数据插入数据库:

fun saveQueryToDb(query: String, insertDate: Calendar) {
    if (query.isBlank()) {
        return
    }
    val suggestion = Suggestion(
            text = query,
            insertDate = insertDate,
            type = SuggestionType.HISTORY
    )
    CoroutineScope(IO).launch {
        suggestionDAO.insert(suggestion)
    }
}

fun addPin(pin: String) {
    if (pin.isBlank()) {
        return
    }
    val suggestion = Suggestion(
            text = pin,
            insertDate = Calendar.getInstance(),
            type = SuggestionType.PINNED
    )
    CoroutineScope(IO).launch {
        suggestionDAO.insert(suggestion)
    }
}

fun addSuggestions(suggestions: List<String>) {
    addItems(suggestions, SuggestionType.SUGGESTION)
}

private fun addItems(items: List<String>, suggestionType: SuggestionType) {
    if (items.isEmpty()) {
        return
    }

    CoroutineScope(COMPUTATION).launch {
        val insertDate = Calendar.getInstance()
        val filteredList = items.filterNot { it.isBlank() }
        val suggestionList = filteredList.map { History(text = it, insertDate = insertDate, suggestionType = suggestionType) }
        withContext(IO) {
            suggestionDAO.insert(suggestionList)
        }
    }
}

还有一些其他的方法,但让我们专注于上面的那些。

编辑: 以上所有方法都是我制作的库的一部分,它们不是 suspend 因为我不想强制特定对用户的编程类型,例如在使用 lib 时强制使用 Rx 或 Coroutines。

问题

假设我尝试使用上述 addSuggestions() 方法添加建议列表,并且我还尝试使用 addPin() 添加固定建议> 方法。固定文本也出现在建议列表中。

val list = getSuggestions() // Getting a list somewhere
addSuggestions(list)
addPin(list.first())

当我尝试这样做时,有时会先添加 pin,然后它会被列表中的建议覆盖,这让我觉得我可能一直在处理某种竞争条件。由于 addSuggestions() 方法要处理的数据较多,而且两个方法会并行运行,我相信 addPin() 方法会先完成。

现在,我的 Coroutines 知识非常有限,我想知道是否有办法将这些方法调用排入队列并确保它们按照我调用它们的完全相同的顺序执行,必须强烈保证避免覆盖数据并在以后获得时髦的结果。我怎样才能实现这种行为?

最佳答案

我会遵循 Go 语言的口号“不要通过共享内存进行通信;通过通信来共享内存”,这意味着不是维护原子变量或作业并尝试在它们之间进行同步,而是将您的操作建模为消息并使用协程actors处理它们。

sealed class Message {
   data AddSuggestions(val suggestions: List<String>) : Message()
   data AddPin(val pin: String) : Message()
}

在你的类里面


private val parentScope = CoroutineScope(Job())

private val actor = parentScope.actor<Message>(Dispatchers.IO) {
        for (msg in channel) {
            when (msg) {
                is Message.AddSuggestions -> TODO("Map to the Suggestion and do suggestionDAO.insert(suggestions)")
                is Message.AddPin -> TODO("Map to the Pin and do suggestionDAO.insert(pin)")
            }
        }
    }

fun addSuggestions(suggestions: List<String>) {
    actor.offer(Message.AddSuggestions(suggestions))
}

fun addPin(pin: String) {
    actor.offer(Message.AddPin(pin))
}

通过使用 Actor,您将能够对消息进行排队,并且它们将按 FIFO 顺序进行处理。

关于android - 如何将顺序协程 block 排入队列,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64229018/

相关文章:

android - android.os.Looper 会耗尽电池电量吗?

android - 已启用 Multidex - 仍然无法合并 dex

java - Bouncy CaSTLe 在尝试解密 AES 消息时抛出 "mac check in OCB failed"

android - 工作管理器 : Cannot access database on the main thread since it may potentially lock the UI for a long period of time

android - 如何从房间数据库中获取日期

android - 如何在我的 Android 11 应用中隐藏状态栏?

java - 获取android中监听特定端口的服务器列表

Android 房间 ORM : Support custom build of SQLite

android - 如何使用adb socket从手机获取结果?

java - 如何使用 jackson ObjectMapper 解析 Mongodb 的 UUID 对象?