Kotlin新式管道Channel融合flow流,协程实现Android废弃的AsyncTaskLoader(B)
测试运行效果的方法:
Kotlin
import kotlinx.coroutines.async
import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking
fun main() {
val TAG = "main"
runBlocking {
val dvm = MyDVM()
dvm.dataChange(object : DataCallback<String>() {
override fun onDataChange(old: String?, new: String?) {
println("收到 old=$old new=$new")
}
})
async(Loader.THREAD_POOL) {
repeat(5) {
println("--")
println("$TAG startLoad $it ${Thread.currentThread().name}")
dvm.mLoader?.startLoad()
delay(3000)
}
}
}
}
以下是实现的主体架构:
Kotlin
open class DataCallback<D> {
open fun onDataChange(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(Loader.THREAD_POOL).async {
mLoader?.mResultChannel?.receiveAsFlow()?.collect {
mLiveData.value = it as D
}
}
}
fun dataChange(callback: DataCallback<D>?) {
mLiveData.onChange(callback)
}
open fun getLoader(): Loader<D>? {
TODO()
}
}
Kotlin
import kotlin.properties.Delegates
import kotlin.reflect.KProperty
class LiveData<D> {
private val TAG: String = "LiveData"
private var mDataCallback: DataCallback<D>? = null
var value: D? by Delegates.observable(
initialValue = null,
onChange = { prop: KProperty<*>, oldVal: D?, newVal: D? ->
mDataCallback?.onDataChange(oldVal, newVal)
}
)
fun onChange(callback: DataCallback<D>?) {
mDataCallback = callback
}
}
Kotlin
import kotlinx.coroutines.CancellationException
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 THREAD_POOL = newFixedThreadPoolContext(nThreads = 4, name = "my-thread")
}
val mResultChannel = Channel<D?>()
val mTaskChannel = Channel<Msg?>()
private var mTask: Job?
constructor() {
println("$TAG constructor")
mTask = CoroutineScope(THREAD_POOL).async {
mTaskChannel.receiveAsFlow()
.onStart { println("onStart") }
.onCompletion { println("onCompletion") }
.onEmpty { println("onEmpty") }
.onEach {}.flowOn(THREAD_POOL)
.collect {
async(THREAD_POOL) {
val result = loadInBackground()
mResultChannel.send(result)
}
}
mTaskChannel.invokeOnClose {
println("$TAG invokeOnClose")
}
}
}
open fun loadInBackground(): D? {
return TODO("")
}
fun startLoad() {
runBlocking {
async(THREAD_POOL) {
mTaskChannel.send(Msg())
}
}
}
private fun stopTask() {
mTask?.cancel(CancellationException(""))
}
fun close() {
mTaskChannel.close()
mResultChannel.close()
stopTask()
}
class Msg {
val timestamp = System.currentTimeMillis()
}
}
上面是基础框架。下面是开发者需要根据自身业务定制开发的内容:
Kotlin
class MyDVM : DVModel<String, Loader<String>> {
private val TAG = "MyDVM"
constructor() {
println("$TAG constructor")
}
override fun getLoader(): Loader<String>? {
return MyLoader()
}
}
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
}
}
运行输出:
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
收到 old=null new=Hello,world! 1766975534036
--
main startLoad 1 my-thread-1
MyLoader loadInBackground ... my-thread-3
MyLoader loadInBackground done my-thread-3
收到 old=Hello,world! 1766975534036 new=Hello,world! 1766975537049
--
main startLoad 2 my-thread-3
MyLoader loadInBackground ... my-thread-4
MyLoader loadInBackground done my-thread-4
收到 old=Hello,world! 1766975537049 new=Hello,world! 1766975540054
--
main startLoad 3 my-thread-3
MyLoader loadInBackground ... my-thread-4
MyLoader loadInBackground done my-thread-4
收到 old=Hello,world! 1766975540054 new=Hello,world! 1766975543063
--
main startLoad 4 my-thread-4
MyLoader loadInBackground ... my-thread-1
MyLoader loadInBackground done my-thread-1
收到 old=Hello,world! 1766975543063 new=Hello,world! 1766975546077
Process finished with exit code 0
相关: