12

Wiele lat temu wpadłem na problem w jednej z moich aplikacji, gdy próbowałem wykonać polecenie FragmentTransaction w moim oddzwonieniu pod numer onActivityResult(). Googling wokół, znalazłem this question and answer, co powiedziećCzy bezpieczne jest włanie FragmentTransaction wewnątrz onActivityResult()?

W czasie, onActivityResult() nazywa, stan aktywności/fragment może jeszcze nie zostały przywrócone, a zatem wszelkie transakcje, które zdarzają się w tym czasie zostaną utracone w wyniku .

Po zamknięciu dostosowałem rozwiązanie zalecane w tej samej odpowiedzi do mojej aplikacji, a życie było dobre. Jednak ostatnie eksperymenty pokazują, że być może rzeczy się zmieniły i teraz można bezpiecznie zabezpieczyć od FragmentTransaction od środka .

The documentation for (support v4) FragmentManager.beginTransaction() określa bezpieczne okno do transakcji jak:

Uwaga: Transakcja fragment mogą być tworzone tylko/popełnione przed działalności zbawczej jego stan. Jeśli próby popełnienia transakcję po FragmentActivity.onSaveInstanceState() (i przed następnym FragmentActivity.onStart lub FragmentActivity.onResume(), dostaniesz błąd.

Reading the documentation for onActivityResult() widzę

natychmiast otrzymasz to wezwanie przed onResume() kiedy twoja aktywność się ponownie rozpoczyna

To prowadzi mnie do przekonania, że ​​bezpieczne jest wykonywanie tych transakcji w onActivityResult(), as onStart() zostanie już wywołany, umieszczając mnie w bezpiecznym oknie.

Zrobiłem aplikację, aby przetestować to i pomyślnie widzę fragmenty okien dialogowych, które tworzę i zatwierdzam wewnątrz onActivityResult(). Miałem tę samą aplikację rejestrującą również wywołania zwrotne cyklu życia aktywności, więc mogłem sprawdzić ich kolejność i widzę onStart(), następnie onRestoreInstanceState(), a następnie onActivityResult() za każdym razem.

Czy brakuje mi czegoś? A może ramy zostały zmienione, a teraz onActivityResult() gwarantuje bezpieczne miejsce na transakcje fragmentacyjne? Czy to zachowanie różni się w zależności od poziomu interfejsu API?

Znalazłem another question and answer, który wydaje się czytać dokumentację w ten sam sposób co ja, ale oba mają ponad rok i nie odnoszą się do onActivityResult() jako bezpiecznego miejsca transakcji.

+0

Spójrz na http://www.androiddesignpatterns.com/2013/08/fragment-transaction-commit-state-loss.html – elmorabea

+0

@elmorabea Ten post na blogu jest jednym z materiałów, które odnalazłem, kiedy po raz pierwszy ten przypadek. Jednak, jak ilustruje poniższa odpowiedź Azizbekian, wydaje się, że nie jest już dokładnym wyjaśnieniem bezpiecznego okna do zatwierdzenia FragmentTransactions. –

Odpowiedz

14

Trochę nurkowania do źródeł

Jest zmienna logiczna wewnątrz FragmentManager klasy, zwany mStatedSaved. Ta zmienna śledzi zapisany stan w zależności od wywołań cyklu życia aktywności. Here's sposób, że rzuty znany wyjątek:

 

    private void checkStateLoss() { 
     if (mStateSaved) { 
      throw new IllegalStateException(
        "Can not perform this action after onSaveInstanceState"); 
     } 
     ... 
    } 
 

Oznacza to, że, jak tylko to zmienna jest zmieniona false, wtedy transakcje fragment swobodnie być wykonane.Ta zmienna stanie się true, gdy stan działania zostanie zapisany, tj. onSaveInstanceState().


Powrót na pytanie

Mówiłeś, że wcześniej miał problemy zatwierdzanie transakcji z onActivityResult(). To powinno oznaczać, że poprzednio mStateSaved nie był przypisany false i obecnie nim jest. W rzeczywistości tak jest.

Oto onActivityResult() realizacja od O wydaniu:

 

    @Override 
    protected void onActivityResult(int requestCode, int resultCode, Intent data) { 
     mFragments.noteStateNotSaved(); 
     ... 
    } 
 

Gdzie, noteStateNotSaved() zrobi następujący:

 

    public void noteStateNotSaved() { 
     ... 
     mStateSaved = false; 
     ... 
    } 
 

Wręcz przeciwnie, można zobaczyć realizację onActivityResult() Jelly Bean zwolnić:

 

    @Override 
    protected void onActivityResult(int requestCode, int resultCode, Intent data) { 
     int index = requestCode>>16; 
     if (index != 0) { 
      index--; 
      if (mFragments.mActive == null || index = mFragments.mActive.size()) { 
       Log.w(TAG, "Activity result fragment index out of range: 0x" 
         + Integer.toHexString(requestCode)); 
       return; 
      } 
      Fragment frag = mFragments.mActive.get(index); 
      if (frag == null) { 
       Log.w(TAG, "Activity result no fragment exists for index: 0x" 
         + Integer.toHexString(requestCode)); 
      } else { 
       frag.onActivityResult(requestCode&0xffff, resultCode, data); 
      } 
      return; 
     } 

     super.onActivityResult(requestCode, resultCode, data); 
    } 
 

Nic się nie zmieni ma wartość pola mStateSaved, co spowoduje zgłoszenie wyjątku w przypadku zatwierdzenia transakcji.

W rzeczywistości linia mFragments.noteStateNotSaved()was introduced w wydaniu Kit-Kat. Jak widać przez commit's comment wykonane przez Dianne Hackborn:

ActivityFragment należy wyczyścić flagę że stan jest zapisywany gdy odbiera onNewIntent(). Może się to zdarzyć, zanim aktywność zostanie wznowiona, więc być może jeszcze jej nie usunęliśmy. Również trzeba zrobić to samo, co dla onActivityResult().

Podsumowując

Czy onActivityResult() gwarantowana teraz być bezpiecznym miejscem dla transakcji fragment?

Zakładając, że korzystasz ze źródeł, które obejmują commit 4ccc001, który powstał w październiku 2012 r. - tak, jest to bezpieczne miejsce na transakcje fragmentów.

+5

Czytanie odpowiedzi takich jak ta jest czystą radością. Dziękuję Ci. – Vasiliy

+0

Dzięki, @Vasiliy, ciesząc się odpowiedziami/rozmowami również. – azizbekian

+1

@azizbekian bardzo wnikliwa .... idealna odpowiedź .... –