[android] Dialogs / AlertDialogs: How to "block execution" while dialog is up (.NET-style)

Coming from the .NET-environment, I'm now looking to understand how Dialogs work in Android.

In .NET, when calling MessageBox.Show(...) that creates and shows a popup dialog. In the call to Show I can specify what buttons should be available in the popup, for example:

DialogResult myDialogResult = MessageBox.Show("My text here", "My caption here", MessageBoxButtons.YesNoCancel);

As you can see, the call to Show returns a DialogResult when a button is pressed in the popup, informing me what button was clicked. Note that in .NET, execution is halted at the line where the call to Show(...) is made, so it can return the value when a button is pressed.

If I in the above example press "No" the myDialogResult will be equal to

myDialogResult == DialogResult.No

Since I find the .NET-way of using/creating popups very easy and intuitive, I would like that way of creating popups in Android too.

So, the question is if anyone know how to "halt execution" like with the MessageBox.Show, and then return a value whenever the Button is pressed (and the dialog goes away)?

Edit 1

To be a little bit more clear:

I need for the execution to halt and wait until the user has chosen a button to click in the popup. The code that follow the call to show the Dialog is dependent on what button is clicked in the Dialog.

That's why I cannot use what Erich and Alex suggest, since writing code in the onClick-methods as suggested below is not going to work. The reason is that I cannot continue the "normal execution". Let me take an example:

Let me take an example:

int nextStep = 0; // this variable will not be reached from within the onClick-methods

AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage("Hello!")
       .setPositiveButton("Ok", new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int id) {
                nextStep = 1; // *** COMPILER ERROR!! ***
            }
        })
        .setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int id) {
                nextStep = 2; // *** COMPILER ERROR!! ***
            }
        })
        .create().show();

if (nextStep == 1)
{
    // then do some damage
}
else if (nextStep == 2
    // dont do damage

If I wanted the execution to be dependent on the choice in the popup, I would somehow have to make all the variables in the "normal execution" (in this case nextStep) available in the onClick-methods, and that sounds like hell to me.

Edit 2

Another obvious example would be a popup asking "Do you want to continue" with the options "Yes" and "No".

If the user presses "Yes", the whole method should be aborted otherwise it should continue execution. How do you solve that nicely?

This question is related to android

The answer is


I am using Xamarin.Android (MonoDroid), and I have requirments for developing UI Blocking confirm box. I am not going to argue with the client because I can see good reasons for why they want that (details here), so I need to implement this. I tried @Daniel and @MindSpiker above, but these did not work on MonoForAndroid, the moment the message is sent between the threads, the app is crashed. I assume it is something to do with Xamarin mapping.

I ended up creating a separate thread from the UI thread and then blocking that and waiting for the user response as follows:

// (since the controllers code is shared cross-platforms)
protected void RunConfirmAction(Action runnableAction)
{
    if (runnableAction != null)
    {
        if (Core.Platform.IsAndroid)
        {
            var confirmThread = new Thread(() => runnableAction());
            confirmThread.Start();
        }
        else
        {
            runnableAction();
        }
    }
}

// The call to the logout method has now changed like this:
RunConfirmAction(Logout);

// the implemtation of the MessageBox waiting is like this:
public DialogResult MessageBoxShow(string message, string caption, MessageBoxButtons buttons, MessageBoxIcon icon, MessageBoxDefaultButton defaultButton)
{
    if (_CurrentContext != null && _CurrentContext.Screen != null && MainForm.MainActivity != null)
    {
        Action<bool> callback = OnConfirmCallBack;
        _IsCurrentlyInConfirmProcess = true;
        Action messageBoxDelegate = () => MessageBox.Show(((Activity)MainForm.MainActivity), callback, message, caption, buttons);
        RunOnMainUiThread(messageBoxDelegate);
        while (_IsCurrentlyInConfirmProcess)
        {
            Thread.Sleep(1000);
        }               
    }
    else
    {
        LogHandler.LogError("Trying to display a Message box with no activity in the CurrentContext. Message was: " + message);
    }
    return _ConfirmBoxResult ? DialogResult.OK : DialogResult.No;

}

private void OnConfirmCallBack(bool confirmResult)
{
    _ConfirmBoxResult = confirmResult;
    _IsCurrentlyInConfirmProcess = false;
}

private bool _ConfirmBoxResult = false;
private bool _IsCurrentlyInConfirmProcess = false;

Full details on how to do this can be found in my blog post here


Try this in a Thread (not the UI-Thread):

final CountDownLatch latch = new CountDownLatch(1);
handler.post(new Runnable() {
  @Override
  public void run() {
    OnClickListener okListener = new OnClickListener() {
    @Override
    public void onClick(DialogInterface dialog, int which) {
      dialog.dismiss();
      latch.countDown();
    }
  };

  AlertDialog dialog = new AlertDialog.Builder(context).setTitle(title)
    .setMessage(msg).setPositiveButton("OK", okListener).create();
  dialog.show();
}
});
try {
  latch.await();
} catch (InterruptedException e) {
  e.printStackTrace();
}

you set onclick listeners to you buttons. dissmis dialog and do your action. Don't need to halt anything

protected Dialog onCreateDialog(int id) {
  return new AlertDialog.Builder(this).setTitle(R.string.no_connection).setIcon(android.R.drawable.ic_dialog_alert).setView(textEntryView).setPositiveButton(R.string.exit, new DialogInterface.OnClickListener() 
{

   public void onClick(DialogInterface dialog, int whichButton) {

  // Here goes what you want to do
  }

})
}

to call - ex - showDialog(DIALOG_ERROR_PREF);

More http://developer.android.com/guide/topics/ui/dialogs.html


The cleanest and simplest solution is to use your own listener interface so that when the user clicks on the ok button your listener is called with the return value. This method does nothing fancy or complicated and respects android principles.

Define your listener interface as follows:

public interface EditListener
/* Used to get an integer return value from a dialog
 * 
 */
{
 void returnValue(int value); 
}

For my application I created an EditValue class which uses AlertDialog and which I call whenever I want to edit an integer value. Note how the EditListener interface is passed as an argument to this code. When the user clicks on the OK button the value will be returned to your calling code via the EditListener method:

public final class EditValue
/* Used to edit a value using an alert dialog
 * The edited value is returned via the returnValue method of the supplied EditListener             interface
 * Could be modified with different constructors to edit double/float etc
 */
{
 public EditValue(final Activity parent, int value, String title, String message,
                  final EditListener editListener)
 {AlertDialog.Builder alert= new AlertDialog.Builder(parent);
  if(title==null) title= message;
  else if(message==null) message= title;
  if(title!=null) alert.setTitle(title);
  if(message!=null) alert.setMessage(message);

  // Set an EditText view to get user input 
  final EditText input = new EditText(parent);
  input.setText(String.valueOf(value));
  input.setInputType(InputType.TYPE_CLASS_NUMBER);
  alert.setView(input);

  alert.setPositiveButton("OK",new DialogInterface.OnClickListener()
  {public void onClick(DialogInterface dialog, int which)
   {try
    {int newValue= Integer.valueOf(input.getText().toString());
     editListener.returnValue(newValue);
     dialog.dismiss();
    }catch(NumberFormatException err) { }
   }
  });

  alert.setNegativeButton("Cancel", new DialogInterface.OnClickListener()
  {public void onClick(DialogInterface dialog, int which)
   {dialog.dismiss();
    }
  });

  alert.show();
 }
}

Finally when you use EditValue you need to declare your EditListener and you can now access the return value and do what you want to do:

 new EditValue(main,AnchorManager.anchorageLimit,
               main.res.getString(R.string.config_anchorage_limit),null,
               new EditListener()
 {public void returnValue(int value) {AnchorManager.anchorageLimit= value;}
  }
 );


In Android, the structure is different from .NET:

AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage("Hello!")
       .setPositiveButton("Ok", new DialogInterface.OnClickListener() {
           public void onClick(DialogInterface dialog, int id) {
               // Handle Ok
           }
       })
       .setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
           public void onClick(DialogInterface dialog, int id) {
               // Handle Cancel
           }
       })
       .create();

Will get you a dialog with two buttons and you handle the button clicks with callbacks. You might be able to write some code to make the syntax more closely resemble .NET, but the dialog lifecycle is pretty intertwined with Activity, so in the end, it might be more trouble than it's worth. Additional dialog references are here.


just to answer your question...btw sry that i'm 9 months late:D...there's a "workaround" 4 this kind of problems. i.e.

new AlertDialog.Builder(some_class.this).setTitle("bla").setMessage("bla bla").show();
wait();

simply add wait();

and them in the OnClickListener start the class again with notify() something like this

@Override
    public void onClick(DialogInterface dialog, int item) {
        Toast.makeText(getApplicationContext(), "test", Toast.LENGTH_LONG).show();
        **notify**();
        dialog.cancel();
    }

the same workaround goes 4 toasts and other async calls in android


This is the simplest way:

new AlertDialog.Builder(this).setTitle("title").setMessage("message").create().show();

PasswordDialog dlg = new PasswordDialog(this);

if(dlg.showDialog() == DialogResult.OK)

{

    //blabla, anything your self

}


public class PasswordDialog extends Dialog
{
    int dialogResult;
    Handler mHandler ;

    public PasswordDialog(Activity context, String mailName, boolean retry)
    {

        super(context);
        setOwnerActivity(context);
        onCreate();
        TextView promptLbl = (TextView) findViewById(R.id.promptLbl);
        promptLbl.setText("Input password/n" + mailName);
    }
    public int getDialogResult()
    {
        return dialogResult;
    }
    public void setDialogResult(int dialogResult)
    {
        this.dialogResult = dialogResult;
    }
    /** Called when the activity is first created. */

    public void onCreate() {
        setContentView(R.layout.password_dialog);
        findViewById(R.id.cancelBtn).setOnClickListener(new android.view.View.OnClickListener() {

            @Override
            public void onClick(View paramView)
            {
                endDialog(DialogResult.CANCEL);
            }
            });
        findViewById(R.id.okBtn).setOnClickListener(new android.view.View.OnClickListener() {

            @Override
            public void onClick(View paramView)
            {
                endDialog(DialogResult.OK);
            }
            });
        }

    public void endDialog(int result)
    {
        dismiss();
        setDialogResult(result);
        Message m = mHandler.obtainMessage();
        mHandler.sendMessage(m);
    }

    public int showDialog()
    {
        mHandler = new Handler() {
            @Override
              public void handleMessage(Message mesg) {
                  // process incoming messages here
                //super.handleMessage(msg);
                throw new RuntimeException();
              }
          };
        super.show();
        try {
            Looper.getMainLooper().loop();
        }
        catch(RuntimeException e2)
        {
        }
        return dialogResult;
    }

}

Developers of Android and iOS decided that they are powerful and smart enough to reject Modal Dialog conception (that was on market for many-many years already and didn't bother anyone before), unfortunately for us. I believe that there is work around for Android - since you can show dialog from non-ui thread using Runnable class, there should be a way to wait in that thread (non-ui) until dialog is finished.

Edit: Here is my solution, it works great:

    int pressedButtonID;
    private final Semaphore dialogSemaphore = new Semaphore(0, true);
    final Runnable mMyDialog = new Runnable()
    {
        public void run()
        {
            AlertDialog errorDialog = new AlertDialog.Builder( [your activity object here] ).create();
            errorDialog.setMessage("My dialog!");
            errorDialog.setButton("My Button1", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    pressedButtonID = MY_BUTTON_ID1;
                    dialogSemaphore.release();
                    }
                });
            errorDialog.setButton2("My Button2", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    pressedButtonID = MY_BUTTON_ID2;
                    dialogSemaphore.release();
                    }
                });
            errorDialog.setCancelable(false);
            errorDialog.show();
        }
    };

    public int ShowMyModalDialog()  //should be called from non-UI thread
    {
        pressedButtonID = MY_BUTTON_INVALID_ID;
        runOnUiThread(mMyDialog);
        try
        {
            dialogSemaphore.acquire();
        }
        catch (InterruptedException e)
        {
        }
        return pressedButtonID;
    }

I am new to the Android/Java world and was surprised to find out here (unless I don't understand what I read) that modal dialogs don't work. For some very obscure reasons for me at the moment, I got this "ShowMessage" equivalent with an ok button that works on my tablet in a very modal manner.

From my TDialogs.java module:

class DialogMes 
{

  AlertDialog alertDialog ;
  private final Message NO_HANDLER = null;
  public DialogMes(Activity parent,String aTitle, String mes)
  {
    alertDialog = new AlertDialog.Builder(parent).create();
    alertDialog.setTitle(aTitle);    
    alertDialog.setMessage(mes) ;  
    alertDialog.setButton("OK",NO_HANDLER) ;    
    alertDialog.show() ; 
  } 
}

Here's part of the test code:

public class TestDialogsActivity extends Activity implements DlgConfirmEvent
{

   @Override
  public void onCreate(Bundle savedInstanceState)
  {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    Button btShowMessage = (Button) findViewById(R.id.btShowMessage);
    btShowMessage.setOnClickListener(new View.OnClickListener() {
      public void onClick(View view) 
      {
        DialogMes dlgMes = new DialogMes( TestDialogsActivity.this,"Message","life is good") ;                
      }  
    }); 

I also implemented a modal Yes/No dialog following the interface approach suggested above by JohnnyBeGood, and it works pretty good too.

Correction:

My answer is not relevant to the question that I misunderstood. For some reason, I interpreted M. Romain Guy "you don't want to do that" as a no no to modal dialogs. I should have read: "you don't want to do that...this way".

I apologize.


In an attempt to optimize memory and performance dialogs in Android are asynchronous (they are also managed for this reason). Comming from the Windows world, you are used to modal dialogs. Android dialogs are modal but more like non-modal when it comes to execution. Execution does not stop after displaying a dialog.

The best description of Dialogs in Android I have seen is in "Pro Android" http://www.apress.com/book/view/1430215968

This is not a perfect explanation but it should help you to wrap your brain around the differences between Dialogs in Windows and Android. In Windows you want to do A, ask a question with a dialog, and then do B or C. In android design A with all the code you need for B and C in the onClick() of the OnClickListener(s) for the dialog. Then do A and launch the dialog. You’re done with A! When the user clicks a button B or C will get executed.

Windows
-------
A code
launch dialog
user picks B or C
B or C code
done!

Android
-------
OnClick for B code (does not get executed yet)
OnClick for C code (does not get executed yet)
A code
launch dialog
done!
user picks B or C

Hu! Damn I was struggling whole day for this. Somehow I got workaround though I would't recommend it. You will have to use Handler to achieve this.
call getDialogBack function to get

boolean result =  getDialogBack(this);
Log.d(TAG, "onCreate: "+result);   

In getDialogBack write dialog and handler code to make it synchronous

public int getDialogBack(Context context,int i) {
        final Handler handler = new Handler(Looper.getMainLooper()) {

            @Override
            public void handleMessage(Message mesg) {
                throw new RuntimeException();
            }
        };

        AlertDialog.Builder alert = new AlertDialog.Builder(context);
        alert.setTitle("Title");
        alert.setMessage("Message");
        alert.setPositiveButton("Want to print next", new
                DialogInterface.OnClickListener() {
                    public void onClick(DialogInterface dialog, int id) {
                        handler.sendMessage(handler.obtainMessage());
                    }
                });
        alert.setNegativeButton("Return False", new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int id) {
                handler.sendMessage(handler.obtainMessage());
            }
        });
        alert.show();

        try {
            Looper.loop();
        } catch (RuntimeException e) {
        }

        return true;
    }

In Android Dialogs are asynchronous so you're going to have to structure your code a little differently.

So in C# your logic ran something like this in pseudocode:

void doSomeStuff() {
    int result = showDialog("Pick Yes or No");

    if (result == YES) {
        //do stuff for yes
    }
    else if (result == NO) {
        //do stuff for no
    }

    //finish off here
}

For Android it's going to have to be less neat. Think of it like so. You'll have an OnClickListener like this:

public void onClick(DialogInterface dialog, int whichButton) {
   if (whichButton == BUTTON_POSITIVE) {
      doOptionYes();
   }
   else if (whichButton == BUTTON_NEGATIVE) {
      doOptionNo();
   }
}

Which is then supported by the following methods:

void doOptionYes() {
    //do stuff for yes
    endThings();
}

void doOptionNo() {
    //do stuff for no
    endThings();
}

void endThings() {
    //clean up here
}

So what was one method is now four. It may not seem as neat but that's how it works I'm afraid.


Rewriting:

There are radical differences between mobile and desktop environment, as well as between the way in which applications were developed to a handful of years ago and today:

a) mobile devices need to conserve energy. Part of the value they offer. So you need to save resources. Threads are an expensive resource. Halt the progress of a thread is an unacceptable waste of this resource.

b) Nowadays the user is much more demanding. To assist them, we believe that it deserves to have full working CPU and the smallest possible expenditure of energy. Its application is not alone on the device, there is an unknow number of another apps running at same time and your app is not necessarily the most urgent.

c) system-level locks are not an option: a mobile device works with a number of events and services in the background and it is not right for any of them can be locked by an application.

Think about the user receiving a phone call while your "system lock" is working...

Based on the above facts, the answer to the proposed question are:

  • There is a viable way to build a dialog that blocks the main thread until a response from the user?

No. Workarounds do user experience get worse and it may make the mistake of blaming the system itself. This is unfair and penalizes the platform and all its developers.

  • Is there a way to block the entire system with a dialogue?

No. This is strictly forbidden on the platform. No application can interfere with the operation of the system or other applications.

  • I need to refactor my application, or rethink my way of programming to suit me on the Android mobile system architecture.

Yes. Including this aspect.


let me as a thank you to the StackOverflow community, share something nice with you, where I have used the above example in Android code for HTTP calls, which now turns MODAL instead of the usual extra thread and the complexities of merging the threads by use of the a bit peculiar threading. (This is working in our app right now - 15 Oct 2020)

public JSONArray genericHttpModal(Context context, final String url, final JSONObject request) {

    this.url = url;    
    genericHttpRequestQueue = Volley.newRequestQueue(context);      
    class MyJsonReturn {
        JSONArray returnJsonArray;

        public void set(JSONArray i) {
            returnJsonArray = i;
        }

        public void set(String i) {
            try {
                returnJsonArray.put(0, i);
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }

        public JSONArray get() {
            return returnJsonArray;
        }
    }

    final MyJsonReturn mymy = new MyJsonReturn();

    // Positive Response / HTTP OK.
    final Handler handler = new Handler() {
        @Override
        public void handleMessage(@NonNull Message msg) {
            throw new RuntimeException();
        }
    };

    final Response.Listener responseListener = new Response.Listener<String>() {
        @Override
        public void onResponse(String response) {
            try {
                mymy.set(new JSONArray(response));
            } catch (JSONException e) {
                mymy.set("[{\"JSONException:\"" + e.getMessage() + "\"}]");
            }
            handler.sendMessage(handler.obtainMessage());
        }
    };

    // Negative Response / HTTP NOT OK
    final Response.ErrorListener errorListener = new Response.ErrorListener() {
        @Override
        public void onErrorResponse(VolleyError error) {
            result = "fail";

            try {
                mymy.set(new JSONArray("[{\"JSONException:\"" + result + "\"}]"));
            } catch (JSONException e) {
                mymy.set("[{\"JSONException:\"" + e.getMessage() + "\"}]");
            }
            handler.sendMessage(handler.obtainMessage());
        }
    };


    final StringRequest httpRequest = new StringRequest(Request.Method.POST, URL_POINTER + url,
            responseListener,
            errorListener) {

        // Here the mRequestQueue handler will get the parameters for this request here.
        // Ref: https://stackoverflow.com/questions/33573803/how-to-send-a-post-request-using-volley-with-string-body#33578202
        // Ref: Multi Threaded solution 14 Oct 2020 (David Svarrer) : https://stackoverflow.com/questions/2028697/dialogs-alertdialogs-how-to-block-execution-while-dialog-is-up-net-style (This stackoverflow here)
        @Override
        protected java.util.Map<String, String> getParams() throws AuthFailureError {
            return jsonObjectToMap(request);
        }
    };
    httpRequest.setShouldCache(false); // Elijah: Could work on dropping the cache !!!
    genericHttpRequestQueue.add(httpRequest);    

    try {
        Looper.loop();
    } catch (RuntimeException re) {
    }
    return mymy.get();
}

UserSelect =null

AlertDialog.Builder builder = new Builder(ImonaAndroidApp.LoginScreen);
            builder.setMessage("you message");
            builder.setPositiveButton("OK", new OnClickListener() {

                @Override
                public void onClick(DialogInterface dialog, int which) {
                    UserSelect = true ;

                }
            });

            builder.setNegativeButton("Cancel", new OnClickListener() {

                @Override
                public void onClick(DialogInterface dialog, int which) {
                    UserSelect = false ;

                }
            });
           // in UI thread
        builder.show();             
        // wait until the user select
            while(UserSelect ==null);

Ted, as you probably found out, you unfortunately can't do that on Android. Dialogs are modal, but asynchronous, and it will definitely disrupt the sequence you're trying to establish as you would have done on .NET (or Windows for that matter). You will have to twist your code around and break some logic that would have been very easy to follow based on your example.

Another very simple example is to save data in a file, only to find out that the file is already there and asking to overwrite it or not. Instead of displaying a dialog and having an if statement to act upon the result (Yes/No), you will have to use callbacks (called listeners in Java) and split your logic into several functions.

On Windows, when a dialog is shown, the message pump continues in the background (only the current message being processed is on hold), and that works just fine. That allows users to move your app and let is repaint while you're displaying a dialog box for example. WinMo supports synchronous modal dialogs, so does BlackBerry but just not Android.


A simplified version of Daniel's answer above. This function gets a yes or no from user in an alert dialog but could easily be modified to get other input.

private boolean mResult;
public boolean getYesNoWithExecutionStop(String title, String message, Context context) {
    // make a handler that throws a runtime exception when a message is received
    final Handler handler = new Handler() {
        @Override
        public void handleMessage(Message mesg) {
            throw new RuntimeException();
        } 
    };

    // make a text input dialog and show it
    AlertDialog.Builder alert = new AlertDialog.Builder(context);
    alert.setTitle(title);
    alert.setMessage(message);
    alert.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
        public void onClick(DialogInterface dialog, int whichButton) {
            mResult = true;
            handler.sendMessage(handler.obtainMessage());
        }
    });
    alert.setNegativeButton("No", new DialogInterface.OnClickListener() {
        public void onClick(DialogInterface dialog, int whichButton) {
            mResult = false;
            handler.sendMessage(handler.obtainMessage());
        }
    });
    alert.show();

    // loop till a runtime exception is triggered.
    try { Looper.loop(); }
    catch(RuntimeException e2) {}

    return mResult;
}