android - 如何通过触摸中断长时间的处理?
问题描述
如果用户需要,我需要编程一种方式,允许用户通过触摸特定按钮来中断我的长时间处理。
因此,如果他想中断,我需要 2 个线程,一个运行我的长进程,另一个等待用户交互
从我阅读的所有内容中,我了解到对我来说最好的解决方案是协程
但是,我之前从未在Kotlin中使用过协程。所以我是那个功能的外行。
老实说,互联网上的教程对我来说都不够清楚。困难重重,我能够做点什么。
下面的代码在按钮btRunOrStop
点击处理内部,它工作正常,与标准有所不同:
if (! solved) // Stopping the long process
bStat.setText("") // clearing the progress
solving = false
runBlocking { jobGl.cancelAndJoin()} // Cancel and ends my long process.
} else { // Starting the long process
bStat.setText("Solving ...") // static progress
GlobalScope.launch {
try {
solving = true // Warn that the long process is running
jobGl = async {
runningProcess(param) // Finally my rocket was launched
}
retGl = jobGl.await() // return of my process, after running OK
solved = true // warn that the process has ended OK.
solving = false // No more pending process
// Generate an artificial button touch
mEv = MotionEvent.obtain(x,y,MotionEvent.ACTION_UP,0F,0F,0)
// False click to post processing after process OK.
btRunOrStop.dispatchTouchEvent(mEv)
}
finally{} // Exception handing. It does not work.
}
}
else { code after successful execution}
最后是我漫长的过程:
suspend fun solveEquac(param:Double):Double {
..... if (! solving) return ......
..... if (! GlobalScope.isActive) return ... // It does not work.
}
不幸的是,我被要求使用变量 ( solving
) 而不是使用isActive
或异常块 ( ),这是Kotlintry finally
官方文档推荐的,因为它不起作用。该过程停止,但只有在它结束之后。
我的问题:
1)GlobalScope
在同一活动中 使用是否可能导致内存泄漏?
2)如果是,它会在什么情况下发生?
3)如果是,我该如何避免?
4)为什么异常处理(try finally
)或isActive
不起作用?
解决方案
是的,您应该避免使用 GlobalScope,因为它会使取消作业和防止泄漏变得笨拙。它是一个单例,所以它会比你的活动更长寿。发生泄漏是因为您的launch
lambda 通过使用 Activity 的属性来捕获对您的 Activity 的引用。
作为旁注,isActive
您的 GlobalScope 始终如此,因为您从未取消您的 GlobalScope,只有它的一个子作业。如果您一次将 GlobalScope 用于一项以上的工作,您可能不想取消它。
使用 CoroutineScope 最简单的方法之一就是将它作为委托附加到您的 Activity,因此您的 Activity 是它自己的范围,允许您直接调用launch
. 你只需要记住取消它,onDestroy()
这样工作就不会超过你的活动。(至少不会长久,只要你让你的工作可以取消——见下文。)
class MyActivity: AppCompatActivity(), CoroutineScope by MainScope() {
//...
override fun onDestroy() {
super.onDestroy()
cancel() // cancel any jobs in this Activity's scope
}
}
最佳实践是始终让您的suspend
函数在内部为其操作选择正确的调度程序。要使您的挂起函数可取消,它必须是其中包含退出点的东西,这些退出点通常是调用取消检查挂起函数(如调用它们的yield()
和和您自己的函数)。delay()
如果它为假,您也可以检查isActive
并提前返回,这使您的函数可取消但可用,就像yield()
退出另一个挂起函数一样。所以如果有一个大的for循环,你可以yield()
在循环中调用给它一个退出点。或者,如果有一堆连续的步骤,则yield()
在其间放置调用。另一种方法是继续检查状态isActive
,这是上下文的属性,因此您可以直接从withContext
块中引用它。
// Just an example. You'll have to come up with a way to break up your calculation.
suspend fun solveEquation(param:Double): Double = withContext(Dispatchers.Default) {
val x = /** some calculation */
yield()
val y = /** some calculation with x */
yield()
var z = /** some calculation with y */
for (i in 0..1000) {
yield()
z += /** some calculation */
}
z
}
我不确定你想做什么try/finally
。您只需要在处理流时像往常一样使用它们。您可以将 ayield()
放在try
oruse
块内,并确信该finally
块将执行而不管取消。
为了使您的工作可以取消,我建议使用一个在不可运行时为 null 的变量:
var calcJob: Job? = null // Accessed only from main thread.
然后你的按钮监听器可以是这样的:
btRunOrStop.onClickListener = {
// Cancel job if it's running.
// Don't want to join it because that would block the main thread.
calcJob?.let {
bStat.setText("")
it.cancel()
calcJob = null
} ?: run { // Create job if one didn't exist to cancel
calcJob = launch { // Called on the Activity scope so we're in the main UI thread.
bStat.setText("Solving ...")
mEv = MotionEvent.obtain(x,y,MotionEvent.ACTION_UP,0F,0F,0)
btRunOrStop.dispatchTouchEvent(mEv)
// You might want to check this doesn't trigger the onClickListener and
// create endless loop. (I don't know if it does.)
val result = solveEquation(someParam) // background calculation
// Coroutine resumes on main thread so we can freely work with "result"
// and update UI here.
calcJob = null // Mark job as finished.
}
}
}
因此,通过遵循使用= withContext(Dispatchers.Default /*or IO*/){}
来定义挂起函数的做法,您可以安全地在其他任何地方执行顺序 UI 操作,并且代码launch
块中的代码将非常干净。
推荐阅读
- perl - 使用 Spreadsheet::WriteExcel 读取文件并将段写入 Excel
- python - 当在 VS Code 中使用 render_template 调用模板时,Python 3.8.5 Flask 在 WSGI 应用程序中出现内部服务器错误 - 500 服务器错误
- c# - NET5 - 如何在单台机器上运行大量 Web 验收测试
- python - 在 Nginx + Digital Ocean Deployment 中加载静态文件时遇到问题
- visual-studio-code - 内部人员 vscode 的 jupyter 扩展中是否有 Run all cell below 按钮?
- javascript - 使用 JS 或 JQuery 连接后如何不显示没有输入值的输入?
- r - 将数据框中的所有值乘以另一个数据框中的单列并将结果保存在新对象中
- react-native - 卸载可可豆荚中链接的包以响应本机 0.63
- mysql - MySQL/Mariadb 每条记录的开销?
- c# - 按平均考试成绩降序分组?