Kotlin管道Channel融合flow流,协程实现Android废弃的AsyncTaskLoader(A)

Kotlin管道Channel融合flow流,协程实现Android废弃的AsyncTaskLoader(A)

Android官方已经把AsyncTaskLoader标记为废弃,并建议开发者使用Kotlin最新的协程+flow实现AsyncTaskLoader的功能。现在用Kotlin原生的管道Channel与flow,结合协程,实现一种类似于AsyncTaskLoader的并发异步加载框架。数据更新到view或ui层,选择使用Kotlin的"可观察数据委托"架构Delegates.observable。

Kotlin 复制代码
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.async
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.onCompletion
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.onEmpty
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.newFixedThreadPoolContext
import kotlinx.coroutines.runBlocking

open class Loader<D> {

    companion object {
        const val TAG = "Loader"
    }

    val mResultChannel = Channel<D?>()

    val mTaskChannel = Channel<Msg?>()
    val mThreadPool = newFixedThreadPoolContext(nThreads = 4, name = "my-thread")

    private var mTask: Job

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

        mTask = CoroutineScope(mThreadPool).async {
            mTaskChannel.receiveAsFlow()
                .onStart { println("onStart") }
                .onCompletion { println("onCompletion") }
                .onEmpty { println("onEmpty") }
                .onEach {}.flowOn(mThreadPool)
                .collect {
                    async(mThreadPool) {
                        val result = loadInBackground()
                        mResultChannel.send(result)
                    }
                }

            mTaskChannel.invokeOnClose {
                println("$TAG invokeOnClose")
            }
        }
    }

    open fun loadInBackground(): D? {
        return TODO("")
    }

    fun startLoad() {
        runBlocking {
            async(mThreadPool) {
                mTaskChannel.send(Msg())
            }
        }
    }

    fun close() {
        mTaskChannel.close()
        mResultChannel.close()
        mTask.cancel()
    }

    class Msg {
        val timestamp = System.currentTimeMillis()
    }
}
Kotlin 复制代码
import kotlin.properties.Delegates
import kotlin.reflect.KProperty

class LiveData<D> {
    private val TAG: String = "LiveData"

    private var mChangeCallback: ChangeCallback<D>? = null

    var value: D? by Delegates.observable(
        initialValue = null,

        onChange = { prop: KProperty<*>, oldVal: D?, newVal: D? ->
            mChangeCallback?.dataChange(oldVal, newVal)
        }
    )

    fun onChange(callback: ChangeCallback<D>?) {
        mChangeCallback = callback
    }

    interface ChangeCallback<D> {
        fun dataChange(old: D?, new: D?)
    }
}
Kotlin 复制代码
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.async
import kotlinx.coroutines.flow.receiveAsFlow

open class DVModel<D, L : Loader<D>> {
    private val TAG = "DVModel"

    val mLiveData = LiveData<D>()
    var mLoader: L? = null

    constructor() {
        println("$TAG constructor")
        mLoader = getLoader() as L

        CoroutineScope(mLoader?.mThreadPool!!).async {
            mLoader?.mResultChannel?.receiveAsFlow()?.collect {
                mLiveData.value = it as D
            }
        }
    }

    open fun getLoader(): Loader<D>? {
        TODO()
    }
}
Kotlin 复制代码
import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking

class MyLoader : Loader<String> {
    private val TAG = "MyLoader"

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

    //假设这里是耗时加载任务
    override fun loadInBackground(): String {
        val result = "Hello,world! ${System.currentTimeMillis()}"

        runBlocking {
            println("$TAG loadInBackground ... ${Thread.currentThread().name}")
            delay(2000)
            println("$TAG loadInBackground done ${Thread.currentThread().name}")
        }

        return result
    }
}
Kotlin 复制代码
class MyDVM : DVModel<String, Loader<String>> {
    private val TAG = "MyDVM"

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

    override fun getLoader(): Loader<String>? {
        return MyLoader()
    }
}

测试的主类:

Kotlin 复制代码
import LiveData.ChangeCallback
import kotlinx.coroutines.async
import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking


fun main() {
    val TAG = "main"

    runBlocking {
        val dvm = MyDVM()

        dvm.mLiveData.onChange(object : ChangeCallback<String> {
            override fun dataChange(old: String?, new: String?) {
                println("收到 $old $new")
            }
        })

        async(dvm.mLoader?.mThreadPool!!) {
            repeat(5) {
                println("--")
                println("$TAG startLoad $it ${Thread.currentThread().name}")

                dvm.mLoader?.startLoad()
                delay(3000)
            }
        }
    }
}

运行输出:

DVModel constructor

Loader constructor

MyLoader constructor

MyDVM constructor

--

main startLoad 0 my-thread-3

onStart

MyLoader loadInBackground ... my-thread-4

MyLoader loadInBackground done my-thread-4

收到 null Hello,world! 1766471011855

--

main startLoad 1 my-thread-4

MyLoader loadInBackground ... my-thread-3

MyLoader loadInBackground done my-thread-3

收到 Hello,world! 1766471011855 Hello,world! 1766471014865

--

main startLoad 2 my-thread-4

MyLoader loadInBackground ... my-thread-4

MyLoader loadInBackground done my-thread-4

收到 Hello,world! 1766471014865 Hello,world! 1766471017872

--

main startLoad 3 my-thread-1

MyLoader loadInBackground ... my-thread-4

MyLoader loadInBackground done my-thread-4

收到 Hello,world! 1766471017872 Hello,world! 1766471020887

--

main startLoad 4 my-thread-4

MyLoader loadInBackground ... my-thread-3

MyLoader loadInBackground done my-thread-3

收到 Hello,world! 1766471020887 Hello,world! 1766471023901

Process finished with exit code 0

相关:

https://blog.csdn.net/zhangphil/article/details/132088085

https://blog.csdn.net/zhangphil/article/details/132089024

https://blog.csdn.net/zhangphil/article/details/143364291

https://blog.csdn.net/zhangphil/article/details/143350336

https://blog.csdn.net/zhangphil/article/details/143291905

相关推荐
jinanwuhuaguo12 小时前
(第二十七篇)OpenClaw四月的演化风暴:OpenClaw 2026年4月全版本更新的文明级解读
大数据·人工智能·架构·kotlin·openclaw
我命由我1234512 小时前
Kotlin 开发 - lateinit 关键字
android·java·开发语言·kotlin·android studio·android-studio·android runtime
小书房17 小时前
Kotlin的by
android·开发语言·kotlin·委托·by
jinanwuhuaguo18 小时前
(第二十八篇)OpenClaw成本与感知的奇点——从“Token封建制”到“全民养虾”的本体论地基
android·人工智能·kotlin·拓扑学·openclaw
jinanwuhuaguo2 天前
OpenClaw联邦之心——从孤岛记忆到硅基集体潜意识的拓扑学革命(第二十三篇)
android·人工智能·kotlin·拓扑学·openclaw
pengyu2 天前
【Kotlin 协程修仙录 · 筑基境 · 后阶】 | 调度器的艺术:Dispatchers 四大护法与 withContext 性能密码
android·kotlin
千码君20162 天前
flutter: 分享一下基于trae cn 构建的过程
java·vscode·flutter·kotlin·trae
小书房3 天前
Kotlin的内联函数
java·开发语言·kotlin·inline·内联函数
zhangphil3 天前
Android Page3与Flow分页查媒体数据库展示宫格图片列表,Kotlin
android·kotlin
胡致和4 天前
配置变更后,弹窗为什么飞到了最左边?
kotlin