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