W tym przewodniku omawiamy narzędzia do debugowania fragmenty.
Logowanie FragmentManager
FragmentManager
mogą wysyłać różne komunikaty
Logcat. Ta opcja jest domyślnie wyłączona.
ale czasami te komunikaty dziennika mogą pomóc w rozwiązaniu problemu.
problemy z fragmentami. Funkcja FragmentManager
generuje najbardziej istotne dane wyjściowe
na poziomach logowania DEBUG
i VERBOSE
.
Logowanie możesz włączyć za pomocą tych opcji:
Polecenie adb shell
:
adb shell setprop log.tag.FragmentManager DEBUG
Możesz też włączyć logowanie szczegółowe w ten sposób:
adb shell setprop log.tag.FragmentManager VERBOSE
Po włączeniu rejestrowania szczegółowego możesz zastosować poziom rejestrowania,
w oknie Logcat. Jednak
filtruje wszystkie logi, a nie tylko logi FragmentManager
. Zwykle najlepiej jest
włącz logowanie FragmentManager
tylko na wymaganym poziomie logowania.
Logowanie DEBUG
Na poziomie DEBUG
FragmentManager
zwykle wysyła komunikaty logu dotyczące
zmian stanu cyklu życia. Każdy wpis logu zawiera symbol toString()
.
z środowiska Fragment
.
Wpis w dzienniku zawiera te informacje:
- Prosta nazwa klasy instancji
Fragment
. - Kod skrótu tożsamości
instancji
Fragment
. - Unikalny identyfikator instancji
Fragment
w menedżerze fragmentów. Jest stabilna zmian konfiguracji oraz procesu śmierci i rekreacji. - Identyfikator kontenera, do którego dodano element
Fragment
, ale tylko wtedy, gdy jest on ustawiony. - Tag
Fragment
, ale tylko wtedy, gdy jest ustawiony.
Oto przykładowy wpis logu DEBUG
:
D/FragmentManager: moveto ATTACHED: NavHostFragment{92d8f1d} (fd92599e-c349-4660-b2d6-0ece9ec72f7b id=0x7f080116)
- Zajęcia
Fragment
toNavHostFragment
. - Kod skrótu tożsamości to
92d8f1d
. - Unikalny identyfikator to
fd92599e-c349-4660-b2d6-0ece9ec72f7b
. - Identyfikator kontenera to
0x7f080116
. - Tag został pominięty, ponieważ nie został ustawiony żaden tag. Jeśli jest obecny, wskazuje
identyfikator w formacie
tag=tag_value
.
Aby zapewnić zwięzłość i czytelność, identyfikatory UUID są skracane w następujący sposób: przykłady.
Inicjuję: NavHostFragment
, a potem startDestination
Tworzę Fragment
typu FirstFragment
i przechodzi do:
stan RESUMED
:
D/FragmentManager: moveto ATTACHED: NavHostFragment{92d8f1d} (<UUID> id=0x7f080116) D/FragmentManager: mName=null mIndex=-1 mCommitted=false D/FragmentManager: Operations: D/FragmentManager: Op #0: SET_PRIMARY_NAV NavHostFragment{92d8f1d} (<UUID> id=0x7f080116) D/FragmentManager: moveto CREATED: NavHostFragment{92d8f1d} (<UUID> id=0x7f080116) D/FragmentManager: mName=null mIndex=-1 mCommitted=false D/FragmentManager: Operations: D/FragmentManager: Op #0: REPLACE FirstFragment{ccd2189} (<UUID> id=0x7f080116) D/FragmentManager: Op #1: SET_PRIMARY_NAV FirstFragment{ccd2189} (<UUID> id=0x7f080116) D/FragmentManager: moveto ATTACHED: FirstFragment{ccd2189} (<UUID> id=0x7f080116) D/FragmentManager: moveto CREATED: FirstFragment{ccd2189} (<UUID> id=0x7f080116) D/FragmentManager: moveto CREATE_VIEW: NavHostFragment{92d8f1d} (<UUID> id=0x7f080116) D/FragmentManager: moveto CREATE_VIEW: FirstFragment{ccd2189} (<UUID> id=0x7f080116) D/FragmentManager: moveto ACTIVITY_CREATED: NavHostFragment{92d8f1d} (<UUID> id=0x7f080116) D/FragmentManager: moveto RESTORE_VIEW_STATE: NavHostFragment{92d8f1d} (<UUID> id=0x7f080116) D/FragmentManager: moveto ACTIVITY_CREATED: FirstFragment{ccd2189} (<UUID> id=0x7f080116) D/FragmentManager: moveto RESTORE_VIEW_STATE: FirstFragment{ccd2189} (<UUID> id=0x7f080116) D/FragmentManager: moveto STARTED: NavHostFragment{92d8f1d} (<UUID> id=0x7f080116) D/FragmentManager: moveto STARTED: FirstFragment{ccd2189} (<UUID> id=0x7f080116) D/FragmentManager: moveto RESUMED: NavHostFragment{92d8f1d} (<UUID> id=0x7f080116) D/FragmentManager: moveto RESUMED: FirstFragment{ccd2189} (<UUID> id=0x7f080116)
Po interakcji użytkownika FirstFragment
przechodzi z
w różnych stanach cyklu życia. Następnie powstaje instancja SecondFragment
i przejścia
do stanu RESUMED
:
D/FragmentManager: mName=07c8a5e8-54a3-4e21-b2cc-c8efc37c4cf5 mIndex=-1 mCommitted=false D/FragmentManager: Operations: D/FragmentManager: Op #0: REPLACE SecondFragment{84132db} (<UUID> id=0x7f080116) D/FragmentManager: Op #1: SET_PRIMARY_NAV SecondFragment{84132db} (<UUID> id=0x7f080116) D/FragmentManager: movefrom RESUMED: FirstFragment{ccd2189} (<UUID> id=0x7f080116) D/FragmentManager: movefrom STARTED: FirstFragment{ccd2189} (<UUID> id=0x7f080116) D/FragmentManager: movefrom ACTIVITY_CREATED: FirstFragment{ccd2189} (<UUID> id=0x7f080116) D/FragmentManager: moveto ATTACHED: SecondFragment{84132db} (<UUID> id=0x7f080116) D/FragmentManager: moveto CREATED: SecondFragment{84132db} (<UUID> id=0x7f080116) D/FragmentManager: moveto CREATE_VIEW: SecondFragment{84132db} (<UUID> id=0x7f080116) D/FragmentManager: moveto ACTIVITY_CREATED: SecondFragment{84132db} (<UUID> id=0x7f080116) D/FragmentManager: moveto RESTORE_VIEW_STATE: SecondFragment{84132db} (<UUID> id=0x7f080116) D/FragmentManager: moveto STARTED: SecondFragment{84132db} (<UUID> id=0x7f080116) D/FragmentManager: movefrom CREATE_VIEW: FirstFragment{ccd2189} (<UUID> id=0x7f080116) D/FragmentManager: moveto RESUMED: SecondFragment{84132db} (<UUID> id=0x7f080116)
Wszystkie instancje Fragment
mają prefiks identyfikatora, który umożliwia śledzenie
różne wystąpienia
tej samej klasy: Fragment
.
WYBÓR SZCZEGÓŁOWE
Na poziomie VERBOSE
FragmentManager
zwykle wysyła komunikaty logu dotyczące swoich
stan wewnętrzny:
V/FragmentManager: Run: BackStackEntry{f9d3ff3} V/FragmentManager: add: NavHostFragment{86274b0} (<UUID> id=0x7f080130) V/FragmentManager: Added fragment to active set NavHostFragment{86274b0} (<UUID> id=0x7f080130) V/FragmentManager: computeExpectedState() of 1 for NavHostFragment{86274b0} (<UUID> id=0x7f080130) D/FragmentManager: moveto ATTACHED: NavHostFragment{86274b0} (<UUID> id=0x7f080130) V/FragmentManager: Commit: BackStackEntry{5cfd2ae} D/FragmentManager: mName=null mIndex=-1 mCommitted=false D/FragmentManager: Operations: D/FragmentManager: Op #0: SET_PRIMARY_NAV NavHostFragment{86274b0} (<UUID> id=0x7f080130) V/FragmentManager: computeExpectedState() of 1 for NavHostFragment{86274b0} (<UUID> id=0x7f080130) D/FragmentManager: moveto CREATED: NavHostFragment{86274b0} (<UUID> id=0x7f080130) V/FragmentManager: Commit: BackStackEntry{e93833f} D/FragmentManager: mName=null mIndex=-1 mCommitted=false D/FragmentManager: Operations: D/FragmentManager: Op #0: REPLACE FirstFragment{886440c} (<UUID> id=0x7f080130) D/FragmentManager: Op #1: SET_PRIMARY_NAV FirstFragment{886440c} (<UUID> id=0x7f080130) V/FragmentManager: Run: BackStackEntry{e93833f} V/FragmentManager: add: FirstFragment{886440c} (<UUID> id=0x7f080130) V/FragmentManager: Added fragment to active set FirstFragment{886440c} (<UUID> id=0x7f080130) V/FragmentManager: computeExpectedState() of 1 for FirstFragment{886440c} (<UUID> id=0x7f080130) D/FragmentManager: moveto ATTACHED: FirstFragment{886440c} (<UUID> id=0x7f080130) V/FragmentManager: computeExpectedState() of 1 for FirstFragment{886440c} (<UUID> id=0x7f080130) D/FragmentManager: moveto CREATED: FirstFragment{886440c} (<UUID> id=0x7f080130) V/FragmentManager: computeExpectedState() of 1 for FirstFragment{886440c} (<UUID> id=0x7f080130) V/FragmentManager: computeExpectedState() of 1 for FirstFragment{886440c} (<UUID> id=0x7f080130) V/FragmentManager: computeExpectedState() of 1 for FirstFragment{886440c} (<UUID> id=0x7f080130) V/FragmentManager: computeExpectedState() of 1 for FirstFragment{886440c} (<UUID> id=0x7f080130) V/FragmentManager: computeExpectedState() of 1 for NavHostFragment{86274b0} (<UUID> id=0x7f080130) V/FragmentManager: computeExpectedState() of 1 for NavHostFragment{86274b0} (<UUID> id=0x7f080130) V/FragmentManager: computeExpectedState() of 1 for NavHostFragment{86274b0} (<UUID> id=0x7f080130) V/FragmentManager: computeExpectedState() of 4 for NavHostFragment{86274b0} (<UUID> id=0x7f080130) D/FragmentManager: moveto CREATE_VIEW: NavHostFragment{86274b0} (<UUID> id=0x7f080130) V/FragmentManager: computeExpectedState() of 2 for FirstFragment{886440c} (<UUID> id=0x7f080130) D/FragmentManager: moveto CREATE_VIEW: FirstFragment{886440c} (<UUID> id=0x7f080130) V/FragmentManager: computeExpectedState() of 2 for FirstFragment{886440c} (<UUID> id=0x7f080130) V/FragmentManager: computeExpectedState() of 2 for FirstFragment{886440c} (<UUID> id=0x7f080130) V/FragmentManager: computeExpectedState() of 4 for NavHostFragment{86274b0} (<UUID> id=0x7f080130) D/FragmentManager: moveto ACTIVITY_CREATED: NavHostFragment{86274b0} (<UUID> id=0x7f080130) D/FragmentManager: moveto RESTORE_VIEW_STATE: NavHostFragment{86274b0} (<UUID> id=0x7f080130) V/FragmentManager: computeExpectedState() of 4 for FirstFragment{886440c} (<UUID> id=0x7f080130) D/FragmentManager: moveto ACTIVITY_CREATED: FirstFragment{886440c} (<UUID> id=0x7f080130) D/FragmentManager: moveto RESTORE_VIEW_STATE: FirstFragment{886440c} (<UUID> id=0x7f080130) V/FragmentManager: computeExpectedState() of 4 for FirstFragment{886440c} (<UUID> id=0x7f080130) V/FragmentManager: SpecialEffectsController: Enqueuing add operation for fragment FirstFragment{886440c} (<UUID> id=0x7f080130) V/FragmentManager: computeExpectedState() of 4 for FirstFragment{886440c} (<UUID> id=0x7f080130) V/FragmentManager: computeExpectedState() of 4 for FirstFragment{886440c} (<UUID> id=0x7f080130) V/FragmentManager: SpecialEffectsController: For fragment FirstFragment{886440c} (<UUID> id=0x7f080130) mFinalState = VISIBLE -> VISIBLE. V/FragmentManager: SpecialEffectsController: Container androidx.fragment.app.FragmentContainerView{7578ffa V.E...... ......I. 0,0-0,0 #7f080130 app:id/nav_host_fragment_content_fragment} is not attached to window. Cancelling pending operation Operation {382a9ab} {mFinalState = VISIBLE} {mLifecycleImpact = ADDING} {mFragment = FirstFragment{886440c} (<UUID> id=0x7f080130)} V/FragmentManager: SpecialEffectsController: Operation {382a9ab} {mFinalState = VISIBLE} {mLifecycleImpact = ADDING} {mFragment = FirstFragment{886440c} (<UUID> id=0x7f080130)} has called complete. V/FragmentManager: SpecialEffectsController: Setting view androidx.constraintlayout.widget.ConstraintLayout{3968808 I.E...... ......I. 0,0-0,0} to VISIBLE V/FragmentManager: computeExpectedState() of 4 for FirstFragment{886440c} (<UUID> id=0x7f080130) V/FragmentManager: computeExpectedState() of 4 for NavHostFragment{86274b0} (<UUID> id=0x7f080130) V/FragmentManager: SpecialEffectsController: Enqueuing add operation for fragment NavHostFragment{86274b0} (<UUID> id=0x7f080130) V/FragmentManager: computeExpectedState() of 4 for NavHostFragment{86274b0} (<UUID> id=0x7f080130) V/FragmentManager: computeExpectedState() of 4 for NavHostFragment{86274b0} (<UUID> id=0x7f080130) V/FragmentManager: SpecialEffectsController: For fragment NavHostFragment{86274b0} (<UUID> id=0x7f080130) mFinalState = VISIBLE -> VISIBLE. V/FragmentManager: SpecialEffectsController: Container androidx.fragment.app.FragmentContainerView{2ba8ba1 V.E...... ......I. 0,0-0,0 #7f080130 app:id/nav_host_fragment_content_fragment} is not attached to window. Cancelling pending operation Operation {f7eb1c6} {mFinalState = VISIBLE} {mLifecycleImpact = ADDING} {mFragment = NavHostFragment{86274b0} (<UUID> id=0x7f080130)} V/FragmentManager: SpecialEffectsController: Operation {f7eb1c6} {mFinalState = VISIBLE} {mLifecycleImpact = ADDING} {mFragment = NavHostFragment{86274b0} (<UUID> id=0x7f080130)} has called complete. V/FragmentManager: SpecialEffectsController: Setting view androidx.fragment.app.FragmentContainerView{7578ffa I.E...... ......I. 0,0-0,0 #7f080130 app:id/nav_host_fragment_content_fragment} to VISIBLE V/FragmentManager: computeExpectedState() of 4 for NavHostFragment{86274b0} (<UUID> id=0x7f080130) V/FragmentManager: Run: BackStackEntry{5cfd2ae} V/FragmentManager: computeExpectedState() of 4 for NavHostFragment{86274b0} (<UUID> id=0x7f080130) V/FragmentManager: computeExpectedState() of 4 for NavHostFragment{86274b0} (<UUID> id=0x7f080130) V/FragmentManager: computeExpectedState() of 4 for NavHostFragment{86274b0} (<UUID> id=0x7f080130) V/FragmentManager: computeExpectedState() of 5 for NavHostFragment{86274b0} (<UUID> id=0x7f080130) D/FragmentManager: moveto STARTED: NavHostFragment{86274b0} (<UUID> id=0x7f080130) V/FragmentManager: computeExpectedState() of 5 for FirstFragment{886440c} (<UUID> id=0x7f080130) D/FragmentManager: moveto STARTED: FirstFragment{886440c} (<UUID> id=0x7f080130) V/FragmentManager: computeExpectedState() of 5 for FirstFragment{886440c} (<UUID> id=0x7f080130) V/FragmentManager: computeExpectedState() of 5 for FirstFragment{886440c} (<UUID> id=0x7f080130) V/FragmentManager: computeExpectedState() of 5 for NavHostFragment{86274b0} (<UUID> id=0x7f080130) V/FragmentManager: computeExpectedState() of 5 for NavHostFragment{86274b0} (<UUID> id=0x7f080130) V/FragmentManager: computeExpectedState() of 7 for NavHostFragment{86274b0} (<UUID> id=0x7f080130) V/FragmentManager: computeExpectedState() of 7 for NavHostFragment{86274b0} (<UUID> id=0x7f080130) D/FragmentManager: moveto RESUMED: NavHostFragment{86274b0} (<UUID> id=0x7f080130) V/FragmentManager: computeExpectedState() of 7 for FirstFragment{886440c} (<UUID> id=0x7f080130) V/FragmentManager: computeExpectedState() of 7 for FirstFragment{886440c} (<UUID> id=0x7f080130) D/FragmentManager: moveto RESUMED: FirstFragment{886440c} (<UUID> id=0x7f080130) V/FragmentManager: computeExpectedState() of 7 for FirstFragment{886440c} (<UUID> id=0x7f080130) V/FragmentManager: computeExpectedState() of 7 for FirstFragment{886440c} (<UUID> id=0x7f080130) V/FragmentManager: computeExpectedState() of 7 for NavHostFragment{86274b0} (<UUID> id=0x7f080130) V/FragmentManager: computeExpectedState() of 7 for NavHostFragment{86274b0} (<UUID> id=0x7f080130)
Ten przykład obejmuje tylko wczytywanie na stronie FirstFragment
. W tym przejście na
SecondFragment
znacznie zwiększa liczbę wpisów logu.
Wiele komunikatów logu na poziomie VERBOSE
jest rzadko używanych przez aplikacje
dla programistów. Wiedza o tym, kiedy wystąpią zmiany w stosunku wstecznym, może pomóc
i debugowanie niektórych problemów.
StrictMode dla fragmentów
Program jest w wersji 1.4.0 lub nowszej
Biblioteka Jetpack Fragment zawiera
StrictMode dla fragmentów. Wykryją typowe problemy, które powodują
zachowanie aplikacji w nieoczekiwany sposób. Więcej informacji na temat współpracy z
StrictMode
, zobacz StrictMode.
Niestandardowy
Policy
określa, które naruszenia są wykrywane, i jaka kara zostanie zastosowana
po wykryciu naruszeń.
Aby zastosować niestandardową zasadę StrictMode, przypisz ją do
FragmentManager
Zrób to jak najwcześniej. W tym przypadku trzeba to zrobić w
bloku init
lub w konstruktorze Java:
Kotlin
class ExampleActivity : AppCompatActivity() { init { supportFragmentManager.strictModePolicy = FragmentStrictMode.Policy.Builder() .penaltyDeath() .detectFragmentReuse() .allowViolation(FirstFragment::class.java, FragmentReuseViolation::class.java) .build() } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val binding = ActivityExampleBinding.inflate(layoutInflater) setContentView(binding.root) ... } }
Java
class ExampleActivity extends AppCompatActivity() { ExampleActivity() { getSupportFragmentManager().setStrictModePolicy( new FragmentStrictMode.Policy.Builder() .penaltyDeath() .detectFragmentReuse() .allowViolation(FirstFragment.class, FragmentReuseViolation.class) .build() ); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState) ActivityExampleBinding binding = ActivityExampleBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); ... } }
W przypadkach, gdy musisz znać Context
, aby określić, czy
włącz StrictMode, na przykład z wartości zasobu z wartością logiczną, możesz
odrocz przypisanie zasady StrictMode do FragmentManager
za pomocą
OnContextAvailableListener
:
Kotlin
class ExampleActivity : AppCompatActivity() { init { addOnContextAvailableListener { context -> if(context.resources.getBoolean(R.bool.enable_strict_mode)) { supportFragmentManager.strictModePolicy = FragmentStrictMode.Policy.Builder() .penaltyDeath() .detectFragmentReuse() .allowViolation(FirstFragment::class.java, FragmentReuseViolation::class.java) .build() } } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val binding = ActivityExampleBinding.inflate(layoutInflater) setContentView(binding.root) ... } }
Java
class ExampleActivity extends AppCompatActivity() { ExampleActivity() { addOnContextAvailableListener((context) -> { if(context.getResources().getBoolean(R.bool.enable_strict_mode)) { getSupportFragmentManager().setStrictModePolicy( new FragmentStrictMode.Policy.Builder() .penaltyDeath() .detectFragmentReuse() .allowViolation(FirstFragment.class, FragmentReuseViolation.class) .build() ); } } } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState) ActivityExampleBinding binding = ActivityExampleBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); ... } }
Ostatni punkt, w którym możesz skonfigurować StrictMode tak, by wychwytywał wszystkie możliwe
znajduje się w
onCreate()
przed połączeniem z numerem super.onCreate()
:
Kotlin
class ExampleActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { supportFragmentManager.strictModePolicy = FragmentStrictMode.Policy.Builder() .penaltyDeath() .detectFragmentReuse() .allowViolation(FirstFragment::class.java, FragmentReuseViolation::class.java) .build() super.onCreate(savedInstanceState) val binding = ActivityExampleBinding.inflate(layoutInflater) setContentView(binding.root) ... } }
Java
class ExampleActivity extends AppCompatActivity() { @Override protected void onCreate(Bundle savedInstanceState) { getSupportFragmentManager().setStrictModePolicy( new FragmentStrictMode.Policy.Builder() .penaltyDeath() .detectFragmentReuse() .allowViolation(FirstFragment.class, FragmentReuseViolation.class) .build() ); super.onCreate(savedInstanceState) ActivityExampleBinding binding = ActivityExampleBinding.inflate(getLayoutInflater()); setContentView(binding.getRoot()); ... } }
Ta zasada używana w tych przykładach wykrywa tylko przypadki ponownego użycia fragmentów,
a aplikacja przestaje działać, gdy tylko wystąpi jakiś błąd. penaltyDeath()
może być
jest przydatny w debugowaniu kompilacji,
bo zawodzą one na tyle szybko, że nie można ich zignorować
naruszenia zasad.
Możesz też zezwolić na wybiórcze wyświetlanie określonych naruszeń. Zasada stosowana w poprzedni przykład wymusza jednak to naruszenie we wszystkich pozostałych fragmentach . Jest to przydatne w przypadkach, gdy komponent biblioteki innej firmy może zawierają naruszenia StrictMode.
W takich przypadkach możesz tymczasowo dodać te naruszenia do listy dozwolonych StrictMode w przypadku komponentów, które nie należą do Ciebie, dopóki biblioteka usunięcie naruszenia zasad.
Szczegółowe informacje o konfigurowaniu innych naruszeń znajdziesz w dokumentacji
FragmentStrictMode.Policy.Builder
Istnieją 3 typy kar.
penaltyLog()
przesyła szczegóły naruszeń do narzędzia Logcat.penaltyDeath()
zamyka aplikację po wykryciu naruszeń.penaltyListener()
umożliwia dodanie niestandardowego detektora wywoływanego zawsze, gdy dojdzie do naruszenia – wykryto.
W Policy
możesz zastosować dowolną kombinację kar. Jeśli Twoja zasada
nie określono wyraźnie kary, zastosowana zostanie domyślna wartość penaltyLog()
. Jeśli
zastosuj karę inną niż penaltyLog()
w niestandardowym elemencie Policy
, a następnie
Funkcja penaltyLog()
jest wyłączona, chyba że została przez Ciebie ustawiona.
penaltyListener()
może być przydatne, jeśli korzystasz z zewnętrznej biblioteki logów
które mają być rejestrowane. Możesz też włączyć
wykrywanie przypadków niekrytycznych w kompilacjach wersji i rejestrowanie ich w raportach o awariach.
bibliotece. Ta strategia może wykrywać przypadki naruszenia zasad, które zostałyby pominięte.
Aby ustawić globalną zasadę StrictMode, ustaw domyślną zasadę, która będzie stosowana do wszystkich
FragmentManager
instancji za pomocą metody
FragmentStrictMode.setDefaultPolicy()
:
Kotlin
class MyApplication : Application() { override fun onCreate() { super.onCreate() FragmentStrictMode.defaultPolicy = FragmentStrictMode.Policy.Builder() .detectFragmentReuse() .detectFragmentTagUsage() .detectRetainInstanceUsage() .detectSetUserVisibleHint() .detectTargetFragmentUsage() .detectWrongFragmentContainer() .apply { if (BuildConfig.DEBUG) { // Fail early on DEBUG builds penaltyDeath() } else { // Log to Crashlytics on RELEASE builds penaltyListener { FirebaseCrashlytics.getInstance().recordException(it) } } } .build() } }
Java
public class MyApplication extends Application { @Override public void onCreate() { super.onCreate(); FragmentStrictMode.Policy.Builder builder = new FragmentStrictMode.Policy.Builder(); builder.detectFragmentReuse() .detectFragmentTagUsage() .detectRetainInstanceUsage() .detectSetUserVisibleHint() .detectTargetFragmentUsage() .detectWrongFragmentContainer(); if (BuildConfig.DEBUG) { // Fail early on DEBUG builds builder.penaltyDeath(); } else { // Log to Crashlytics on RELEASE builds builder.penaltyListener((exception) -> FirebaseCrashlytics.getInstance().recordException(exception) ); } FragmentStrictMode.setDefaultPolicy(builder.build()); } }
W sekcjach poniżej opisujemy typy naruszeń i możliwe sposoby obejścia tych problemów.
Ponowne wykorzystanie fragmentu
Naruszenie zasady ponownego użycia fragmentów jest włączone za pomocą:
detectFragmentReuse()
i rzuca
FragmentReuseViolation
To naruszenie zasad wskazuje na ponowne użycie instancji Fragment
po jej usunięciu
od FragmentManager
. To ponowne użycie może powodować problemy, ponieważ Fragment
może
zachowują stan od poprzedniego użycia i nie zachowują się spójnie. Jeśli utworzysz
nowej instancji, po dodaniu do instancji jest ona zawsze w stanie początkowym
FragmentManager
Użycie tagu fragmentu
Naruszenie zasad użycia tagu fragmentu jest włączone za pomocą tagu
detectFragmentTagUsage()
i rzuca
FragmentTagUsageViolation
To naruszenie zasad wskazuje, że Fragment
zawiera wartość zawyżoną przy użyciu <fragment>
w układzie XML. Aby rozwiązać ten problem, zwiększ: Fragment
<androidx.fragment.app.FragmentContainerView>
zamiast w <fragment>
. Fragmenty rozszerzone za pomocą FragmentContainerView
są w stanie niezawodnie obsługiwać
Liczba transakcji i zmian konfiguracji: Fragment
. Mogą one nie działać jako
oczekiwany w przypadku używania tagu <fragment>
.
Zachowaj wykorzystanie instancji
Naruszenie zasad dotyczących zachowywania wykorzystania instancji jest włączone za pomocą
detectRetainInstanceUsage()
i rzuca
RetainInstanceUsageViolation
To naruszenie zasad wskazuje na użycie zachowywanego elementu Fragment
, zwłaszcza jeśli
są połączenia do
setRetainInstance()
.
lub
getRetainInstance()
,
które zostały wycofane.
Zamiast używać tych metod do zarządzania zachowanymi instancjami Fragment
zapisz stan w
ViewModel
.
która zajmie się tym za Ciebie.
Ustaw wskazówkę widoczną dla użytkownika
Ustawianie naruszenia wskazówek dotyczących widocznych dla użytkownika jest włączone za pomocą:
detectSetUserVisibleHint()
i rzuca
SetUserVisibleHintViolation
To naruszenie zasad wskazuje połączenie do
setUserVisibleHint()
który został wycofany.
Jeśli wywołujesz tę metodę ręcznie, wywołaj
setMaxLifecycle()
. Jeśli zastąpisz tę metodę, przenieś działanie do
onResume()
podczas przechodzenia w true
i
onPause()
podczas przechodzenia w ciągu false
.
Docelowe wykorzystanie fragmentu
Naruszenie zasad użycia docelowego fragmentu jest włączone za pomocą:
detectTargetFragmentUsage()
i rzuca
TargetFragmentUsageViolation
To naruszenie zasad wskazuje połączenie do
setTargetFragment()
getTargetFragment()
,
lub getTargetRequestCode()
,
które zostały wycofane. Zamiast korzystać z tych metod, zarejestruj plik
FragmentResultListener
Więcej informacji o przekazywaniu wyników znajdziesz w artykule Przekazuj wyniki między
Fragmenty kodu.
Nieprawidłowy kontener fragmentów
Włączono nieprawidłowe naruszenie kontenera z fragmentami za pomocą:
detectWrongFragmentContainer()
i rzuca
WrongFragmentContainerViolation
To naruszenie zasad wskazuje, że zasób Fragment
został dodany do kontenera innego niż
FragmentContainerView
Tak jak w przypadku korzystania z tagów Fragment
,
transakcje oparte na fragmentach mogą nie działać zgodnie z oczekiwaniami, chyba że będą hostowane w
FragmentContainerView
Użycie widoku kontenera pomaga też rozwiązać problem w interfejsie API View
, który
powoduje, że fragmenty korzystające z animacji wyjścia są rysowane nad wszystkimi pozostałymi
fragmenty.