Kotlin协程flow缓冲buffer任务流,批次任务中选取优先级最高任务最先运行(八)

Kotlin协程flow缓冲buffer任务流,批次任务中选取优先级最高任务最先运行(八)

https://blog.csdn.net/zhangphil/article/details/158844549 基础上改进,增加Listener接口,监听request的状态。

实现基于Kotlin协程的优先级任务调度系统。通过PriorityBlockingQueue实现任务优先级排序,使用Channel和Flow进行任务流缓冲处理。系统支持任务取消机制,并新增Listener接口监听任务状态变化(开始/取消)。核心类包括Loader(任务执行)、LoadRequest(任务请求)和LoadMgr(调度管理器)。测试案例显示,系统能正确按优先级执行任务,并在任务取消时触发回调。该方案适用于需要并发处理且需动态调整优先级的场景,如后台任务调度等。

Kotlin 复制代码
import java.util.UUID

open class Loader {
    companion object {
        private const val TAG = "fly/Loader"
    }

    private var id: Any? = UUID.randomUUID()

    constructor() {

    }

    open fun setId(id: Any?) {
        this.id = id
    }

    open fun getId(): Any? {
        return id
    }

    open fun doInBackground(): Result<Any>? {
        return null
    }

    open fun deliveryResult(result: Result<Any>?) {

    }

    override fun equals(other: Any?): Boolean {
        return id == (other as Loader).id
    }
}
Kotlin 复制代码
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.async
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.buffer
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.newFixedThreadPoolContext
import java.util.concurrent.PriorityBlockingQueue


class LoadMgr {
    companion object {
        private const val TAG = "fly/LoadMgr"

        val INSTANCE = LoadMgr()
        val THREAD_POOL = newFixedThreadPoolContext(nThreads = 4, name = "线程")
    }

    private val mChannel = Channel<LoadRequest>()

    private val bufferCapacity = 10
    private val initialCapacity = 50

    private val mPriorityBlockingQueue = PriorityBlockingQueue(
        initialCapacity,
        Comparator<LoadRequest> { o1, o2 -> o2.getPriority()!!.ordinal - o1.getPriority()!!.ordinal })

    private constructor() {
        println("$TAG constructor")
    }

    fun startup() {
        //接收任务
        CoroutineScope(THREAD_POOL).async {
            println("$TAG Channel start... ${Thread.currentThread().name}")

            mChannel.receiveAsFlow()
                .onEach { it ->  //生产者
                    //println("$TAG onEach-$it ${Thread.currentThread().name}")
                }.buffer(bufferCapacity)
                .collect { it -> //消费者
                    //collect, 这里相当于通过缓冲后匀速发射过来的触发器(trigger)。
                    //收集到的值在此并不重要,这里,只是把它作为触发信号。
                    //println("$TAG collect-$it ${Thread.currentThread().name}")
                    trigger()
                }
        }
    }

    private fun trigger() {
        val loadRequest = mPriorityBlockingQueue.poll()
        println("$TAG 当前最大优先级任务:${loadRequest} ${Thread.currentThread().name}")

        loadRequest?.let {
            CoroutineScope(THREAD_POOL).async {
                val result = if (it.isCancelled()) {
                    it.getListener()?.onCancel(it)
                    println("$TAG id=${loadRequest.getId()} isCancelled=${it.isCancelled()}")
                    return@async
                } else {
                    it.getListener()?.onStart(it)
                    it.getLoader()?.doInBackground()
                }

                println("$TAG id=${loadRequest} doInBackground完成 isCancelled=${loadRequest.isCancelled()} ${Thread.currentThread().name}")
                if (it.isCancelled()) {
                    it.getListener()?.onCancel(it)
                } else {
                    println("$TAG deliveryResult loadRequest=${loadRequest} ${Thread.currentThread().name}")
                    it.getLoader()?.deliveryResult(result)
                }
            }
        }
    }

    fun enqueue(taskInfo: LoadRequest) {
        CoroutineScope(THREAD_POOL).async {
            mPriorityBlockingQueue.add(taskInfo)
            mChannel.send(taskInfo)
        }
    }

    fun destroy() {
        mChannel.cancel()
        mChannel.close()
    }
}
Kotlin 复制代码
import java.util.UUID

open class LoadRequest {
    private var id: Any? = null
    private var tag: Any? = null
    private var loader: Loader? = null
    private var priority: Priority? = null
    private var listener: Listener? = null
    private var isCancelled = false

    private constructor() {

    }

    fun getId(): Any? {
        return id
    }

    fun getTag(): Any? {
        return tag
    }

    fun getLoader(): Loader? {
        return loader
    }

    fun getListener(): Listener? {
        return listener
    }

    fun getPriority(): Priority? {
        return priority
    }

    open fun cancel() {
        isCancelled = true
    }

    open fun isCancelled(): Boolean {
        return isCancelled
    }

    override fun equals(other: Any?): Boolean {
        return id == (other as LoadRequest).id
    }

    override fun toString(): String {
        return "LoadRequest(id=$id, tag=$tag, priority=$priority, isCancelled=$isCancelled)"
    }

    interface Listener {
        fun onStart(request: LoadRequest?) {}
        fun onCancel(request: LoadRequest?) {}
        fun onError() {}
        fun onSuccess() {}
    }

    class Builder {
        private var id: Any? = UUID.randomUUID()
        private var tag: Any? = null
        private var loader: Loader? = null
        private var priority: Priority? = Priority.NORMAL
        private var listener: Listener? = null

        constructor() {

        }

        fun id(id: Any): Builder {
            this.id = id
            return this
        }

        fun tag(tag: Any): Builder {
            this.tag = tag
            return this
        }

        fun loader(loader: Loader): Builder {
            this.loader = loader
            return this
        }

        fun listener(listener: Listener): Builder {
            this.listener = listener
            return this
        }

        fun priority(priority: Priority): Builder {
            this.priority = priority
            return this
        }

        fun build(): LoadRequest {
            val request = LoadRequest()
            request.id = id
            request.tag = tag
            request.loader = loader
            request.priority = priority
            request.listener = listener
            return request
        }
    }
}
Kotlin 复制代码
import kotlin.random.Random

fun main() {
    val TAG = "fly/main"

    LoadMgr.INSTANCE.startup()

    val requests = ArrayList<LoadRequest>()

    //源源不断的密集发送加载任务。
    repeat(30) { idx ->
        val loader = MyLoader()
        loader.setId(idx)

        val p = Priority.entries.toTypedArray().random()
        val request = LoadRequest.Builder()
            .id(idx)
            .priority(p)
            .listener(object : LoadRequest.Listener {
                override fun onCancel(request: LoadRequest?) {
                    println("$TAG id=${request?.getId()} Listener cancelled")
                }
            })
            .loader(loader)
            .build()

        requests.add(request)
        LoadMgr.INSTANCE.enqueue(request)
    }
    println("$TAG enqueue 全部发送完成")

    //模拟在众多并发任务启动后,由于某些情况,加载任务突然需要取消。
    requests.shuffle().run {
        requests.forEachIndexed { _, request ->
            Thread.sleep(100)

            if (Random.nextBoolean()) {
                request.cancel()
                println("$TAG id=${request.getId()} cancel()")
            }
        }
    }

    Thread.sleep(999999)
    println("$TAG sleep结束")

    LoadMgr.INSTANCE.destroy()
}
Kotlin 复制代码
class MyLoader : Loader() {

    companion object {
        const val TAG = "fly/MyLoader"
    }

    override fun doInBackground(): Result<Any>? {
        println("$TAG id=${getId()} doInBackground开始 ${Thread.currentThread().name}")
        //假设耗时操作
        Thread.sleep(2000)
        println("$TAG id=${getId()} doInBackground完成 ${Thread.currentThread().name}")

        val result = Result.success("hello, result id=${getId()}")
        return result
    }

    // 不要寄希望于doInBackground里面直接返回结果,因为在并发任务环境下,如果doInBackground已开始运行,框架就无法取消它的运行,即便之后通过总控开关取消这条请求。
    // 因此,在deliveryResult等待任务正常加载(没有被取消)后返回的结果,才能保证并发任务逻辑上的正常。
    override fun deliveryResult(result: Result<Any>?) {
        println("$TAG id=${getId()} deliveryResult ${result?.getOrNull()} ${Thread.currentThread().name}")
    }
}
Kotlin 复制代码
enum class Priority {
    LOW,
    NORMAL,
    HIGH,
    IMMEDIATELY
}
Kotlin 复制代码
open class SimpleLoader : Loader() {
    open fun worker() {

    }

    final override fun doInBackground(): Result<Any>? {
        worker()
        return null
    }
}

运行输出:

fly/LoadMgr constructor

fly/LoadMgr Channel start... 线程-1

fly/main enqueue 全部发送完成

fly/LoadMgr 当前最大优先级任务:LoadRequest(id=2, tag=null, priority=IMMEDIATELY, isCancelled=false) 线程-3

fly/LoadMgr 当前最大优先级任务:LoadRequest(id=15, tag=null, priority=IMMEDIATELY, isCancelled=false) 线程-3

fly/LoadMgr 当前最大优先级任务:LoadRequest(id=20, tag=null, priority=IMMEDIATELY, isCancelled=false) 线程-3

fly/MyLoader id=2 doInBackground开始 线程-2

fly/MyLoader id=15 doInBackground开始 线程-1

fly/LoadMgr 当前最大优先级任务:LoadRequest(id=26, tag=null, priority=IMMEDIATELY, isCancelled=false) 线程-3

fly/MyLoader id=20 doInBackground开始 线程-4

fly/LoadMgr 当前最大优先级任务:LoadRequest(id=28, tag=null, priority=IMMEDIATELY, isCancelled=false) 线程-3

fly/LoadMgr 当前最大优先级任务:LoadRequest(id=29, tag=null, priority=HIGH, isCancelled=false) 线程-3

fly/LoadMgr 当前最大优先级任务:LoadRequest(id=9, tag=null, priority=HIGH, isCancelled=false) 线程-3

fly/LoadMgr 当前最大优先级任务:LoadRequest(id=8, tag=null, priority=HIGH, isCancelled=false) 线程-3

fly/LoadMgr 当前最大优先级任务:LoadRequest(id=23, tag=null, priority=HIGH, isCancelled=false) 线程-3

fly/LoadMgr 当前最大优先级任务:LoadRequest(id=4, tag=null, priority=HIGH, isCancelled=false) 线程-3

fly/LoadMgr 当前最大优先级任务:LoadRequest(id=27, tag=null, priority=HIGH, isCancelled=false) 线程-3

fly/LoadMgr 当前最大优先级任务:LoadRequest(id=14, tag=null, priority=HIGH, isCancelled=false) 线程-3

fly/LoadMgr 当前最大优先级任务:LoadRequest(id=7, tag=null, priority=NORMAL, isCancelled=false) 线程-3

fly/LoadMgr 当前最大优先级任务:LoadRequest(id=18, tag=null, priority=NORMAL, isCancelled=false) 线程-3

fly/MyLoader id=26 doInBackground开始 线程-3

fly/main id=0 cancel()

fly/main id=23 cancel()

fly/main id=2 cancel()

fly/main id=22 cancel()

fly/main id=5 cancel()

fly/main id=12 cancel()

fly/main id=17 cancel()

fly/main id=24 cancel()

fly/MyLoader id=2 doInBackground完成 线程-2

fly/MyLoader id=26 doInBackground完成 线程-3

fly/MyLoader id=15 doInBackground完成 线程-1

fly/MyLoader id=20 doInBackground完成 线程-4

fly/LoadMgr id=LoadRequest(id=26, tag=null, priority=IMMEDIATELY, isCancelled=false) doInBackground完成 isCancelled=false 线程-3

fly/LoadMgr id=LoadRequest(id=20, tag=null, priority=IMMEDIATELY, isCancelled=false) doInBackground完成 isCancelled=false 线程-4

fly/LoadMgr id=LoadRequest(id=15, tag=null, priority=IMMEDIATELY, isCancelled=false) doInBackground完成 isCancelled=false 线程-1

fly/LoadMgr id=LoadRequest(id=2, tag=null, priority=IMMEDIATELY, isCancelled=true) doInBackground完成 isCancelled=true 线程-2

fly/LoadMgr deliveryResult loadRequest=LoadRequest(id=26, tag=null, priority=IMMEDIATELY, isCancelled=false) 线程-3

fly/LoadMgr deliveryResult loadRequest=LoadRequest(id=20, tag=null, priority=IMMEDIATELY, isCancelled=false) 线程-4

fly/LoadMgr deliveryResult loadRequest=LoadRequest(id=15, tag=null, priority=IMMEDIATELY, isCancelled=false) 线程-1

fly/main id=2 Listener cancelled

fly/MyLoader id=28 doInBackground开始 线程-2

fly/MyLoader id=20 deliveryResult hello, result id=20 线程-4

fly/MyLoader id=26 deliveryResult hello, result id=26 线程-3

fly/MyLoader id=15 deliveryResult hello, result id=15 线程-1

fly/MyLoader id=9 doInBackground开始 线程-3

fly/MyLoader id=29 doInBackground开始 线程-4

fly/MyLoader id=8 doInBackground开始 线程-1

fly/main id=21 cancel()

fly/main id=15 cancel()

fly/main id=13 cancel()

fly/main id=4 cancel()

fly/main id=20 cancel()

fly/main id=9 cancel()

fly/main id=26 cancel()

fly/main id=14 cancel()

fly/MyLoader id=9 doInBackground完成 线程-3

fly/LoadMgr id=LoadRequest(id=9, tag=null, priority=HIGH, isCancelled=true) doInBackground完成 isCancelled=true 线程-3

fly/main id=9 Listener cancelled

fly/MyLoader id=29 doInBackground完成 线程-4

fly/MyLoader id=8 doInBackground完成 线程-1

fly/MyLoader id=28 doInBackground完成 线程-2

fly/LoadMgr id=LoadRequest(id=8, tag=null, priority=HIGH, isCancelled=false) doInBackground完成 isCancelled=false 线程-1

fly/LoadMgr id=LoadRequest(id=29, tag=null, priority=HIGH, isCancelled=false) doInBackground完成 isCancelled=false 线程-4

fly/main id=23 Listener cancelled

fly/LoadMgr deliveryResult loadRequest=LoadRequest(id=8, tag=null, priority=HIGH, isCancelled=false) 线程-1

fly/LoadMgr id=LoadRequest(id=28, tag=null, priority=IMMEDIATELY, isCancelled=false) doInBackground完成 isCancelled=false 线程-2

fly/LoadMgr deliveryResult loadRequest=LoadRequest(id=29, tag=null, priority=HIGH, isCancelled=false) 线程-4

fly/MyLoader id=8 deliveryResult hello, result id=8 线程-1

fly/LoadMgr deliveryResult loadRequest=LoadRequest(id=28, tag=null, priority=IMMEDIATELY, isCancelled=false) 线程-2

fly/MyLoader id=29 deliveryResult hello, result id=29 线程-4

fly/main id=4 Listener cancelled

fly/MyLoader id=28 deliveryResult hello, result id=28 线程-2

fly/MyLoader id=27 doInBackground开始 线程-4

fly/main id=14 Listener cancelled

fly/LoadMgr id=14 isCancelled=true

fly/LoadMgr id=23 isCancelled=true

fly/LoadMgr id=4 isCancelled=true

fly/MyLoader id=18 doInBackground开始 线程-3

fly/MyLoader id=7 doInBackground开始 线程-2

fly/LoadMgr 当前最大优先级任务:LoadRequest(id=1, tag=null, priority=NORMAL, isCancelled=false) 线程-1

fly/LoadMgr 当前最大优先级任务:LoadRequest(id=25, tag=null, priority=NORMAL, isCancelled=false) 线程-1

fly/LoadMgr 当前最大优先级任务:LoadRequest(id=24, tag=null, priority=NORMAL, isCancelled=true) 线程-1

fly/LoadMgr 当前最大优先级任务:LoadRequest(id=0, tag=null, priority=NORMAL, isCancelled=true) 线程-1

fly/LoadMgr 当前最大优先级任务:LoadRequest(id=10, tag=null, priority=LOW, isCancelled=false) 线程-1

fly/LoadMgr 当前最大优先级任务:LoadRequest(id=22, tag=null, priority=LOW, isCancelled=true) 线程-1

fly/LoadMgr 当前最大优先级任务:LoadRequest(id=5, tag=null, priority=LOW, isCancelled=true) 线程-1

fly/LoadMgr 当前最大优先级任务:LoadRequest(id=12, tag=null, priority=LOW, isCancelled=true) 线程-1

fly/LoadMgr 当前最大优先级任务:LoadRequest(id=11, tag=null, priority=LOW, isCancelled=false) 线程-1

fly/LoadMgr 当前最大优先级任务:LoadRequest(id=3, tag=null, priority=LOW, isCancelled=false) 线程-1

fly/LoadMgr 当前最大优先级任务:LoadRequest(id=21, tag=null, priority=LOW, isCancelled=true) 线程-1

fly/LoadMgr 当前最大优先级任务:LoadRequest(id=16, tag=null, priority=LOW, isCancelled=false) 线程-1

fly/MyLoader id=1 doInBackground开始 线程-1

fly/MyLoader id=27 doInBackground完成 线程-4

fly/LoadMgr id=LoadRequest(id=27, tag=null, priority=HIGH, isCancelled=false) doInBackground完成 isCancelled=false 线程-4

fly/LoadMgr deliveryResult loadRequest=LoadRequest(id=27, tag=null, priority=HIGH, isCancelled=false) 线程-4

fly/MyLoader id=27 deliveryResult hello, result id=27 线程-4

fly/MyLoader id=25 doInBackground开始 线程-4

fly/MyLoader id=18 doInBackground完成 线程-3

fly/MyLoader id=7 doInBackground完成 线程-2

fly/LoadMgr id=LoadRequest(id=18, tag=null, priority=NORMAL, isCancelled=false) doInBackground完成 isCancelled=false 线程-3

fly/LoadMgr deliveryResult loadRequest=LoadRequest(id=18, tag=null, priority=NORMAL, isCancelled=false) 线程-3

fly/LoadMgr id=LoadRequest(id=7, tag=null, priority=NORMAL, isCancelled=false) doInBackground完成 isCancelled=false 线程-2

fly/MyLoader id=18 deliveryResult hello, result id=18 线程-3

fly/LoadMgr deliveryResult loadRequest=LoadRequest(id=7, tag=null, priority=NORMAL, isCancelled=false) 线程-2

fly/MyLoader id=7 deliveryResult hello, result id=7 线程-2

fly/main id=24 Listener cancelled

fly/LoadMgr id=24 isCancelled=true

fly/main id=0 Listener cancelled

fly/MyLoader id=10 doInBackground开始 线程-3

fly/LoadMgr id=0 isCancelled=true

fly/main id=22 Listener cancelled

fly/LoadMgr id=22 isCancelled=true

fly/main id=5 Listener cancelled

fly/LoadMgr id=5 isCancelled=true

fly/main id=12 Listener cancelled

fly/LoadMgr id=12 isCancelled=true

fly/MyLoader id=11 doInBackground开始 线程-2

fly/MyLoader id=1 doInBackground完成 线程-1

fly/LoadMgr id=LoadRequest(id=1, tag=null, priority=NORMAL, isCancelled=false) doInBackground完成 isCancelled=false 线程-1

fly/LoadMgr deliveryResult loadRequest=LoadRequest(id=1, tag=null, priority=NORMAL, isCancelled=false) 线程-1

fly/MyLoader id=1 deliveryResult hello, result id=1 线程-1

fly/MyLoader id=3 doInBackground开始 线程-1

fly/MyLoader id=25 doInBackground完成 线程-4

fly/LoadMgr id=LoadRequest(id=25, tag=null, priority=NORMAL, isCancelled=false) doInBackground完成 isCancelled=false 线程-4

fly/LoadMgr deliveryResult loadRequest=LoadRequest(id=25, tag=null, priority=NORMAL, isCancelled=false) 线程-4

fly/MyLoader id=25 deliveryResult hello, result id=25 线程-4

fly/main id=21 Listener cancelled

fly/LoadMgr id=21 isCancelled=true

fly/MyLoader id=16 doInBackground开始 线程-4

fly/MyLoader id=10 doInBackground完成 线程-3

fly/LoadMgr id=LoadRequest(id=10, tag=null, priority=LOW, isCancelled=false) doInBackground完成 isCancelled=false 线程-3

fly/LoadMgr deliveryResult loadRequest=LoadRequest(id=10, tag=null, priority=LOW, isCancelled=false) 线程-3

fly/MyLoader id=10 deliveryResult hello, result id=10 线程-3

fly/LoadMgr 当前最大优先级任务:LoadRequest(id=13, tag=null, priority=LOW, isCancelled=true) 线程-3

fly/LoadMgr 当前最大优先级任务:LoadRequest(id=19, tag=null, priority=LOW, isCancelled=false) 线程-3

fly/LoadMgr 当前最大优先级任务:LoadRequest(id=6, tag=null, priority=LOW, isCancelled=false) 线程-3

fly/LoadMgr 当前最大优先级任务:LoadRequest(id=17, tag=null, priority=LOW, isCancelled=true) 线程-3

fly/main id=13 Listener cancelled

fly/LoadMgr id=13 isCancelled=true

fly/MyLoader id=19 doInBackground开始 线程-3

fly/MyLoader id=11 doInBackground完成 线程-2

fly/LoadMgr id=LoadRequest(id=11, tag=null, priority=LOW, isCancelled=false) doInBackground完成 isCancelled=false 线程-2

fly/LoadMgr deliveryResult loadRequest=LoadRequest(id=11, tag=null, priority=LOW, isCancelled=false) 线程-2

fly/MyLoader id=11 deliveryResult hello, result id=11 线程-2

fly/MyLoader id=6 doInBackground开始 线程-2

fly/MyLoader id=3 doInBackground完成 线程-1

fly/LoadMgr id=LoadRequest(id=3, tag=null, priority=LOW, isCancelled=false) doInBackground完成 isCancelled=false 线程-1

fly/LoadMgr deliveryResult loadRequest=LoadRequest(id=3, tag=null, priority=LOW, isCancelled=false) 线程-1

fly/MyLoader id=3 deliveryResult hello, result id=3 线程-1

fly/main id=17 Listener cancelled

fly/LoadMgr id=17 isCancelled=true

fly/MyLoader id=16 doInBackground完成 线程-4

fly/LoadMgr id=LoadRequest(id=16, tag=null, priority=LOW, isCancelled=false) doInBackground完成 isCancelled=false 线程-4

fly/LoadMgr deliveryResult loadRequest=LoadRequest(id=16, tag=null, priority=LOW, isCancelled=false) 线程-4

fly/MyLoader id=16 deliveryResult hello, result id=16 线程-4

fly/MyLoader id=6 doInBackground完成 线程-2

fly/LoadMgr id=LoadRequest(id=6, tag=null, priority=LOW, isCancelled=false) doInBackground完成 isCancelled=false 线程-2

fly/LoadMgr deliveryResult loadRequest=LoadRequest(id=6, tag=null, priority=LOW, isCancelled=false) 线程-2

fly/MyLoader id=6 deliveryResult hello, result id=6 线程-2

fly/MyLoader id=19 doInBackground完成 线程-3

fly/LoadMgr id=LoadRequest(id=19, tag=null, priority=LOW, isCancelled=false) doInBackground完成 isCancelled=false 线程-3

fly/LoadMgr deliveryResult loadRequest=LoadRequest(id=19, tag=null, priority=LOW, isCancelled=false) 线程-3

fly/MyLoader id=19 deliveryResult hello, result id=19 线程-3

相关推荐
木尧大兄弟19 小时前
Decoder-Only 模型 自回归vs一次前传 两种 Hidden State 的对比
数据挖掘·回归·kotlin
我命由我1234519 小时前
Android 开发 - UriMatcher(一个 URI 分类器)
android·java·java-ee·kotlin·android studio·android-studio·android runtime
我命由我1234521 小时前
Android 多进程开发 - FileDescriptor、Uri、AIDL 接口定义不能抛出异常
android·java·java-ee·kotlin·android studio·android-studio·android runtime
Kapaseker1 天前
一杯半 Kotlin 美式详解 value class
android·kotlin
黄林晴1 天前
Kotlin 2.3.20 发布!解构声明不怕写反了
android·kotlin
蜡台2 天前
Android Gradle 项目下载编译失败解决---持续更新
android·java·kotlin·gradle
BoomHe2 天前
Kotlin shareIn 和 stateIn 使用场景
android·kotlin·android jetpack
Kapaseker2 天前
一杯 Kotlin 美式学透 enum class
android·kotlin
耶叶3 天前
Android开发:基于SharedPreferences实现的状态缓存
android·kotlin