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

相关推荐
モンキー・D・小菜鸡儿5 小时前
kotlin 推牌九(麻将)小游戏
kotlin·小游戏
JMchen1235 小时前
跨平台相机方案深度对比:CameraX vs. Flutter Camera vs. React Native
java·经验分享·数码相机·flutter·react native·kotlin·dart
DokiDoki之父1 天前
边写软件边学kotlin(一):Kotlin语法初认识:
android·开发语言·kotlin
fundroid2 天前
Kotlin 泛型进阶:in、out 与 reified 实战
android·开发语言·kotlin
JMchen1233 天前
现代Android图像处理管道:从CameraX到OpenGL的60fps实时滤镜架构
android·图像处理·架构·kotlin·android studio·opengl·camerax
JMchen1234 天前
Android CameraX深度解析:从Camera1到CameraX的相机架构演进
android·java·数码相机·架构·kotlin·移动开发·android-studio
倔强的石头1064 天前
【Linux指南】进程控制系列(五)实战 —— 微型 Shell 命令行解释器实现
linux·运维·kotlin
Hz4535 天前
Android Jetpack核心组件协同实战:Navigation 3.X+Lifecycle+Flow+Hilt的架构革新
android·kotlin
JMchen1235 天前
Android音频编码原理与实践:从AAC到Opus,深入解析音频编码技术与移动端实现
android·经验分享·学习·kotlin·android studio·音视频·aac
JMchen1235 天前
Android音频处理全解析:从3A算法到空间音频,打造专业级音频体验
android·经验分享·算法·kotlin·android studio·音视频