Robust way to pass value back from Dialog to Activity on Android?

android, android-alertdialog, dialog

Solution

You are on the right track, I follow the method recommended by the Android Developers - Using DialogFragments article.

You create your DialogFragment and define an interface that the Activity will implement, like you have done above with this:

public interface MyDialogFragmentListener {
    public void onReturnValue(String foo);
}

Then in the DialogFragment when you want to return the result to the Activity you cast the activity to the interface:

@Override
public void onClick(DialogInterface dialog, int id) {
    MyDialogFragmentListener activity = (MyDialogFragmentListener) getActivity();
    activity.onReturnValue("some value");
}

Then in the `Activity` you implement that interface and grab the value:

public class MyActivity implements MyDialogFragmentListener {
    ...
    @Override
    public void onReturnValue(String foo) {
        Log.i("onReturnValue", "Got value " + foo + " back from Dialog!");
    }
}

Problem

This question has come up several times and I've read all the answers, but I haven't seen a truly robust way to handle this. In my solution, I am using listeners from the calling `Activity` to the `AlertDialog` like so: ``` public class MyDialogFragment extends DialogFragment { public interface MyDialogFragmentListener { public void onReturnValue(String foo); } public void init(boolean someValue) { sSomeValue = someValue; listeners = new ArrayList<MyDialogFragmentListener>(); } static boolean sSomeValue; private static ArrayList<MyDialogFragmentListener> listeners; public void addMyDialogFragmentListener(MyDialogFragmentListener l) { listeners.add(l); } public void removeMyDialogFragmentListener(MyDialogFragmentListener l) { listeners.remove(l); } @Override public Dialog onCreateDialog(Bundle savedInstanceState) { AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); builder.setTitle(R.string.title) .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int id) { for (MyDialogFragmentListener listener : listeners) { listener.onReturnValue("some value"); } } }) .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { // User cancelled the dialog // Nothing to do but exit } }); if (sSomeValue) { builder.setMessage(R.string.some_value_message); } else { builder.setMessage(R.string.not_some_value_message); } // Create the AlertDialog object and return it return builder.create(); } } ``` Then in the calling `Activity`, I instantiate the object normally, pass in any arguments through `init` and set my listener. Here's the problem: when you rotate the device and change orientation while the dialog is open, both the `Activity` and `MyDialogFragment` objects get re-created. To ensure that the input values don't get screwed up, I am setting my initialized values as `static`. This feels hacky to me, but since there will only be one such dialog at a time, I am ok with it. Where the problem comes in is with the return value. The original listener will get called. That's fine because the object still exists, but if there is a requirement to update the UI on the `Activity` (which there is), it won't get updated because the new `Activity` instance is now controlling the UI. One solution I am considering is casting `getActivity()` in the dialog class to my `Activity` and forcing the dialog itself to add a listener, rather than having the calling `Activity` do it. But this just feels like a snowballing of hacks. What is the best practice for handling this gracefully?

Original source