Android 后台任务 运行 时不显示对话框
Android dialog not showing when background task is running
这是我在这里的问题的扩展,您可以在其中看到包含按钮的片段和正在使用的服务的所有代码:
我有一个按钮,点击它会启动一个服务,收集一些传感器数据,插入数据库。再次单击时,我想显示进度对话框,等待现有后台任务完成,然后关闭进度对话框。
我现在可以使用片段中的 Handler
在片段和服务之间正确地来回发送消息(如我的其他问题所示)。问题是进度对话框不会显示在 UI 线程中,直到执行程序任务队列被清除
在我的片段中,按钮 onClick
看起来像这样:
//Show stop dialog
stopDialog = new ProgressDialog(mainActivity);
stopDialog.setMessage("Stopping...");
stopDialog.setTitle("Saving data");
stopDialog.setProgressNumberFormat(null);
stopDialog.setCancelable(false);
stopDialog.setMax(100);
stopDialog.show();
Log.d(TAG, "dialog up");
//Stop the service
mainActivity.stopService(new Intent(mainActivity, SensorService.class));
我的服务 onDestroy
看起来像这样:
public void onDestroy() {
//Prevent new tasks from being added to thread
executor.shutdown();
Log.d(TAG, "Executor shutdown is called");
try {
//Wait for all tasks to finish before we proceed
while (!executor.awaitTermination(1, TimeUnit.SECONDS)) {
Log.i(TAG, "Waiting for current tasks to finish");
}
} catch (InterruptedException e) {
executor.shutdownNow();
Thread.currentThread().interrupt();
}
if (executor.isTerminated()){
//Stop everything else once the task queue is clear
unregisterReceiver(receiver);
unregisterListener();
wakeLock.release();
dbHelper.close();
stopForeground(true);
//Dismiss progress dialog - commented out for debugging
//sendMessage("HIDE");
}
}
预期
单击“停止”按钮时,进度对话框应显示,onDestroy
在服务中被调用,执行程序队列完成现有任务,然后通过向处理程序发送消息关闭对话框(这是最后一个部分已被注释掉,所以我可以弄清楚发生了什么)
现实
单击“停止”后,在 logcat 中我可以看到消息 dialog up
,然后是 Executor shutdown is called
。当它完成当前任务队列时,我可以看到 Waiting for current tasks to finish
。所以事件的顺序是正确的,但是进度对话框直到所有 onDestroy
代码之后才真正显示,即使(在片段中)我告诉它在服务被告知停止之前显示。
在我点击停止按钮后,我也看到了很多跳帧,大概是在执行程序试图清除它的队列时:Skipped 336 frames! The application may be doing too much work on its main thread.
虽然我不确定为什么执行程序在后台线程中工作导致 UI 无法更新
这是怎么回事? UI阻塞?
在我看来,对话框应该显示,然后服务被告知停止,它将在执行程序任务队列的其余部分工作,然后被解雇。但由于某种原因,对话显示直到执行程序完全终止后才会发生。
我的代码中有什么东西阻止 UI 线程显示进度对话框吗?我本以为,因为执行程序是 运行 在后台线程中,所以 UI 线程可以自由地进行 show/display 对话之类的事情
注意:我知道我可以使用 AsyncTask
轻松解决此问题,并且有一个类似的 problem/solution here,但我想为此使用执行程序。大多数问题都涉及活动和异步任务,但我的问题使用了片段、服务和执行程序的奇怪组合,我找不到任何 questions/answers
编辑:
经过一番谷歌搜索后,我发现了这个问题 here,这表明 awaitTermination
可能阻塞了 UI 线程。所以我把大部分等待执行程序队列清除的 onDestroy
代码移到了一个新线程中:
public void onDestroy() {
//Prevent new tasks from being added to thread
executor.shutdown();
Log.d(TAG, "Executor shutdown is called");
new Thread(new Runnable() {
public void run() {
try {
//Wait for all tasks to finish before we proceed
while (!executor.awaitTermination(1, TimeUnit.SECONDS)) {
Log.i(TAG, "Waiting for current tasks to finish");
}
} catch (InterruptedException e) {
executor.shutdownNow();
Thread.currentThread().interrupt();
}
if (executor.isTerminated()) {
//Stop everything else once the task queue is clear
unregisterReceiver(receiver);
unregisterListener();
wakeLock.release();
dbHelper.close();
stopForeground(true);
//Dismiss progress dialog
sendMessage("HIDE");
}
}
}).start();
}
现在当我点击停止时,进度对话框似乎会立即显示,但我每次都会遇到这个异常:
异常调度输入事件。
应用程序中的 JNI 检测到错误:JNI CallObjectMethod 调用时出现未决异常 java.util.concurrent.RejectedExecutionException:任务 com.example.app.SensorService$InsertHandler@35e04d3 被 java.util.concurrent.ThreadPoolExecutor@2b28810[正在关闭,池大小 = 1,活动线程 = 1 , 排队任务 = 481, 完成任务 = 2069]
(这只是异常的第一部分 - 它很长)
我不确定发生了什么。当新线程启动时,执行线程 运行 execute
完成其当前队列中的任务。这会导致 2 个线程如何同时工作吗?
您已正确确定 Dialog
未显示的原因是因为您的 UI 线程在 awaitTermination
.
中被阻止
RejectedExecutionException
的原因是您在ThreadPoolExecutor
已经处于关闭状态后提交任务执行
请注意,您一调用executor.shutdown()
,执行程序就进入关闭状态。但是,在执行程序耗尽所有未决任务之前,您实际上并没有使用 SensorManager
注销您的侦听器。在此期间,您可能会收到 onSensorChanged
回调并因此调用 executor.execute(...)
,每个回调都会导致异常。
简单的解决方法是在调用 execute(...)
之前检查是否 executor.isShutdown()
,但更合适的做法是在关闭执行程序之前从 SensorManager
注销您的侦听器:
public class SensorService extends Service implements SensorEventListener {
// ....
public void onDestroy() {
// Prevent any new tasks from being created.
unregisterListener();
// Shutdown
executor.shutdown();
// ...
}
}
这是我在这里的问题的扩展,您可以在其中看到包含按钮的片段和正在使用的服务的所有代码:
我有一个按钮,点击它会启动一个服务,收集一些传感器数据,插入数据库。再次单击时,我想显示进度对话框,等待现有后台任务完成,然后关闭进度对话框。
我现在可以使用片段中的 Handler
在片段和服务之间正确地来回发送消息(如我的其他问题所示)。问题是进度对话框不会显示在 UI 线程中,直到执行程序任务队列被清除
在我的片段中,按钮 onClick
看起来像这样:
//Show stop dialog
stopDialog = new ProgressDialog(mainActivity);
stopDialog.setMessage("Stopping...");
stopDialog.setTitle("Saving data");
stopDialog.setProgressNumberFormat(null);
stopDialog.setCancelable(false);
stopDialog.setMax(100);
stopDialog.show();
Log.d(TAG, "dialog up");
//Stop the service
mainActivity.stopService(new Intent(mainActivity, SensorService.class));
我的服务 onDestroy
看起来像这样:
public void onDestroy() {
//Prevent new tasks from being added to thread
executor.shutdown();
Log.d(TAG, "Executor shutdown is called");
try {
//Wait for all tasks to finish before we proceed
while (!executor.awaitTermination(1, TimeUnit.SECONDS)) {
Log.i(TAG, "Waiting for current tasks to finish");
}
} catch (InterruptedException e) {
executor.shutdownNow();
Thread.currentThread().interrupt();
}
if (executor.isTerminated()){
//Stop everything else once the task queue is clear
unregisterReceiver(receiver);
unregisterListener();
wakeLock.release();
dbHelper.close();
stopForeground(true);
//Dismiss progress dialog - commented out for debugging
//sendMessage("HIDE");
}
}
预期
单击“停止”按钮时,进度对话框应显示,onDestroy
在服务中被调用,执行程序队列完成现有任务,然后通过向处理程序发送消息关闭对话框(这是最后一个部分已被注释掉,所以我可以弄清楚发生了什么)
现实
单击“停止”后,在 logcat 中我可以看到消息 dialog up
,然后是 Executor shutdown is called
。当它完成当前任务队列时,我可以看到 Waiting for current tasks to finish
。所以事件的顺序是正确的,但是进度对话框直到所有 onDestroy
代码之后才真正显示,即使(在片段中)我告诉它在服务被告知停止之前显示。
在我点击停止按钮后,我也看到了很多跳帧,大概是在执行程序试图清除它的队列时:Skipped 336 frames! The application may be doing too much work on its main thread.
虽然我不确定为什么执行程序在后台线程中工作导致 UI 无法更新
这是怎么回事? UI阻塞?
在我看来,对话框应该显示,然后服务被告知停止,它将在执行程序任务队列的其余部分工作,然后被解雇。但由于某种原因,对话显示直到执行程序完全终止后才会发生。
我的代码中有什么东西阻止 UI 线程显示进度对话框吗?我本以为,因为执行程序是 运行 在后台线程中,所以 UI 线程可以自由地进行 show/display 对话之类的事情
注意:我知道我可以使用 AsyncTask
轻松解决此问题,并且有一个类似的 problem/solution here,但我想为此使用执行程序。大多数问题都涉及活动和异步任务,但我的问题使用了片段、服务和执行程序的奇怪组合,我找不到任何 questions/answers
编辑:
经过一番谷歌搜索后,我发现了这个问题 here,这表明 awaitTermination
可能阻塞了 UI 线程。所以我把大部分等待执行程序队列清除的 onDestroy
代码移到了一个新线程中:
public void onDestroy() {
//Prevent new tasks from being added to thread
executor.shutdown();
Log.d(TAG, "Executor shutdown is called");
new Thread(new Runnable() {
public void run() {
try {
//Wait for all tasks to finish before we proceed
while (!executor.awaitTermination(1, TimeUnit.SECONDS)) {
Log.i(TAG, "Waiting for current tasks to finish");
}
} catch (InterruptedException e) {
executor.shutdownNow();
Thread.currentThread().interrupt();
}
if (executor.isTerminated()) {
//Stop everything else once the task queue is clear
unregisterReceiver(receiver);
unregisterListener();
wakeLock.release();
dbHelper.close();
stopForeground(true);
//Dismiss progress dialog
sendMessage("HIDE");
}
}
}).start();
}
现在当我点击停止时,进度对话框似乎会立即显示,但我每次都会遇到这个异常:
异常调度输入事件。 应用程序中的 JNI 检测到错误:JNI CallObjectMethod 调用时出现未决异常 java.util.concurrent.RejectedExecutionException:任务 com.example.app.SensorService$InsertHandler@35e04d3 被 java.util.concurrent.ThreadPoolExecutor@2b28810[正在关闭,池大小 = 1,活动线程 = 1 , 排队任务 = 481, 完成任务 = 2069]
(这只是异常的第一部分 - 它很长)
我不确定发生了什么。当新线程启动时,执行线程 运行 execute
完成其当前队列中的任务。这会导致 2 个线程如何同时工作吗?
您已正确确定 Dialog
未显示的原因是因为您的 UI 线程在 awaitTermination
.
RejectedExecutionException
的原因是您在ThreadPoolExecutor
已经处于关闭状态后提交任务执行
请注意,您一调用executor.shutdown()
,执行程序就进入关闭状态。但是,在执行程序耗尽所有未决任务之前,您实际上并没有使用 SensorManager
注销您的侦听器。在此期间,您可能会收到 onSensorChanged
回调并因此调用 executor.execute(...)
,每个回调都会导致异常。
简单的解决方法是在调用 execute(...)
之前检查是否 executor.isShutdown()
,但更合适的做法是在关闭执行程序之前从 SensorManager
注销您的侦听器:
public class SensorService extends Service implements SensorEventListener {
// ....
public void onDestroy() {
// Prevent any new tasks from being created.
unregisterListener();
// Shutdown
executor.shutdown();
// ...
}
}