Android dialog的最佳实践-如何避免应用程序崩溃

Android dialog的最佳实践-如何避免应用程序崩溃,android,android-alertdialog,progressdialog,android-progressbar,crashlytics-android,Android,Android Alertdialog,Progressdialog,Android Progressbar,Crashlytics Android,我在应用程序中使用AlertDialog,当我需要执行某些网络操作时,该对话框将向用户显示一些文本,并阻止用户与应用程序交互,直到网络操作完成。 当网络操作完成后,我将调用Disclease以关闭对话框,然后用户可以再次与应用程序交互。 我的代码如下所示: //pseudo-code runs in UI/main thread private void fun() { //business code here... mainExecutor.execute(new R

我在应用程序中使用AlertDialog,当我需要执行某些网络操作时,该对话框将向用户显示一些文本,并阻止用户与应用程序交互,直到网络操作完成。 当网络操作完成后,我将调用Disclease以关闭对话框,然后用户可以再次与应用程序交互。 我的代码如下所示:

//pseudo-code runs in UI/main thread
private void fun() {
    //business code here...
    
    mainExecutor.execute(new Runnable() {
        @Override
        public void run() {
            int timeLeftSec = 10;
            while (true)
            {
                final int timeLeft = timeLeftSec--;
                final String msg = "Time left ";
                mainHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        ShowDialog(msg + " " + timeLeft + " s");
                    }
                });
                
                if (CheckIfNetworkOpDone() || timeLeft < 0) {
                    mainHandler.post(new Runnable() {
                        @Override
                        public void run() {
                            DismissProcessDialog();
                        }
                    });
                    break;
                } else {
                    try {
                        Thread.sleep(1000);
                    }catch (Exception e)
                    {
                        //
                    }
                }
            }
        }
    }
}

private void ShowDialog(String info)
{
    if (mainDialog == null || mainDialogTextView == null)
    {
        View dialogView = LayoutInflater.from(this).inflate(R.layout.processbar_dialog, null);

        if (mainDialog == null)
        {
            mainDialog = new AlertDialog.Builder(this).create();
            mainDialog.setView(dialogView);
            mainDialog.setCancelable(false);
            mainDialog.setCanceledOnTouchOutside(false);
        }
        if (mainDialogTextView == null)
        {
            mainDialogTextView = dialogView.findViewById(R.id.dialogTextView);
        }
    }

    mainDialogTextView.setText(info);
    mainDialog.show();
}

private void DismissProcessDialog()
{
    if (mainDialog != null)
    {
        try {
            Activity activity = (Activity) mainActivityContext;

            if (activity == null || activity.isDestroyed() || activity.isFinishing())
            {
                return;
            }

            Context context = ((ContextWrapper)(mainDialog.getContext())).getBaseContext();
            if (!(context instanceof Activity ))
            {
                return;
            }
            activity = (Activity) context;
            if (activity == null || activity.isDestroyed() || activity.isFinishing())
            {
                return;
            }

            if (mainDialog != null && mainDialog.isShowing())
            {
                mainDialog.dismiss();
            }
        }
        catch (Exception e)
        {
            //
        }
    }
}

    @Override
    protected void onDestroy() {
        if (mainDialog != null && mainDialog.isShowing())
        {
            mainDialog.dismiss();
        }
        mainDialog = null;

        super.onDestroy();
    }

ProgressDialog已折旧,我改用AlertDialog。我不使用ProgressBar的原因是,我想在网络操作完成之前阻止用户。我发现当ProgressBar显示时,用户仍然可以按按钮

关于如何修复此类崩溃或如何处理此场景以正确显示对话框,是否有最佳实践


如有任何建议,将不胜感激。谢谢

在我看来,似乎有两个问题,您正在从后台线程调用Disclose dialog。您应该始终从UI线程进行UI调用(
runOnUi{…}
将对此有所帮助)。其次,您正在使用
while(true)
运行一个循环。这是非常糟糕的做法,您应该在这里传递一个可以在循环内部结束的条件。这将无限期运行,因此当前第一次通过将进行网络调用。如果它立即结束,第二遍尝试取消对话。然后,第三个过程满足相同的条件现在您在崩溃日志中找到了dviewlock,因为它试图从两个后台线程访问同一对象。

在这里使用progressbar而不是alertdialog更合理。如果要防止用户在加载应用程序时与应用程序交互,可以使屏幕上除progressbar之外的所有组件不可见,也可以使它们可见,但更改其聚焦性或可点击性。

当用户离开(或旋转)时,可能会发生异常情况您的应用程序在网络运行期间以及您的
活动
(或
片段
)被销毁(或重新创建)。然后不再有对话框可关闭,并引发异常,请参阅

虽然上面的答案解决了您的问题,但它犯了在后台任务中保留对
活动的引用的错误。我过去所做的是让我的
活动
实现一个回调接口,并将此回调的
WeakReference
传递给后台任务。然后后台任务必须检查回调(
活动
)是否仍然存在

上述解决方案仍然存在一个问题,即您必须为在轮换时创建的每个新的
活动
(或任何其他配置更改)重新启动任务。现代方法是让
活动
(或
片段
)在
视图模型
中观察
LiveData
。看


此外,建议使用
对话框片段
,因为该片段在重新创建时会被还原。当您旋转屏幕(或在应用程序被销毁后切换到应用程序)时,正常的
警报对话框
会消失。

您是否考虑过管理用户与应用程序的交互

有时我使用在我的utils类中定义的这两个方法

公共类Utils{
公共静态void disableUserInteraction(活动){
activity.getWindow().setFlags(WindowManager.LayoutParams.FLAG)不可触摸,
WindowManager.LayoutParams.FLAG(不可触摸);
}
公共静态void enableUserInteraction(活动){
activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG\u不可触摸);
}
}
我这样称呼他们:

 Utils.disableUserInteraction(activity);

上述方法设置了适当的标志,以便在需要时忽略用户交互。

查看此项。您需要检查活动状态和对话框状态以避免此类错误。@Monster Brain感谢您的建议,但您可能无法阅读我的代码,因为我已经检查了代码中的活动和对话框状态。但是它仍然崩溃。您尝试过dismissAllowingStateLoss()了吗?DismissProcessDialog或onDestroy中是否发生错误?我认为它应该发生在DismissProcessDialog中,但我无法从崩溃日志中获取它,因为我没有显示任何代码。我最近刚刚添加了onDestroy,之前有一些崩溃日志。所以我想它应该来自DismissProcessDialog.1)handler.post将确保它在UI线程上运行,否则,它将立即崩溃。。。2) 有一个打破循环的条件,您可能在if条件检查中看不到。我同意禁用其他组件的clickable,但我没有找到一个好方法,因为需要处理很多组件…在XML中,放置两个布局,其中一个只有一个progressbar,另一个是您的主布局。当数据加载布局时,显示progressbar,并且主布局可点击性设置为false@托马斯
 Utils.disableUserInteraction(activity);