Wie kann ein DialogFragment durch Rotation ordnungsgemäß beibehalten werden?

74

Ich habe eine FragmentActivity, die ein DialogFragment hostet.

Das DialogFragment führt Netzwerkanforderungen aus und verarbeitet die Facebook-Authentifizierung. Daher muss ich sie während der Rotation beibehalten.

Ich habe alle anderen Fragen zu diesem Problem gelesen, aber keine von ihnen hat das Problem tatsächlich gelöst.

Ich verwende putFragment und getFragment, um die Fragment-Instanz zu speichern und sie während der Neuerstellung der Aktivität erneut abzurufen.

Beim Aufruf von getFragment in onRestoreInstanceState wird jedoch immer eine Nullzeigerausnahme angezeigt. Ich möchte auch verhindern, dass der Dialog während der Rotation geschlossen wird, aber bisher kann ich die Instanz nicht einmal beibehalten.

Irgendwelche Ideen, was falsch läuft?

So sieht mein Code derzeit aus:

public class OKLoginActivity extends FragmentActivity implements OKLoginDialogListener
{

    private OKLoginFragment loginDialog;
    private static final String 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);
    }

}

Dies ist die Ausnahme-Stack-Ablaufverfolgung:

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)
ch3rryc0ke
quelle
Können Sie uns den Exception Stack Trace geben? Ich denke, Sie möchten sich vielleicht auf diesen Aspekt des Problems konzentrieren.
Brian Attwell
Was passiert, wenn Sie die Aufrufe von putFragment und getFragment löschen? Wenn das DialogFragment derzeit auf dem Bildschirm angezeigt wird, sollte der Status des Fragments bei einer Konfigurationsänderung wiederhergestellt werden.
user697495
Ich denke, die NullPointerException verschwindet, wenn Sie super.onSaveInstanceState(outState)in Ihrer überschriebenen onSaveInstanceStateMethode einen Aufruf hinzufügen .
Burnttoast11

Antworten:

146

DialogFragmentRufen Sie in Ihrem Fragment.setRetainInstance(boolean)mit dem Wert an true. Sie müssen das Fragment nicht manuell speichern, das Framework kümmert sich bereits um all dies. Wenn Sie dies aufrufen, wird verhindert, dass Ihr Fragment bei der Rotation zerstört wird und Ihre Netzwerkanforderungen nicht betroffen sind.

Möglicherweise müssen Sie diesen Code hinzufügen, um zu verhindern, dass Ihr Dialogfeld bei der Rotation aufgrund eines Fehlers in der Kompatibilitätsbibliothek geschlossen wird:

@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();
}
antonyt
quelle
Das hat den Trick gemacht. Ich denke, was ich vorher falsch gemacht habe, war, dass ich den showDialg () - Code in onCreateView mit einer Nullprüfung auf savedInstanceState
ch3rryc0ke
Fehler erhalten- java.lang.RuntimeException: Aktivität {com.attchment / com.attchment.MainActivity} kann nicht zerstört werden: java.lang.IllegalStateException: OnDismissListener wird bereits von DialogFragment übernommen und kann nicht ersetzt werden.
abh22ishek
12
Hey Google, komm schon, das ist keine Raketenwissenschaft. Warum reparierst du es nicht? :)
Diego
1
@Diego Google ist nicht dafür bekannt, Fehler schnell zu beheben. CoordinatorView ist voll von Fehlern, die seit 2 Jahren nicht behoben wurden. Hatte einen Fehler in MapFragment, der nach 3 Jahren behoben wurde. Zumindest der MapFragment-Fehler wurde irgendwann behoben :)
A. Steenbergen
Fragment.javasagt dies über setRetainInstance: "Dies kann nur mit Fragmenten verwendet werden, die sich nicht im Backstack befinden." DialogFragment befindet sich definitiv im Backstack show. Weiß jemand, ob dieser Codekommentar einfach falsch ist?
AndroidGy
16

Einer der Vorteile der Verwendung dialogFragmentgegenüber der alertDialogBuilderbloßen Verwendung besteht genau darin, dass sich das Dialogfragment beim Drehen ohne Benutzereingriff automatisch neu erstellen kann.

Wenn sich das Dialogfragment jedoch nicht selbst neu erstellt, ist es möglich, dass Sie überschreiben onSaveInstanceState, aber nicht aufrufen super:

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState); // <-- must call this if you want to retain dialogFragment upon rotation
    ...
}
Neoh
quelle
1
+1 dazu, nur um zu erwähnen, dass dies meiner Erfahrung nach für Ansichten gilt und wir noch Variablen speichern müssen
Badr
11

Dies ist eine bequeme Methode, bei der der Fix aus Antonytys Antwort verwendet wird:

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

Lassen Sie einfach DialogFragmentdiese Klasse erweitern und alles wird gut. Dies ist besonders praktisch, wenn Sie mehrere DialogFragmentsin Ihrem Projekt haben, die alle dieses Update benötigen.

Willi Mentzel
quelle