我花了一些时间来找到开发人员友好的解决方案(不向项目中添加依赖项),该解决方案涉及如何在后台线程中执行一些艰巨的任务,并在任务完成后将结果返回到主线程。我发现“ AsyncTask”可以做到这一点。但是要使用它,您需要为需要在后台运行的每个任务编写样板代码。我是iOS开发人员,决定尝试与Android相关的开发。因此,在Swift中,您只需使用下面的代码即可完成此任务:
DispatchQueue.global().async(execute: {
//Do some hard task in background
DispatchQueue.main.async(execute: {
//Return to main
})
})
这看起来很简单。但是在Kotlin中,我找不到这种简单的解决方案,因此决定创建它。
这就是我做的:
我创建了通用类
import android.os.AsyncTask
class BaseAsyncTask<M>: AsyncTask<()->M, Int, M>() {
var completion: ((M)->Unit)? = null
override fun doInBackground(vararg params: (() -> M)?): M? {
for (p in params) {
return p?.invoke()
}
return null
}
override fun onPostExecute(result: M) {
super.onPostExecute(result)
completion?.invoke(result)
}
}
和经理
class AsyncManager {
companion object {
fun <M>execute(inBackground: ()->M, inMain: (M)->Unit): BaseAsyncTask<M> {
val task = BaseAsyncTask<M>()
task.completion = inMain
task.execute(inBackground)
return task
}
fun <M>execute(inBackground: ()->M): BaseAsyncTask<M> {
val task = BaseAsyncTask<M>()
task.execute(inBackground)
return task
}
}
}
现在我这样使用它:
AsyncManager.execute({
//Do some hard task in background
}, {
//Return to main
})
看起来对开发人员友好。
Log.e("MAIN", "MAIN THREAD SHOULD NOT BE BLOCKED")
AsyncManager.execute({
Log.e("TASK", "Started background task")
val retval = "The value from background"
Thread.sleep(5000)
Log.e("TASK", "Finished background task with result: " + retval)
retval
}, {
Log.e("TASK", "Started task in Main thread with result from Background: " + it)
})
Log.e("MAIN", "MAIN THREAD SHOULD NOT BE BLOCKED - 1")
和日志:
2019-03-27 17:11:00.719 17082-17082 / com.test.testapp E /主要:主要 不应阻塞螺纹
2019-03-27 17:11:00.722 17082-17082 / com.test.testapp E /主要:主要 不应阻止线程-1
2019-03-27 17:11:00.722 17082-17124 / com.test.testapp E / TASK:已启动 后台任务
2019-03-27 17:11:05.737 17082-17124 / com.test.testapp E /任务:已完成 具有结果的后台任务:后台的值
2019-03-27 17:11:05.738 17082-17082 / com.test.testapp E /任务:已启动 主线程中的任务,其背景为背景:来自的值 背景
所以问题是专业的Android开发人员对该解决方案有何看法。万一我要使用它,我会遇到什么问题。也许有某些原因不使用此解决方案。
答案 0 :(得分:2)
ianhanniballake's answer是正确的,但可能有点不完整,因此我想提供一个完整的通用示例。
build.gradle(:app):
dependencies { // this line is probably already present
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.3"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.3"
}
全局CoroutineScope不绑定任何作业。
使用GlobalScope 推出整体运作的顶级协程 应用程序寿命,并且不会过早取消。应用 代码通常应使用应用程序定义的CoroutineScope。
使用 强烈建议不要在GlobalScope实例上进行异步或启动。 取自here
因此,您想将具有生命周期的任何类用作CoroutineScope
,以便其死亡时将正在运行的后台任务带到坟墓。人们经常建议为此使用一项活动。但是,有一个case to be made,您不希望任何外部类将您的活动用作其CoroutineScope
,因此您可以改用受保护的字段:
protected val scope = CoroutineScope(Job() + Dispatchers.Main)
在撰写本文时,我不知道为什么必须在这里创建Job()
。我所知道的是+
运算符已重载以将这两个上下文合并为一个。对于分派器部分,您可以选择一个合理的部分。选项包括
Dispatchers.Main
用于UI线程Dispatchers.Default
用于后台线程池Dispatchers.IO
用于阻止I / O密集型操作Dispatchers.Unconfined
表示何时真正知道自己在做什么。 This article不鼓励“正常”使用它。 现在,所有这一切都变得毫无用处了,代码变得异常简单:
import kotlin.coroutines.*
// ...
myButton.setOnClickListener() { v: View? ->
myButton.setColorToYellow() // some UI thread work
scope.launch(Dispatchers.Default) {
val result = longComputation() // some background work
withContext(Dispatchers.Main) {
// some UI thread work for when the background work is done
root.findViewById<TextView>(R.id.text_home).text = "Result: $result"
}
}
myButton.setColorToRed() // more UI thread work. this is done instantly
}
当然,这可以在任何地方完成-我只是使用一个按钮和一个onClickListener
来举例说明可能的用例。
答案 1 :(得分:1)
如果您使用的是Kotlin,则正确的方法是通过Coroutines,它可以使您编写如下代码:
// Launch a coroutine that by default goes to the main thread
GlobalScope.launch(Dispatchers.Main) {
// Switch to a background (IO) thread
val retval = withContext(Dispatchers.IO) {
Log.e("TASK", "Started background task")
val retval = "The value from background"
Thread.sleep(5000)
Log.e("TASK", "Finished background task with result: " + retval)
retval
}
// Now you're back the main thread
Log.e("TASK", "Started task in Main thread with result from Background: " + retval)
}
请注意,Kotlin协程在structured concurrency下运行,因此您通常希望避免使用GlobalScope
,而是将协程的范围限定为与“活动/片段”生命周期相关联。通常,此刻需要立即自己完成。