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" }) } }
在
Activity
或Fragment
中:lifecycleScope.executeAsyncTask(onPreExecute = { // ... }, doInBackground = { // ... "Result" // send data to "onPostExecute" }, onPostExecute = { // ... here "it" is a data returned from "doInBackground" })
使用viewModelScope
或lifecycleScope
将下一行(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时,至少更容易掌握。