后台任务运行时不显示 Android 对话框



这是我在这里的问题的扩展,您可以在其中看到包含按钮的片段和正在使用的服务的所有代码: Android 在片段和服务之间发送消息

我有一个按钮,单击该按钮将启动服务,收集一些传感器数据,插入数据库。再次单击时,我想显示进度对话框,等待现有后台任务完成,然后关闭进度对话框。

我现在可以使用片段中的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 阻止?

在我看来,对话框应该显示,然后告诉 THEN 服务停止,它将通过执行程序任务队列的其余部分工作,然后被关闭。但由于某种原因,对话显示直到执行程序完全终止后才会发生。

我的代码中是否有某些内容阻止 UI 线程显示进度对话框?我本以为因为执行器在后台线程中运行,所以 UI 线程可以自由显示/显示对话框等内容

注意:我知道我可以使用AsyncTask轻松解决此问题,并且这里有类似的问题/解决方案,但我想为此使用执行器。大多数问题都涉及活动和异步任务,但我的问题使用了片段、服务和执行器的奇怪组合,我找不到任何问题/答案

编辑:

经过一番谷歌搜索,我在这里找到了这个问题,这表明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: Task 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();
        // ...
    }
}

相关内容

  • 没有找到相关文章

最新更新