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

相关推荐
YB13752 天前
kotlin 关键字expect/actual的作用
kotlin
Android打工仔2 天前
不要再把 Flow 当作一个容器
android·kotlin
天空之城--2 天前
Android行业一周动态:编码趋势与行业资讯汇总
android·性能优化·架构·kotlin·android jetpack
JMchen3 天前
第 2 篇|Kotlin 进阶 —— 集合、循环与条件表达式
android·kotlin
YB13753 天前
Kotlin 三类关键字
kotlin
撩得Android一次心动4 天前
Jetpack Compose 知识点整理1【个人用】
android·学习·kotlin·android jetpack·compose
ForteScarlet4 天前
Kotlin 2.4.20 现已发布,新特性多不多?
android·java·开发语言·开源·kotlin·jetbrains
JMchen1234 天前
Cursor高阶玩法:.cursorrules编写与Spec-Driven开发实战,把AI调教成专属结对编程搭档
人工智能·vscode·kotlin·cursor·结对编程·spec-driven·.cursorrules
hai_android4 天前
Kotlin 协程作用域源码剖析
android·kotlin
leluckys4 天前
【KMP】-KMP 项目commonMain、androidMain、iosMain 到底放什么?
kotlin