这是我的问题的扩展,您可以在其中查看包含按钮的片段和正在使用的服务的所有代码:Android sending messages between fragment and service
我有一个按钮,单击该按钮将启动一个服务,收集一些传感器数据,插入数据库。再次单击时,我想显示一个进度对话框,等待现有后台任务完成,然后关闭进度对话框。
我现在可以使用片段中的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线程可以自由显示/显示对话框等内容
注意:我知道我可以使用AsyncTask
轻松解决此问题,并且它有类似的问题/解决方案here,但我想为此使用执行程序。大多数问题涉及活动和asynctasks,但我的问题使用片段,服务和执行器的奇怪组合,我找不到任何问题/答案
修改
经过一些谷歌搜索后,我发现了这个问题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个线程同时工作的问题?
答案 0 :(得分:2)
您已正确识别出Dialog
未显示的原因是因为您的UI线程在awaitTermination
中被屏蔽了。
RejectedExecutionException
的原因是您在ThreadPoolExecutor
已经处于关闭状态后提交执行任务。
请注意,只要您致电executor.shutdown()
,执行人就会进入关闭状态。但是,您实际上不会使用SensorManager
取消注册您的侦听器,直到执行程序耗尽所有挂起的任务。在此期间,您可能会收到onSensorChanged
个回调,从而调用executor.execute(...)
,每个回调都会导致异常。
简单的解决方法是在调用executor.isShutdown()
之前检查是否execute(...)
,但在关闭执行程序之前从SensorManager
取消注册侦听器更合适:
public class SensorService extends Service implements SensorEventListener {
// ....
public void onDestroy() {
// Prevent any new tasks from being created.
unregisterListener();
// Shutdown
executor.shutdown();
// ...
}
}