Comment conserver correctement un DialogFragment par rotation?

J’ai un FragmentActivity qui héberge un DialogFragment.

Le DialogFragment exécute les requêtes réseau et gère l’authentification Facebook. Je dois donc le conserver pendant la rotation.

J’ai lu toutes les autres questions relatives à ce problème, mais aucune d’entre elles n’a résolu le problème.

J’utilise putFragment et getFragment pour enregistrer l’instance Fragment et la récupérer lors de la recréation d’activité.

Cependant, je reçois toujours une exception de pointeur nul sur l’appel de getFragment dans onRestoreInstanceState. Je voudrais aussi éviter que le dialog soit rejeté pendant la rotation, mais jusqu’à présent, je ne peux même pas en retenir l’instance.

Des idées, qu’est-ce qui ne va pas?

Voici à quoi ressemble actuellement mon code:

public class OKLoginActivity extends FragmentActivity implements OKLoginDialogListener { private OKLoginFragment loginDialog; private static final Ssortingng TAG_LOGINFRAGMENT = "OKLoginFragment"; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); FragmentManager fm = getSupportFragmentManager(); if(savedInstanceState == null) { loginDialog = new OKLoginFragment(); loginDialog.show(fm, TAG_LOGINFRAGMENT); } } @Override public void onSaveInstanceState(Bundle outState) { getSupportFragmentManager().putFragment(outState,TAG_LOGINFRAGMENT, loginDialog); } @Override public void onRestoreInstanceState(Bundle inState) { FragmentManager fm = getSupportFragmentManager(); loginDialog = (OKLoginFragment) fm.getFragment(inState, TAG_LOGINFRAGMENT); } } 

Ceci est la trace de la stack d’exception:

 02-01 16:31:13.684: E/AndroidRuntime(9739): FATAL EXCEPTION: main 02-01 16:31:13.684: E/AndroidRuntime(9739): java.lang.RuntimeException: Unable to start activity ComponentInfo{io.openkit.example.sampleokapp/io.openkit.OKLoginActivity}: java.lang.NullPointerException 02-01 16:31:13.684: E/AndroidRuntime(9739): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2180) 02-01 16:31:13.684: E/AndroidRuntime(9739): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2230) 02-01 16:31:13.684: E/AndroidRuntime(9739): at android.app.ActivityThread.handleRelaunchActivity(ActivityThread.java:3692) 02-01 16:31:13.684: E/AndroidRuntime(9739): at android.app.ActivityThread.access$700(ActivityThread.java:141) 02-01 16:31:13.684: E/AndroidRuntime(9739): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1240) 02-01 16:31:13.684: E/AndroidRuntime(9739): at android.os.Handler.dispatchMessage(Handler.java:99) 02-01 16:31:13.684: E/AndroidRuntime(9739): at android.os.Looper.loop(Looper.java:137) 02-01 16:31:13.684: E/AndroidRuntime(9739): at android.app.ActivityThread.main(ActivityThread.java:5039) 02-01 16:31:13.684: E/AndroidRuntime(9739): at java.lang.reflect.Method.invokeNative(Native Method) 02-01 16:31:13.684: E/AndroidRuntime(9739): at java.lang.reflect.Method.invoke(Method.java:511) 02-01 16:31:13.684: E/AndroidRuntime(9739): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:793) 02-01 16:31:13.684: E/AndroidRuntime(9739): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:560) 02-01 16:31:13.684: E/AndroidRuntime(9739): at dalvik.system.NativeStart.main(Native Method) 02-01 16:31:13.684: E/AndroidRuntime(9739): Caused by: java.lang.NullPointerException 02-01 16:31:13.684: E/AndroidRuntime(9739): at android.support.v4.app.FragmentManagerImpl.getFragment(FragmentManager.java:528) 02-01 16:31:13.684: E/AndroidRuntime(9739): at io.openkit.OKLoginActivity.onRestoreInstanceState(OKLoginActivity.java:62) 02-01 16:31:13.684: E/AndroidRuntime(9739): at android.app.Activity.performRestoreInstanceState(Activity.java:910) 02-01 16:31:13.684: E/AndroidRuntime(9739): at android.app.Instrumentation.callActivityOnRestoreInstanceState(Instrumentation.java:1131) 02-01 16:31:13.684: E/AndroidRuntime(9739): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2158) 

Dans votre DialogFragment , appelez Fragment.setRetainInstance(boolean) avec la valeur true . Vous n’avez pas besoin de sauvegarder le fragment manuellement, le framework prend déjà en charge tout cela. En appelant cela, votre fragment ne sera pas détruit en rotation et vos requêtes réseau ne seront pas affectées.

Vous devrez peut-être append ce code pour empêcher le renvoi de votre boîte de dialog en raison d’un bogue avec la bibliothèque de compatibilité:

 @Override public void onDestroyView() { Dialog dialog = getDialog(); // handles https://code.google.com/p/android/issues/detail?id=17423 if (dialog != null && getRetainInstance()) { dialog.setDismissMessage(null); } super.onDestroyView(); } 

Un des avantages de l’utilisation de dialogFragment par rapport à l’utilisation de alertDialogBuilder est justement parce que le dialog peut automatiquement se recréer lors de la rotation sans intervention de l’utilisateur.

Cependant, lorsque le dialog ne se recrée pas, il est possible que vous onSaveInstanceState sans appeler super :

 @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); // <-- must call this if you want to retain dialogFragment upon rotation ... } 

Ceci est une méthode pratique utilisant le correctif de la réponse d’Antonyt:

 public class RetainableDialogFragment extends DialogFragment { public RetainableDialogFragment() { setRetainInstance(true); } @Override public void onDestroyView() { Dialog dialog = getDialog(); // handles https://code.google.com/p/android/issues/detail?id=17423 if (dialog != null && getRetainInstance()) { dialog.setDismissMessage(null); } super.onDestroyView(); } } 

Laissez simplement votre DialogFragment étendre cette classe et tout ira bien. Cela devient particulièrement pratique si vous avez plusieurs DialogFragments dans votre projet qui ont tous besoin de ce correctif.