异步症作为Kotlin Coroutine



to asynctask的典型用途:我想在另一个线程中运行一个任务,在完成该任务之后,我想在我的UI线程中执行某些操作,即隐藏一个进度栏。/p>

任务要在TextureView.SurfaceTextureListener.onSurfaceTextureAvailable中启动,完成后,我想隐藏进度栏。同步执行此操作是不起作用的,因为它会阻止构建UI的线程,而将屏幕变黑,甚至没有显示我要隐藏的进度栏。

到目前为止,我使用此信息:

inner class MyTask : AsyncTask<ProgressBar, Void, ProgressBar>() {
    override fun doInBackground(vararg params: ProgressBar?) : ProgressBar {
        // do async
        return params[0]!!
    }
    override fun onPostExecute(result: ProgressBar?) {
        super.onPostExecute(result)
        result?.visibility = View.GONE
    }
}

,但是这些课程不可能丑陋,所以我想摆脱它们。我想和Kotlin Coroutines一起做。我尝试了一些变体,但它们似乎都没有用。我很可能会怀疑工作的那个是:

runBlocking {
        // do async
}
progressBar.visibility = View.GONE

但这无法正常工作。据我了解,runBlocking不会像AsyncTask那样启动新线程,这是我需要做的。但是使用thread Coroutine,我看不到合理的方法可以在完成后获得通知。另外,我也不能将progressBar.visibility = View.GONE放入新线程中,因为只有UI线程可以进行此类操作。

我是新手Coroutines事物:

  • 实施 CoroutinesCope 接口。
  • 引用作业 coroutinecontext instances。
  • 使用暂停函数修改器暂停coroutine,而无需阻止主线程调用在背景线程中运行代码的函数时>
  • 使用 withContext(dispatchers.io)函数在后台线程中运行代码和启动函数启动Coroutine。

通常,我为此使用一个单独的类,例如"主持人"或" ViewModel"

class Presenter : CoroutineScope {
    private var job: Job = Job()
    override val coroutineContext: CoroutineContext
        get() = Dispatchers.Main + job // to run code in Main(UI) Thread
    // call this method to cancel a coroutine when you don't need it anymore,
    // e.g. when user closes the screen
    fun cancel() {
        job.cancel()
    }
    fun execute() = launch {
        onPreExecute()
        val result = doInBackground() // runs in background thread without blocking the Main Thread
        onPostExecute(result)
    }
    private suspend fun doInBackground(): String = withContext(Dispatchers.IO) { // to run code in Background Thread
        // do async work
        delay(1000) // simulate async work
        return@withContext "SomeResult"
    }
    // Runs on the Main(UI) Thread
    private fun onPreExecute() {
        // show progress
    }
    // Runs on the Main(UI) Thread
    private fun onPostExecute(result: String) {
        // hide progress
    }
}

使用ViewModel使用viewModelScope更简洁:

class MyViewModel : ViewModel() {
    
    fun execute() = viewModelScope.launch {
        onPreExecute()
        val result = doInBackground() // runs in background thread without blocking the Main Thread
        onPostExecute(result)
    }
    private suspend fun doInBackground(): String = withContext(Dispatchers.IO) { // to run code in Background Thread
        // do async work
        delay(1000) // simulate async work
        return@withContext "SomeResult"
    }
    // Runs on the Main(UI) Thread
    private fun onPreExecute() {
        // show progress
    }
    // Runs on the Main(UI) Thread
    private fun onPostExecute(result: String) {
        // hide progress
    }
}

要使用viewModelScope将下一行添加到应用程序 build.gradle 文件的依赖项中:

implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$LIFECYCLE_VERSION"

在编写final LIFECYCLE_VERSION = "2.3.0-alpha04"


这也是使用kotlin coroutines和CoroutineScope上的扩展功能实现异步任务。

另一种方法是在 CoroutineScope:<<

创建通用扩展功能/p>

fun <R> CoroutineScope.executeAsyncTask(
        onPreExecute: () -> Unit,
        doInBackground: () -> R,
        onPostExecute: (R) -> Unit
) = launch {
    onPreExecute()
    val result = withContext(Dispatchers.IO) { // runs in background thread without blocking the Main Thread
        doInBackground()
    }
    onPostExecute(result)
}

现在我们可以与任何CoroutineScope

一起使用它
  • ViewModel中:

      class MyViewModel : ViewModel() {
          fun someFun() {
              viewModelScope.executeAsyncTask(onPreExecute = {
                  // ...
              }, doInBackground = {
                  // ...
                  "Result" // send data to "onPostExecute"
              }, onPostExecute = {
                  // ... here "it" is a data returned from "doInBackground"
              })
          }
      }
    
  • ActivityFragment中:

      lifecycleScope.executeAsyncTask(onPreExecute = {
          // ...
      }, doInBackground = {
          // ...
          "Result" // send data to "onPostExecute"
      }, onPostExecute = {
          // ... here "it" is a data returned from "doInBackground"
      })
    

使用viewModelScopelifecycleScope将下一行(S)添加到应用程序 build.gradle.gradle 文件:

implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$LIFECYCLE_VERSION" // for viewModelScope
implementation "androidx.lifecycle:lifecycle-runtime-ktx:$LIFECYCLE_VERSION" // for lifecycleScope

在编写final LIFECYCLE_VERSION = "2.3.0-alpha05"

时,您可以在UI主线程上运行progressbar,同时使用Coroutine不调整任务。

在您的替代fun increate()方法中,

GlobalScope.launch(Dispatchers.Main) { // Coroutine Dispatcher confined to Main UI Thread
    yourTask() // your task implementation
}

您可以初始化,

private var jobStart: Job? = null

在Kotlin中,VAR声明意味着该特性是可变的。如果你 将其宣布为Val,它是不变的,仅阅读&amp;无法重新分配。

在OnCreate()方法之外,YourTask()可以作为悬浮功能实现,该功能不会阻止主呼叫者线程。

当函数在等待结果返回时暂停该函数时,其运行线程未阻止以执行其他函数。

private suspend fun yourTask() = withContext(Dispatchers.Default){ // with a given coroutine context
    jobStart = launch {
       try{
        // your task implementation
       } catch (e: Exception) {
             throw RuntimeException("To catch any exception thrown for yourTask", e)
      }
    }
  }

对于您的进度栏,您可以在单击按钮时创建一个按钮以显示进度栏。

buttonRecognize!!.setOnClickListener {
    trackProgress(false)
}

在ongreate()之外,

private fun trackProgress(isCompleted:Boolean) {
    buttonRecognize?.isEnabled = isCompleted // ?. safe call
    buttonRecognize!!.isEnabled // !! non-null asserted call
    if(isCompleted) {
        loading_progress_bar.visibility = View.GONE
    } else {
        loading_progress_bar.visibility = View.VISIBLE
    }
}

另一个提示是检查您的Coroutine是否确实在运行 另一个线程,例如。DefaultDisPatcher-Worker-1,

Log.e("yourTask", "Running on thread ${Thread.currentThread().name}")

希望这很有帮助。

首先,您必须使用launch(context)进行Coroutine,而不是使用runBlocking:https://kotlinlang.org/docs/reference/coroutines/coroutine-context-and-dispatchers.html

第二,要获得onPostExecute的效果,您必须使用

activity.runonuithread(可运行)或view.post(可运行)。

这不使用coroutines,但这是一个快速解决方案,可以在后台运行并在UI上执行某些操作之后。

与其他方法相比,我不确定这种方法的优缺点,但是它可以效法,并且非常容易理解:

Thread {
    // do the async Stuff
    runOnUIThread {
        // do the UI stuff
    }
    // maybe do some more stuff
}.start()

使用此解决方案,您可以轻松地在两个实体之间传递值和对象。您也可以无限期地嵌套。

以下方法可能能够满足您的需求。它需要更少的样板代码,并适用于100%的用途

GlobalScope.launch {
                bitmap = BitmapFactory.decodeStream(url.openStream())
            }.invokeOnCompletion {
                createNotification()
            }
private val TAG = MainActivity::class.simpleName.toString()
    private var job = Job()
    //coroutine Exception
    val handler = CoroutineExceptionHandler { _, exception ->
        Log.d(TAG, "$exception handled !")
    }
    //coroutine context
    val coroutineContext: CoroutineContext get() = Dispatchers.Main + job + handler
    //coroutine scope
    private val coroutineScope = CoroutineScope(coroutineContext)


    fun execute() = coroutineScope.launch {
        onPreExecute()
        val result = doInBackground() // runs in background thread without blocking the Main Thread
        onPostExecute(result)
    }

    private suspend fun doInBackground(): String =
        withContext(Dispatchers.IO) { // to run code in Background Thread
            // do async work
            //delay(5000) // simulate async work
            loadFileFromStorage()
            return@withContext "SomeResult"
        }
    // Runs on the Main(UI) Thread
    private fun onPreExecute() {
        LoadingScreen.displayLoadingWithText(this,"Loading Files",false)
    }
    // Runs on the Main(UI) Thread
    private fun onPostExecute(result: String) {
        //progressDialogDialog?.dismiss()
        LoadingScreen.hideLoading()
        // hide progress
    }

我开始在我的Android项目中迁移我的 AsyncTask东西来使用coroutines ...如果您真的需要在完成异步任务后在UI上做某事(即,您是您的只是在异步中覆盖Doinbackground和Onpostexecute)

val job = CoroutineScope(Dispatchers.IO).async {
    val rc = ...
    return@async rc
}
CoroutineScope(Dispatchers.Main).launch {
    val job_rc = job.await() // whatever job returns is fed to job_rc
    // do UI updates here
}

您不需要使用I/O调度器的工作...如果不是I/O密集型,则可以使用默认值。

但是,等待工作完成的Coroutine需要在主/UI线程上进行,因此您可以更新UI。

是的,有一些语法糖可以用来使上述代码看起来更酷,但是当人们刚开始迁移到使用Coroutines时,至少更容易掌握。

相关内容

  • 没有找到相关文章

最新更新