Android Support DialogFragment crashes on screen rotation

社会主义新天地 提交于 2019-12-10 15:58:16

问题


I am trying to switch my app to use dialog fragments but I get an application crash when rotating the screen while the dialog is visible. I can reproduce this in a very simple app described below. Create a new project in Android studio and add a DialogFragment like so:

public class MainActivity extends ActionBarActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        if (savedInstanceState == null) {
            getSupportFragmentManager().beginTransaction()
                    .add(R.id.container, new PlaceholderFragment())
                    .commit();
        }
        if (savedInstanceState == null) {
            (new Handler()).postDelayed(new Runnable() {
                @Override
                public void run() {
                    AlertDialog dialog = new AlertDialog.Builder(MainActivity.this)
                            .setMessage("Alert")
                            .setTitle("My Alert")
                            .create();
                    MyDialogFragment dialogFragment = new MyDialogFragment();
                    dialogFragment.setDialog(dialog);
                    dialogFragment.show(getSupportFragmentManager(), "dialog");

                }
            }, 1000);
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();
        if (id == R.id.action_settings) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    /**
     * A placeholder fragment containing a simple view.
     */
    public static class PlaceholderFragment extends Fragment {

        public PlaceholderFragment() {
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                 Bundle savedInstanceState) {
            View rootView = inflater.inflate(R.layout.fragment_main, container, false);
            return rootView;
        }

        @Override
        public void onActivityCreated(Bundle savedState) {
            super.onActivityCreated(savedState);
        }
    }

    public static class MyDialogFragment extends DialogFragment {
        private Dialog mDialog;

        public MyDialogFragment() {
            super();
            mDialog = null;
        }

        @Override
        public void onCreate(Bundle state) {
            super.onCreate(state);
        }

        // Set the dialog to display
        public void setDialog(Dialog dialog) {
            mDialog = dialog;
        }

        // Return a Dialog to the DialogFragment.
        @Override
        public Dialog onCreateDialog(Bundle savedInstanceState) {
            return mDialog;
        }
    }
}

Now run the app, and after the dialog shows up (1 second after loading), rotate the screen. Note that I only create the dialog on the initial onCreate above.

Here is the exception Im getting:

01-30 11:19:40.199  31986-31986/? E/AndroidRuntime﹕ FATAL EXCEPTION: main
    Process: com.example.testdialogs, PID: 31986
    java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.testdialogs/com.example.testdialogs.MainActivity}: java.lang.NullPointerException
            at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2215)
            at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2265)
            at android.app.ActivityThread.handleRelaunchActivity(ActivityThread.java:3758)
            at android.app.ActivityThread.access$900(ActivityThread.java:145)
            at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1212)
            at android.os.Handler.dispatchMessage(Handler.java:102)
            at android.os.Looper.loop(Looper.java:136)
            at android.app.ActivityThread.main(ActivityThread.java:5081)
            at java.lang.reflect.Method.invokeNative(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:515)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:781)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)
            at dalvik.system.NativeStart.main(Native Method)
     Caused by: java.lang.NullPointerException
            at android.support.v4.app.DialogFragment.onActivityCreated(DialogFragment.java:368)
            at android.support.v4.app.Fragment.performActivityCreated(Fragment.java:1508)
            at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:947)
            at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1104)
            at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1086)
            at android.support.v4.app.FragmentManagerImpl.dispatchActivityCreated(FragmentManager.java:1884)
            at android.support.v4.app.FragmentActivity.onStart(FragmentActivity.java:566)
            at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1171)
            at android.app.Activity.performStart(Activity.java:5241)
            at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2178)
            at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2265)
            at android.app.ActivityThread.handleRelaunchActivity(ActivityThread.java:3758)
            at android.app.ActivityThread.access$900(ActivityThread.java:145)
            at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1212)
            at android.os.Handler.dispatchMessage(Handler.java:102)
            at android.os.Looper.loop(Looper.java:136)
            at android.app.ActivityThread.main(ActivityThread.java:5081)
            at java.lang.reflect.Method.invokeNative(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:515)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:781)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:597)
            at dalvik.system.NativeStart.main(Native Method)
            
            
            
            

My gradle script looks like this:

apply plugin: 'android'

android {
    compileSdkVersion 19
    buildToolsVersion "19.0.1"

    defaultConfig {
        minSdkVersion 9
        targetSdkVersion 19
        versionCode 1
        versionName "1.0"
    }

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_7
        targetCompatibility JavaVersion.VERSION_1_7
    }
    buildTypes {
        release {
            runProguard false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
        }
    }
}

dependencies {
    compile 'com.android.support:appcompat-v7:+'
}

which is just the standard build script when you create a new project and uses the latest android support jar.

I can do setRetainInstance in the DialogFragment's onCreate method and then it doesn't crash, but the dialog gets dismissed on rotation. This is better than a crash obviously but not what I'm looking for.

I'm not really sure what the preferred way of using DialogFragments is but I got this idea from some sample code straight from google (for the google service sdk). I figured they knew what they were doing so I'd use the same concept.


回答1:


I hate to answer my own questions but I figured out the reason for the crash. I need to actually do the dialog creation in onCreateDialog of the DialogFragment rather than setting it from my Activity. Not exactly sure why this is the case. Perhaps on rotation the android system erases references tied to the old instance of the Activity. This isn't totally ideal but I can work with it by perhaps passing in the data used to create the dialog in

Here is the update code which works on rotation without crashing:

public class MainActivity extends ActionBarActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        if (savedInstanceState == null) {
            getSupportFragmentManager().beginTransaction()
                    .add(R.id.container, new PlaceholderFragment())
                    .commit();
        }
        if (savedInstanceState == null) {
            (new Handler()).postDelayed(new Runnable() {
                @Override
                public void run() {
                    MyDialogFragment dialogFragment = new MyDialogFragment();
                    dialogFragment.show(getSupportFragmentManager(), "dialog");

                }
            }, 1000);
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();
        if (id == R.id.action_settings) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }

    /**
     * A placeholder fragment containing a simple view.
     */
    public static class PlaceholderFragment extends Fragment {

        public PlaceholderFragment() {
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                 Bundle savedInstanceState) {
            View rootView = inflater.inflate(R.layout.fragment_main, container, false);
            return rootView;
        }

        @Override
        public void onActivityCreated(Bundle savedState) {
            super.onActivityCreated(savedState);
        }
    }

    public static class MyDialogFragment extends DialogFragment {
        public MyDialogFragment() {
            super();
        }

        @Override
        public void onCreate(Bundle state) {
            super.onCreate(state);
        }


        // Return a Dialog to the DialogFragment.
        @Override
        public Dialog onCreateDialog(Bundle savedInstanceState) {
            return AlertDialog dialog = new AlertDialog.Builder(MainActivity.this)
                        .setMessage("Alert")
                        .setTitle("My Alert")
                        .create();
        }
    }  
}



回答2:


I solved such a problem via subclassing DialogFragment class and overriding next two lifecycle callback methods:

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setRetainInstance(true);
}

@Override
public void onDestroyView() {
    if (getDialog() != null && getRetainInstance()) {
        getDialog().setDismissMessage(null);
    }
    super.onDestroyView();
}



回答3:


When the activity is first created, the savedInstanceState is null because it had no previous state to be saved. When the screen is rotated the activity is destroyed and the onCreate(savedInstanceState) is called again with some states saved (like your DialogFragment state, which is visible).

So, firstly your savedInstaceState is null, when the screen rotates, instances are saved and then restored on the onCreate(savedInstanceState) so it is not null anymore and anything inside if(savedInstanceState == null) will be called, and then you get the NullPointerException.

To fix the problem, remove this validation:

if(savedInstanceState == null){

    ///blablalba

}


来源:https://stackoverflow.com/questions/21465932/android-support-dialogfragment-crashes-on-screen-rotation

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!