findFragmentByTag null für Fragment A, falls setRetain (true) für Fragment B

8

Mein Problem beinhaltet eine Aktivität, die drei Supportfragmente enthält. Einer ist ein normales programmatisches Fragment (nennen wir es ein Home-Fragment). Das eine ist ein Porträtfragment, das auf dem Hauptfragment hinzugefügt wird, wenn das Gerät ausgerichtet ist, und das andere ist "kopflos", um unabhängig von Konfigurationsänderungen eine asynchrone Aufgabe fortzusetzen. Sehr einfach, ich arbeitete an diesem netten Beispiel .

%Vor%

}

In meiner Aktivität onCreate () erstelle ich das kopflose Fragment und füge es bei Bedarf hinzu.

%Vor%

Ich starte dann eine asynchrone Aufgabe (über meine startFetching () -Funktion) nach einer Verzögerung von 6 Sekunden (zum Testen), die in der onCreateView () des Porträtfragments ausgelöst wurde, das hinzugefügt wird, wenn sich die Ausrichtung in Hochformat ändert. Die Orientierungsänderung wird in der Aktivität onCreate () der Aktivität erkannt:

%Vor%

Nach Abschluss der Aufgabe kehre ich zur Aktivität zurück und versuche, die Benutzeroberfläche des aktiven Hochformatfragments zu aktualisieren. Der Fragmentmanager kann sie jedoch nicht finden. findFragmentByTag () gibt null zurück.

Um es klar zu sagen:

  • Das Tag ist korrekt
  • Das Fragment wird gefunden, wenn ich nicht das Gerät ausricht, und starte stattdessen die asynchrone Aufgabe irgendwo während der Aktivität onResume () zum Beispiel.
  • Wenn ich dem kopflosen Fragment nicht sage, dass es sich selbst behalten soll - und dadurch den Vorteil verliert, es nicht neu zu erschaffen, wird das Porträtfragment auch korrekt gefunden.
  • Debugging Ich kann alle 3 Fragmente im Manager sehen, wenn der kopflose nicht darauf eingestellt ist, sich selbst zu behalten . Wenn es so ist, kann ich nur das kopflose Fragment sehen.

Vielleicht werden durch das Beibehalten eines Fragments aggressiv andere Fragmente, die nicht beibehalten werden, oder etwas in diesem Sinne getötet?

    
Daniel Wilson 30.10.2015, 22:25
quelle

1 Antwort

4

Die Wurzel des Problems besteht darin, wie Sie den Verweis auf die Aktivität innerhalb des kopflosen Fragments beibehalten.
Aus dem bereitgestellten Code geht nicht hervor, wie Sie die Benutzeroberfläche nach Abschluss von AsyncTask aktualisieren. Nehmen wir an, Sie verwenden mRequest aus dem ersten Code-Snippet. Sie geben mRequest an den Konstruktor an, wenn Sie eine neue AsyncTask benötigen und diese Referenz verwenden, nachdem AsyncTask abgeschlossen ist.
Es ist in Ordnung, wenn zwischen dem Zeitpunkt der Erstellung der Aktivität und der Aktualisierung der Benutzeroberfläche keine Bildschirmdrehung erfolgt. Dies liegt daran, dass Sie den Verweis auf die noch aktive Aktivität verwenden.
Es ist nicht in Ordnung, wenn Sie den Bildschirm drehen. Sie haben jedes Mal nach der Rotation neue Aktivitäten. Aber mRequest wird nur einmal zugewiesen, wenn Sie ein kopfloses Fragment beim ersten Aufruf von onCreate() der Aktivität erstellen. Es enthält also eine Referenz auf die erste Instanz der Aktivität, die nach der Rotation nicht aktiv ist. Es gibt zwei Fälle von Aktivität nach Rotation in Ihrem Fall: der erste - der von mRequest referenziert wird und der zweite - der sichtbar und aktiv ist. Sie können dies bestätigen, indem Sie die Referenz der Aktivität in onCreate : Log.i(TAG, "onCreate: this=" + this); und die Methode der internen Aktivität protokollieren, die die Benutzeroberfläche nach der asynchronen Task aktualisiert: Log.i(TAG, "updating UI: this=" + this);
Außerdem ist die erste Aktivität im Zerstörten Zustand. Alle Fragmente werden von dieser Aktivität abgelöst und nicht zurückgehaltene Fragmente werden zerstört. Deshalb gibt findFragmentByTag null zurück.
Wenn das kopflose Fragment nicht auf sich selbst festgelegt ist, wird es in onCreate() der Aktivität bei jedem Aufruf neu erstellt. So verweist mRequest immer auf die zuletzt erstellte Aktivität mit allen Fragmenten. In diesem Fall gibt findFragmentByTag nicht null zurück Um dieses Problem zu vermeiden, schlage ich vor:

  1. Verwenden Sie die schwache Referenz, um die Referenz der Aktivität zu speichern. So etwas wie:
    private WeakReference<RequestCustomerDetails> mRequest;
  2. Erstellen Sie eine Methode in HeadlessCustomerDetailFetchFragment , um diese Referenz zu aktualisieren.
    public void updateResultProcessor(RequestCustomerDetails requestCustomerDetails) { mRequest = new WeakReference(requestCustomerDetails); // Update ui if there is stored result of AsyncTask (see p.4b) }
  3. Rufen Sie diese Methode aus der onCreate () -Aktivität jedes Mal auf.
  4. Wenn AsyncTask beendet ist:
    a) Wenn mRequest.get() nicht null ist, update UI.
    b) Wenn mRequest.get() ist null , dann speichern Sie das Ergebnis innerhalb des Headless-Fragments und verwenden Sie es in S.2 Eine schwache Referenz ermöglicht es GC, zerstörte Aktivität zu verarbeiten und null innerhalb einer schwachen Referenz zu setzen. Null innerhalb der schwachen Referenz signalisiert, dass es keine Benutzeroberfläche gibt und nichts zu aktualisieren ist. Das Speichern des Ergebnisses von AsyncTask in einem kopflosen Fragment ermöglicht es, dieses Ergebnis zu verwenden, um die Benutzeroberfläche nach ihrer Wiederherstellung zu aktualisieren.

    Hoffe, das wird helfen. Entschuldigung für mein Englisch. Wenn etwas unklar ist, werde ich versuchen es zu erklären.
Nick S 08.11.2015, 19:15
quelle