在后台线程中执行硬任务,在主线程中返回结果

Dmi*_*try 3 multithreading android android-asynctask kotlin

我花了一些时间来寻找一个开发人员友好的解决方案(不向项目添加依赖项)如何在后台线程中执行一些艰巨的任务,并在任务完成后将结果返回到主线程。我找到了允许这样做的“AsyncTask”。但是要使用它,您需要为需要在后台运行的每个任务编写样板代码。我是 iOS 开发者,决定尝试 Android 相关的开发。所以在 Swift 中你可以简单地使用下一个代码来完成这个任务:

DispatchQueue.global().async(execute: {
      //Do some hard task in background
   DispatchQueue.main.async(execute: {
      //Return to main
   })
})
Run Code Online (Sandbox Code Playgroud)

这看起来很简单。但是在 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)
    }
}
Run Code Online (Sandbox Code Playgroud)

和经理

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
        }
    }

}
Run Code Online (Sandbox Code Playgroud)

现在我像这样使用它:

AsyncManager.execute({
   //Do some hard task in background
}, {
  //Return to main
})
Run Code Online (Sandbox Code Playgroud)

看起来对开发人员友好。

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")
Run Code Online (Sandbox Code Playgroud)

和日志:

2019-03-27 17:11:00.719 17082-1​​7082/com.test.testapp E/MAIN:不应阻塞主线程

2019-03-27 17:11:00.722 17082-1​​7082/com.test.testapp E/MAIN:不应阻塞主线程 - 1

2019-03-27 17:11:00.722 17082-1​​7124/com.test.testapp E/TASK:启动后台任务

2019-03-27 17:11:05.737 17082-1​​7124/com.test.testapp E/TASK:完成后台任务,结果:来自后台的值

2019-03-27 17:11:05.738 17082-1​​7082/com.test.testapp E/TASK:在主线程中启动任务,结果来自后台:来自后台的值

所以问题是专业的 Android 开发人员如何看待这个解决方案。如果我使用它,我会遇到什么问题。也许有一些理由不使用这个解决方案。

ian*_*ake 7

如果您使用 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)
}
Run Code Online (Sandbox Code Playgroud)

请注意,Kotlin 协程在结构化并发运行,因此您通常希望避免使用GlobalScope,而是将协程范围绑定到您的 Activity / Fragment 生命周期。这通常需要您自己立即完成。

  • @Dmitry - 我认为你需要克服这种思维模式。为已经存在的东西写一个更糟糕的版本,而且已经存在了 100 倍,并且经过了更好的测试,这从来都不是一个可持续的方法。 (2认同)