Поддержка Android DialogFragment падает при повороте экрана

Я пытаюсь переключить свое приложение на использование фрагментов диалогового окна, но при сбое экрана при появлении диалогового окна появляется крах приложения. Я могу воспроизвести это в очень простом приложении, описанном ниже. Создайте новый проект в студии Android и добавьте DialogFragment следующим образом:

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. ***************().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; } }
}

Теперь запустите приложение, и после появления диалогового окна (через 1 секунду после загрузки) поверните экран. Обратите внимание, что я только создаю диалог на начальном onCreate выше.

Вот исключение Im получение:

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)
            
            
            
            

Мой gradle script выглядит следующим образом:

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:+'
}

который является стандартной конструкцией script при создании нового проекта и использует последнюю банку поддержки Android.

Я могу сделать setRetainInstance в методе DialogFragment onCreate, а затем он не сбой, но диалог отбрасывается при вращении. Это лучше, чем крушение, но не то, что я ищу.

Я не совсем уверен, какой предпочтительный способ использования DialogFragments, но я получил эту идею из некоторого примера кода прямо из google (для службы google service sdk). Я подумал, что они знают, что они делают, поэтому я бы использовал ту же концепцию.

3 ответа

Я ненавижу отвечать на свои вопросы, но я выяснил причину крушения. Мне нужно на самом деле создать диалог в onCreateDialog диалога DialogFragment, а не устанавливать его из моей Activity. Не совсем понятно, почему это так. Возможно, при ротации система андроида стирает ссылки, привязанные к старому экземпляру Activity. Это не совсем идеально, но я могу работать с ним, возможно, передавая данные, используемые для создания диалога в

Вот код обновления, который работает при вращении без сбоев:

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. ***************().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(); } }
}


Я решил эту проблему с помощью подкласса класса DialogFragment и переопределения следующих двух методов обратного вызова жизненного цикла:

@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();
}


Когда действие сначала создается, savedInstanceState имеет значение null, поскольку он не сохранил предыдущее состояние. Когда экран вращается, активность уничтожается, а onCreate(savedInstanceState) вызывается снова с сохраненными состояниями (например, ваше состояние DialogFragment, которое видно).

Итак, во-первых, ваш savedInstaceState равен нулю, когда экран вращается, экземпляры сохраняются и затем восстанавливаются на onCreate(savedInstanceState), поэтому он больше не равен нулю, и что-либо внутри if(savedInstanceState == null) будет вызвано, а затем вы получите NullPointerException.

Чтобы устранить проблему, удалите эту проверку:

if(savedInstanceState == null){ ///blablalba
}

licensed under cc by-sa 3.0 with attribution.