Fragmente debuggen

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 ist NavHostFragment.
  • 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.