我的测试表明Android在Executor线程中抛出时会默默地忽略AssertionError。在logcat中没有打印任何内容,应用程序也不会崩溃。这似乎是错的,其他人是否也能够重现这一点?知道为什么会这样,或者有办法解决或修复?
这是我的测试:
public class TestActivity extends Activity {
private final ScheduledExecutorService exec = Executors.newSingleThreadScheduledExecutor();
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
exec.execute(new Runnable() {
@Override
public void run() {
System.err.println("about to fail");
if (true ) throw new AssertionError();
}
});
}
}
答案 0 :(得分:0)
这不仅仅是Android;这是(恼人的)沼泽标准的Java行为。 runnable抛出未捕获异常的ExecutorService线程只是在没有记录的情况下终止。
最简单的解决方法是将Executors包装成这样 - 您可以手动执行此操作,也可以创建一个替代而不是库存的替换ExecutorService:
public class PrintingExecutor implements ExecutorService {
private final ExecutorService mDelegate;
public PrintingExecutor (ExecutorService pDelegate) {
mDelegate = pDelegate;
}
@Override
public Future<?> submit(Runnable task) {
return mDelegate.submit (new PrintingRunnable(task));
}
//override the rest of Executor's methods
private static class PrintingRunnable implements Runnable {
private final Runnable mDelegate;
public PrintingRunnable (Runnable toRun) {
mDelegate = toRun;
}
@Override
public void run () {
try {
mDelegate.run();
} catch (Throwable t) {
t.printStackTrace();
}
}
}
}
答案 1 :(得分:0)
我认为这个问题与Exception handling in ThreadPools和How to properly catch RuntimeExceptions from Executors?
重复所以我想我明白现在发生了什么。这不是Android特定的问题,它是通用Java。该问题与您在服务上安排任务的方式有关。
如果使用execute
,则运行时异常将停止服务线程,VM将通过打印堆栈跟踪并停止整个过程来处理(除非您使用Scheduled
执行程序,否则进一步了解。)
如果您使用submit
,则会返回Future
,当您在get()
上致电Future
时,它会抛出java.util.concurrent.ExecutionException
,您可以检查看看发生了什么。在这种情况下,服务会保留处理任务。
因此,如果您要使用get()
,或仅使用Future
,则必须确保在submit
上致电execute()
。
<强>无论其强>:
Executors.newSingleThreadScheduledExecutor()
与Executors.newSingleThreadExecutor)
不同。 Scheduled
执行程序带来了进一步的复杂化,因为它会在线程死亡时重新启动线程并且它会以静默方式执行。因此,在这种情况下,您需要打包Runnable
as suggested in this answer。
以下是一些展示差异的代码:
private final ScheduledExecutorService service = Executors.newSingleThreadExecutor();
public static void testExecuteBomb(ExecutorService service) {
service.execute(new Runnable() {
@Override
public void run() {
printWithMillis("execute bombing...");
if (true) throw new RuntimeException("Bomb");
}
});
sleep();
service.shutdownNow();
}
public static void testSubmitBomb(ExecutorService service) {
try {
Future t = service.submit(new Runnable() {
@Override
public void run() {
printWithMillis("submit bombing...");
if (true) throw new RuntimeException("Bomb");
}
});
sleep();
try {
printWithMillis(String.valueOf(t.get()));
} catch (Exception e) {
throw new RuntimeException(e);
}
} finally {
service.shutdownNow();
}
}