Coroutine生命周期范围和ISACTIVE不起作用?
我一直在与Coroutines合作,以为它们像Java线程一样工作,在长期运行的操作(例如编写大文件)中,Isalive boolean与检查IsInterrupt()相同。
过去,Coroutine一直按照预期的方式工作,但是在当前版本1.6.1-native-Mt中似乎不再像这样了。在下面的代码中,即使我切换到1.6.1非MultithReaded库,Isalive检查中的代码似乎也不会运行。
如果这是应该工作的方式?取消作业后,应采取什么正确的清理方式?
同样,在将Coroutine附加到示波器上时,在这种情况下,当新活动开始时,是否应该自动进行自动处理?如果没有像在Onpause()/onstop()阶段中的Java中那样手动取消手动取消,则似乎继续运行。
Kotlin Coroutine:
import android.os.Bundle
import android.util.Log
import android.widget.Button
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.*
class TestActivity : AppCompatActivity() {
private val tag = "TEST"
lateinit var job: Job
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_test)
val button = findViewById<Button>(R.id.stopbutton)
job = lifecycleScope.launch(Dispatchers.IO) {
(1..120).forEach {
// big long operation e.g. writing a large data file
Log.d(tag, "ticking $it")
if ( !isActive ) {
// this code never happens after....
// 1. button is clicked to cancel job
// 2. app is killed
Log.d(tag, "ticking stopped by button")
// clean up the partially written file
return@launch
}
delay(1000)
}
}
button.setOnClickListener {
job.cancel()
}
}
}
预期输出(应与Java线程代码相同):
ticking 1
ticking 2
ticking 3
ticking stopped
实际Coroutine输出:
ticking 1
ticking 2
ticking 3
以及用于处理线程中断的Java代码。
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.os.SystemClock;
import android.util.Log;
import android.view.View;
import android.widget.Button;
public class MainActivity extends AppCompatActivity {
protected Thread t = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
t = new Thread(){
@Override
public void run() {
for ( int x = 0; x < 120; x++ ) {
Log.d("TEST", "ticking " + x);
if ( interrupted() ) {
Log.d("TEST", "ticking stopped by button");
return;
}
// just for demoing
SystemClock.sleep(1000);
}
}
};
t.start();
Button btn = findViewById(R.id.button);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
t.interrupt();
}
});
}
}
更新1
在关注@Sergio和 @pawel的快速回复后,我删除了delay(),并将其替换为SystemClock.sleep(),就像在Java中一样,以模拟长期操作。现在,它的工作原理如预期的那样,尽管我想知道为什么这么多教程在没有突出问题的情况下使用它。
仍在尝试弄清楚如果不自动cancel()onstop()(如下所述)将coroutine附加到生命周期范围的意义是什么:
https://medium.com/androiddevelopers/cancellation-incellation-in-coroutines-coroutines-aa6b90163629
I've been working with Coroutines thinking they work like Java Threads where the isAlive boolean is the same as checking for isInterrupted() in a long running operation such as writing a large file.
In the past the Coroutine has worked as expected but in the current version 1.6.1-native-mt it doesn't seem to work like that any more. In the code below, the code inside the isAlive check doesn't seem to run even when I switch to the 1.6.1 non-multithreaded library.
If this is the way it's supposed to work? What should the correct way of performing a clean-up after a job is cancelled?
Also when attaching a Coroutine to a scope, in this case the Activity, shouldn't it be auto-cancelled when a new Activity is started? It seems to keep running if it isn't cancelled manually like in Java during the onPause()/onStop() phase.
Kotlin Coroutine:
import android.os.Bundle
import android.util.Log
import android.widget.Button
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.*
class TestActivity : AppCompatActivity() {
private val tag = "TEST"
lateinit var job: Job
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_test)
val button = findViewById<Button>(R.id.stopbutton)
job = lifecycleScope.launch(Dispatchers.IO) {
(1..120).forEach {
// big long operation e.g. writing a large data file
Log.d(tag, "ticking $it")
if ( !isActive ) {
// this code never happens after....
// 1. button is clicked to cancel job
// 2. app is killed
Log.d(tag, "ticking stopped by button")
// clean up the partially written file
return@launch
}
delay(1000)
}
}
button.setOnClickListener {
job.cancel()
}
}
}
Expected Output (Should be same as Java Thread code):
ticking 1
ticking 2
ticking 3
ticking stopped
Actual Coroutine Output:
ticking 1
ticking 2
ticking 3
And the Java code for handling thread interrupts.
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.os.SystemClock;
import android.util.Log;
import android.view.View;
import android.widget.Button;
public class MainActivity extends AppCompatActivity {
protected Thread t = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
t = new Thread(){
@Override
public void run() {
for ( int x = 0; x < 120; x++ ) {
Log.d("TEST", "ticking " + x);
if ( interrupted() ) {
Log.d("TEST", "ticking stopped by button");
return;
}
// just for demoing
SystemClock.sleep(1000);
}
}
};
t.start();
Button btn = findViewById(R.id.button);
btn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
t.interrupt();
}
});
}
}
Update 1
After following @Sergio and @Pawel's quick replies I have removed delay() and replaced it with SystemClock.sleep() just like in Java to simulate long operations. Now it works as expected although I wonder why so many tutorials use it without highlighting the issue.
Still trying to figure out what is the point of attaching a coroutine to a lifecycle scope if it doesn't auto cancel() onstop() such as described here:
https://medium.com/androiddevelopers/cancellation-in-coroutines-aa6b90163629
Or maybe I'm missing the point?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
这是因为
delay()
在内部还检查了取消函数,如果取消发生在其执行过程中,则通过抛出取消异常来停止Coroutine。要获得结果,您想尝试使用thread.sleep()
或类似的阻止睡眠方法而不是delay> delay
或尝试包装调用delay> delay> delay> delay>
进入try-catch
块。It's because
delay()
function internally also checks for cancellations, and if the cancellation happens during its execution it stops a coroutine by throwing a cancellation exception. To get the result you want try to useThread.sleep()
or similar blocking sleeping methods instead ofdelay
or try to wrap callingdelay()
intotry-catch
block.Coroutines中的取消确实是 ISACTIVE 是正确执行的一种方法。
但是您错过了内置的暂停功能内置的部分(在这种情况下 delay )检查并在内部投掷
cancellationException
,以便在取消后不会获得其他循环执行。如果您想对取消做出反应“ rel =“ nofollow noreferrer”>
positionhandler
:并更改取消代码,因此您可以识别取消消息:
Cancellation in coroutines is indeed cooperative and checking
isActive
is one way to do it properly.But you've missed the part that built in suspending functions (in this case
delay
) check and throw aCancellationException
internally so you won't get another loop execution after cancellation.If you want to react to cancellation you can set a
CompletionHandler
:And alter your cancellation code so you can discern the cancellation message:
如果取消Coroutine,则
delay()
或实际上任何其他暂停调用都可以扔concellationException
。因此,一种方法是简单地使用try-catch-catch-catch在出口上清理,因为这些方法仍在运行。如果您需要在清理过程中调用暂停功能,则需要使用
withContext(noncancellable){...}
,以便内部的悬浮函数不会立即投掷concellationException
再次。If the coroutine is cancelled, then
delay()
or practically any other suspend call may throwCancellationException
. So one approach is to simply use try-catch-finally to clean up on exit, as those are still run.If you need to call suspend functions during cleanup, you need to use
withContext(NonCancellable) { ... }
so that the suspend functions inside that don't immediately throwCancellationException
again.