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

相关推荐
Kapaseker2 天前
别再只用 Loading、Success、Error 表示所有页面状态了
android·kotlin
雨白2 天前
深入理解 Kotlin 协程 (九):互通有无,解构 Channel 缓冲策略与底层无锁机制
android·kotlin
蜡台3 天前
Android WebView 设计指南
android·java·kotlin
Kapaseker4 天前
深入理解 Compose 副作用
kotlin
雨白4 天前
深入理解 Kotlin 协程 (八):拾遗补阙,探秘官方框架的调度细节与取消闭环
android·kotlin
GitLqr4 天前
别被“Flutter 传感器延迟 150ms”带偏了:这可能只是你的实现方式错了
flutter·架构·kotlin
我命由我123454 天前
Android 在构建过程中,发现有两个依赖库都包含了相同路径的资源文件
android·java·开发语言·java-ee·kotlin·android-studio·android runtime
Coffeeee4 天前
谷歌的一个优化建议,让我重新学了一遍Android里面如何正确处理位图
android·google·kotlin
Android打工仔6 天前
Continuation 到底是谁创建的?
android·kotlin
Android-Flutter6 天前
android fragment 使用
android·kotlin