Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/android/213.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Android 如何通过触摸中断长时间的处理?_Android_Multithreading_Kotlin - Fatal编程技术网

Android 如何通过触摸中断长时间的处理?

Android 如何通过触摸中断长时间的处理?,android,multithreading,kotlin,Android,Multithreading,Kotlin,我需要编程一种方式,允许用户通过触摸特定的按钮来中断我的长时间处理,如果用户需要的话 因此,如果他想中断,我需要两个线程,一个运行我的长进程,另一个等待用户交互 从我所读到的所有内容中,我明白了对我来说最好的解决方案是协作 然而,我以前从未在Kotlin中使用过协同程序。所以我在这方面是外行 老实说,互联网上的教程对我来说都不够清晰。 我好不容易才做了一件事 下面的代码位于按钮内部btRunOrStop单击处理,它工作正常,与标准有所不同: if (! solved) // Stoppi

我需要编程一种方式,允许用户通过触摸特定的按钮来中断我的长时间处理,如果用户需要的话

因此,如果他想中断,我需要两个线程,一个运行我的长进程,另一个等待用户交互

从我所读到的所有内容中,我明白了对我来说最好的解决方案是协作

然而,我以前从未在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
),而不是使用Kotlin官方文档推荐的
isActive
或异常块(
try finally
),因为它根本不起作用。这个过程停止了,但只有在它结束之后

我的问题是:

1)在同一活动中使用
GlobalScope
是否可能发生内存泄漏

2)如果是,在什么情况下会发生

3)如果是,我如何避免


4)为什么异常处理(
try finally
)或
isActive
不起作用

是的,您应该避免使用GlobalScope,因为它会使取消作业和防止泄漏变得笨拙。它是一个单身汉,所以它会比你的活动寿命长。发生泄漏的原因是,
launch
lambda正在使用活动的属性捕获对活动的引用

作为旁注,
isActive
在您的GlobalScope上始终是正确的,因为您从未取消过您的GlobalScope,只有它的一个子作业。如果一次使用GlobalScope执行多个任务,您可能不想取消它

使用CoroutineScope最简单的方法之一是将其作为委托附加到您的活动中,这样您的活动就是它自己的范围,允许您直接调用
launch
。您只需记住在
onDestroy()
中取消它,这样作业就不会比您的活动寿命长。(至少不会太久,只要你的工作可以取消——见下文。)

最佳做法是始终让您的
挂起
函数在内部为其操作选择正确的调度程序。要使挂起函数可取消,它必须具有退出点,通常是对取消检查挂起函数的调用(如
yield()
delay()
以及您自己调用它们的函数)。您也可以选择检查
isActive
,如果其为false,则提前返回,这使得您的函数可以取消,但可以像
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
做什么。您只需要像处理流时一样使用它们。您可以将
yield()
放在
try
use
块中,并确信
最终将执行
块,而不考虑取消

为了使作业可取消,我建议在不可运行时使用空变量:

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/*或IO*/){}
定义挂起函数的做法,您可以安全地在其他任何地方执行顺序UI操作,并且
启动
块中的代码将非常干净。

我将尝试按照您明智的建议执行。但是,我的应用程序是一个单一的活动应用程序。我使用自定义对话框以编程方式创建了所有屏幕和视图,没有XML。即使如此,你相信内存泄漏吗?不知道XML和它有什么关系。如果您的活动已被破坏,但某个寿命较长的对象仍在引用它,则它将泄漏。即使您只有一个活动应用程序,当您退出活动时,应用程序本身也不会立即被杀死,因此如果活动泄漏,您的死应用程序仍将占用内存。如果用户随后再次打开你的应用程序,则现在有两个活动,即使其中一个已死亡。如果这种情况反复发生,您将有几个并发的泄漏活动。这是否真的是一个问题取决于您。也许即使你的工作没有被取消,你仍然确信它会在一秒钟内完成。然后,您泄漏的活动将在一秒钟后被清除,因此暂时泄漏可能是可以的。不过,你肯定不想在网络运营中这样做,因为他们的生活是不可预测的。但是GlobalScope的唯一优点是为您节省了几行代码来创建本地活动范围。为什么仅仅因为你可以,就在这个简单的项目上进行不好的实践呢?当然,我同意你的看法。只是好奇而已,我已经测试过你的建议了<在
destroy
中,code>CoroutineScope by MainScope()
cancel()
起作用,但
isActive()
MainScope()。isActive()
继续返回true
Yield
只能在挂起函数中使用,但我在处理过程中使用嵌套函数。当我将所有函数都作为挂起函数时,编译器会给出一个内部错误。
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.
        }
    }
}