Search code examples
androidandroid-dialogfragmentandroid-fragmentactivity

DialogFragment becomes null after runtime configuration changes


I'm learning to use Fragment and DialogFragment. As a training I'm trying to extend the AlertDialog example given in the DialogFragment page. Apparently everything is fine: when I show the options menu and click its button a dialog appears and it behaves as expected. However if the configuration changes (for instance when I rotate the screen) while the dialog is displayed then the dialog is recreated properly but clicking any of its buttons crashes the application. I've tried lots of things (for instance, static newInstance, setRetainInstance, onSaveInstanceState methods and other documented suggestions) for fixing the problem but nothing seems to work so I'm at the starting point. I'm using support library v4.

This is my main activity code:

public class MainActivity extends FragmentActivity {
    private MyDialogFragment dlgf;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.activity_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        showMyDialog();
        return true;
    }

    private void showMyDialog(){
        dlgf = MyDialogFragment.newInstance();
        dlgf.show(getSupportFragmentManager(), "dialog");
    }

    public void doPositive() {
        // do stuff...
        dlgf.getDialog().dismiss();
    }

    public void doNegative() {
        // Cancel button pressed
        dlgf.getDialog().dismiss();
    }
}

and this is my DialogFragment code:

public class MyDialogFragment extends DialogFragment {
    private View view;

    public static MyDialogFragment newInstance() {
        MyDialogFragment dlgf = new MyDialogFragment();
        return dlgf;
    }

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        view = getActivity().getLayoutInflater().inflate(R.layout.alertdialog, null);

        return new AlertDialog.Builder(getActivity())
        .setView(view)
        .setTitle("A Title")
        .setPositiveButton("OK",
            new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int buttonID) {
                    ((MainActivity)getActivity()).doPositive();
                }
            })
        .setNegativeButton("Cancel",
            new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int buttonID) {
                    ((MainActivity)getActivity()).doNegative();
                }
            })
        .create();
    }

}

When the execution crashes after clicking the Cancel button the logcat shows a NullPointerException in the doNegative method. If the crash occurs after clicking the OK button then the NullPointerException occurs in the doPositive method. It seems clear that the DialogFragment object is null.

Could someone help me, please? TIA


Solution

  • The Activity instance that originally set dlgf in showMyDialog() is destroyed upon configuration change and a new one is created. That new one does not have dlgf set so you get the NPE.

    getActivity() inside a Fragment will return you the current activity instance the Fragment is attached to. In your case it will correctly call the method in the fresh instance.

    To solve your problem you could simply pass the Fragment that caused the callback to your Activity so you don't have to store a reference to it.

    // in activity
    public void doPositive(MyDialogFragment source) {
        // do stuff...
        source.getDialog().dismiss();
    }
    
    // in dialog fragment  
    .setPositiveButton("OK",
        new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int buttonID) {
                ((MainActivity)getActivity()).doPositive(MyDialogFragment.this);
            }
        })
    

    Another possibility is to let your Activity retain the reference to its MyDialogFragment using onRetainNonConfigurationInstance() / getLastNonConfigurationInstance().