In diesem Leitfaden werden Tools behandelt, die Sie zur Fehlerbehebung Fragmente.
FragmentManager-Logging
FragmentManager
verschiedene Nachrichten an die
Logcat: Dies ist standardmäßig deaktiviert.
aber manchmal können Ihnen diese Logeinträge dabei helfen,
Probleme mit Ihren Fragmenten. FragmentManager
gibt die aussagekräftigste Ausgabe aus
auf den Protokollebenen DEBUG
und VERBOSE
.
Sie können das Logging folgendermaßen aktivieren:
adb shell
-Befehl:
adb shell setprop log.tag.FragmentManager DEBUG
Alternativ können Sie die ausführliche Protokollierung so aktivieren:
adb shell setprop log.tag.FragmentManager VERBOSE
Wenn Sie die ausführliche Protokollierung aktivieren, können Sie eine Logebene anwenden.
Filter im Logcat-Fenster. Dieses
filtert alle Logs, nicht nur die FragmentManager
-Logs. In der Regel ist es am besten,
Aktivieren Sie das FragmentManager
-Logging nur auf der erforderlichen Logebene.
DEBUG-Protokollierung
Auf DEBUG
-Ebene gibt FragmentManager
in der Regel Logeinträge aus, die sich auf
Änderungen am Lebenszyklusstatus. Jeder Logeintrag enthält das Objekt toString()
Dump von Fragment
.
Ein Logeintrag besteht aus den folgenden Informationen:
- Der einfache Klassenname der
Fragment
-Instanz. - Den Hash-Code der Identität
der Instanz
Fragment
. - Die eindeutige ID des Fragmentmanagers der Instanz
Fragment
. Dies ist stabil über Konfigurationsänderungen bis hin zum Beenden und Neuerstellen von Prozessen. - Die ID des Containers, dem die
Fragment
hinzugefügt wird, aber nur, wenn sie festgelegt ist. - Das
Fragment
-Tag, aber nur, wenn es festgelegt ist.
Hier sehen Sie ein Beispiel für einen DEBUG
-Logeintrag:
D/FragmentManager: moveto ATTACHED: NavHostFragment{92d8f1d} (fd92599e-c349-4660-b2d6-0ece9ec72f7b id=0x7f080116)
- Die
Fragment
-Klasse istNavHostFragment
. - Der Identitäts-Hash-Code lautet
92d8f1d
. - Die eindeutige ID lautet
fd92599e-c349-4660-b2d6-0ece9ec72f7b
. - Die Container-ID lautet
0x7f080116
. - Das Tag wird weggelassen, da kein Tag festgelegt wurde. Wenn vorhanden, folgt sie
die ID im Format
tag=tag_value
.
Zur besseren Übersichtlichkeit und Lesbarkeit wurden die UUIDs wie folgt gekürzt: Beispiele.
Hier ist ein NavHostFragment
, der initialisiert wird, und dann der startDestination
Fragment
vom Typ FirstFragment
wird erstellt und der Übergang zu
den RESUMED
-Status:
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)
Nach einer Nutzerinteraktion wechselt FirstFragment
aus der
verschiedenen Lebenszykluszuständen. Dann wird SecondFragment
instanziiert und die Übergänge
bis zum RESUMED
-Status durch:
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)
Allen Fragment
-Instanzen wird eine Kennung angehängt, sodass Sie
verschiedene Instanzen des
dieselbe Fragment
-Klasse.
VERBOSE-Logging
Auf VERBOSE
-Ebene gibt FragmentManager
in der Regel Log-Nachrichten aus
interner Status:
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)
In diesem Beispiel wird nur das Laden von FirstFragment
behandelt. Auch die Umstellung auf
Durch SecondFragment
werden die Logeinträge erheblich erhöht.
Viele Protokollmeldungen der VERBOSE
-Ebene sind für App-Kampagnen wenig nützlich.
zu entwickeln. Zu sehen, wann Änderungen am Back-Stack auftreten, kann jedoch
um Fehler zu beheben.
StrictMode für Fragmente
Version 1.4.0 und höher der
Bibliothek des Jetpack-Fragments
StrictMode für Fragmente. Dabei werden einige häufige Probleme erkannt,
sich auf unerwartete Weise verhalten. Weitere Informationen zur Arbeit mit
StrictMode
, siehe StrictMode.
Ein benutzerdefinierter
Policy
definiert, welche Verstöße erkannt werden und welche Strafe angewendet wird
wenn Verstöße festgestellt werden.
Um eine benutzerdefinierte StrictMode-Richtlinie anzuwenden, weisen Sie sie der
FragmentManager
Tun Sie dies so früh wie möglich. In diesem Fall erfolgt dies in einer
init
-Block oder im Java-Konstruktor:
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()); ... } }
Wenn Sie die Context
kennen müssen, um zu entscheiden,
StrictMode aktivieren, z. B. über den Wert einer booleschen Ressource, können Sie
die Zuweisung einer StrictMode-Richtlinie zum FragmentManager
mithilfe eines
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()); ... } }
Der letzte Punkt, ab dem Sie StrictMode so konfigurieren können, dass alle möglichen
Verstöße liegt in
onCreate()
,
vor dem Aufruf von 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()); ... } }
Die in diesen Beispielen verwendete Richtlinie erkennt nur Verstöße gegen die Wiederverwendung von Fragmenten,
und die App wird bei jedem Auftreten beendet. penaltyDeath()
kann sein
da er schnell genug ausfällt und Sie
nicht ignorieren können,
Verstöße.
Es ist auch möglich, bestimmte Verstöße selektiv zuzulassen. Die Richtlinie, die in Das vorherige Beispiel erzwingt diesen Verstoß jedoch für alle anderen Fragmente, Typen. Dies ist nützlich, wenn die Bibliothekskomponente eines Drittanbieters StrictMode-Verstöße enthalten.
In solchen Fällen können Sie die Verstöße vorübergehend der Zulassungsliste der den StrictMode für Komponenten, deren Eigentümer Sie nicht sind, bis die Bibliothek behebt den Verstoß.
Weitere Informationen zum Konfigurieren anderer Verstöße finden Sie in der Dokumentation zu
FragmentStrictMode.Policy.Builder
Es gibt drei Strafarten.
penaltyLog()
gibt Details zu Verstößen an Logcat aus.penaltyDeath()
Die App wird beendet, wenn Verstöße festgestellt werden.penaltyListener()
können Sie einen benutzerdefinierten Listener hinzufügen, der immer dann aufgerufen wird, wenn Verstöße auftreten. erkannt.
In deinem Policy
kannst du eine beliebige Kombination von Strafen verhängen. Wenn in Ihrer Richtlinie
keine Strafe explizit festgelegt ist, wird der Standardwert penaltyLog()
angewendet. Wenn Sie
eine andere Strafe als penaltyLog()
in deinem benutzerdefinierten Policy
anwenden, dann
penaltyLog()
ist deaktiviert, es sei denn, Sie legen es explizit fest.
penaltyListener()
kann nützlich sein, wenn Sie die Logging-Bibliothek eines Drittanbieters nutzen,
zum Protokollieren von Verstößen. Alternativ können Sie auch
nicht schwerwiegenden Verstoß in Release-Builds erkennen und in einem Crash Reporting erfassen
Bibliothek. Mit dieser Strategie können Verstöße erkannt werden, die andernfalls übersehen wurden.
Legen Sie zum Festlegen einer globalen StrictMode-Richtlinie eine Standardrichtlinie fest, die für alle
FragmentManager
-Instanzen mit dem
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()); } }
In den folgenden Abschnitten werden die Arten von Verstößen und mögliche Behelfslösungen beschrieben.
Wiederverwendung von Fragmenten
Der Verstoß gegen die Wiederverwendung von Fragmenten ist aktiviert mithilfe von
detectFragmentReuse()
und wirft eine
FragmentReuseViolation
Dieser Verstoß gibt die Wiederverwendung einer Fragment
-Instanz nach ihrer Entfernung an
von FragmentManager
. Diese Wiederverwendung kann zu Problemen führen, da das Fragment
behalten den Zustand aus der vorherigen Verwendung bei und verhalten sich nicht konsistent. Wenn Sie eine
neuen Instanz hinzugefügt, befindet sie sich immer im ursprünglichen Zustand, wenn sie
FragmentManager
Verwendung des Fragment-Tags
Der Verstoß gegen die Nutzung des Fragment-Tags wird aktiviert mithilfe von
detectFragmentTagUsage()
und wirft eine
FragmentTagUsageViolation
Dieser Verstoß weist darauf hin, dass ein Fragment
mithilfe von <fragment>
überhöht ist.
-Tag in einem XML-Layout. Um das Problem zu beheben, blähe Fragment
im Inneren auf
<androidx.fragment.app.FragmentContainerView>
statt im <fragment>
Tag. Mit einem FragmentContainerView
aufgeblähte Fragmente können
Fragment
-Transaktionen und Konfigurationsänderungen. Diese funktionieren möglicherweise nicht
zu erwarten, wenn Sie stattdessen das <fragment>
-Tag verwenden.
Instanznutzung beibehalten
Der Verstoß zur Beibehaltung der Instanznutzung ist aktiviert mithilfe von
detectRetainInstanceUsage()
und wirft eine
RetainInstanceUsageViolation
Dieser Verstoß gibt die Verwendung einer beibehaltenen Fragment
an, insbesondere in folgenden Fällen:
gibt es Anrufe bei
setRetainInstance()
oder
getRetainInstance()
,
die beide veraltet sind.
Anstatt diese Methoden zum Verwalten aufbewahrter Fragment
Instanzen zu verwenden
speichern Sie Status in einem
ViewModel
die dies für Sie übernimmt.
Für Nutzer sichtbaren Hinweis festlegen
Der Verstoß gegen die Richtlinie zum Festlegen von sichtbaren Hinweisen für Nutzer wird aktiviert mithilfe von
detectSetUserVisibleHint()
und wirft eine
SetUserVisibleHintViolation
Dieser Verstoß weist auf einen Aufruf an
setUserVisibleHint()
,
das veraltet ist.
Wenn Sie diese Methode manuell aufrufen, rufen Sie
setMaxLifecycle()
. Wenn Sie diese Methode überschreiben,
onResume()
beim Übergeben von true
und
onPause()
wenn false
übergeben wird.
Zielfragmentierung
Der Zielverstoß bei der Fragmentnutzung wird mithilfe von
detectTargetFragmentUsage()
und wirft eine
TargetFragmentUsageViolation
Dieser Verstoß weist auf einen Aufruf an
setTargetFragment()
,
getTargetFragment()
,
oder getTargetRequestCode()
,
die alle veraltet sind. Anstatt diese Methoden zu verwenden, registrieren Sie
FragmentResultListener
Weitere Informationen zum Übergeben von Ergebnissen finden Sie unter Ergebnisse übergeben zwischen
Fragmente.
Falscher Fragmentcontainer
Der falsche Fragment-Containerverstoß wird mit aktiviert.
detectWrongFragmentContainer()
und wirft eine
WrongFragmentContainerViolation
Dieser Verstoß weist auf das Hinzufügen von Fragment
zu einem anderen Container als dem
FragmentContainerView
. Wie bei der Verwendung des Fragment
-Tags gilt:
fragmentierte Transaktionen funktionieren möglicherweise nicht wie erwartet, sofern sie nicht in einem
FragmentContainerView
. Mit einer Containeransicht lässt sich auch ein Problem in der View
API beheben, das
führt dazu, dass Fragmente, die Exit-Animationen verwenden, über alle anderen Elemente gezeichnet werden.
Fragmenten.