Android dialog的最佳实践-如何避免应用程序崩溃
我在应用程序中使用AlertDialog,当我需要执行某些网络操作时,该对话框将向用户显示一些文本,并阻止用户与应用程序交互,直到网络操作完成。 当网络操作完成后,我将调用Disclease以关闭对话框,然后用户可以再次与应用程序交互。 我的代码如下所示: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
//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);