I use an AlertDialog in my app, when I need to do some network operation, The dialog will show up some text to users and block users to interact with the app until the network operation is finished. When the network operation is finished, I will call dismiss to dismiss the dialog and then users can interact with app again. My code is something like the following:
//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();
}
But google play backend shows there is a log of crashes for dismiss. The exception is:
java.lang.IllegalArgumentException:
at android.view.WindowManagerGlobal.findViewLocked (WindowManagerGlobal.java:517)
at android.view.WindowManagerGlobal.removeView (WindowManagerGlobal.java:426)
at android.view.WindowManagerImpl.removeViewImmediate (WindowManagerImpl.java:126)
at android.app.Dialog.dismissDialog (Dialog.java:389)
at android.app.-$$Lambda$oslF4K8Uk6v-6nTRoaEpCmfAptE.run (Unknown Source:2)
at android.os.Handler.handleCallback (Handler.java:883)
at android.os.Handler.dispatchMessage (Handler.java:100)
at android.os.Looper.loop (Looper.java:214)
at android.app.ActivityThread.main (ActivityThread.java:7356)
at java.lang.reflect.Method.invoke (Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run (RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main (ZygoteInit.java:930)
ProgressDialog is depreciated and I use AlertDialog instead. The reason why I don't use ProgressBar is, I want to blcok user until network operation is finished. I find user can still press buttons when ProgressBar is showing.
Is there any best practice on how to fix this kind of crashes or how to deal with this scenario to show dialog correctly?
Any suggestions will be appreciated. Thanks!