Eseguire il debug dei tuoi frammenti

Questa guida illustra gli strumenti che puoi utilizzare per eseguire il debug frammenti.

Logging FragmentManager

FragmentManager possono inviare diversi messaggi Logcat. Questa opzione è disattivata per impostazione predefinita, ma a volte questi messaggi di log possono aiutarti problemi con i tuoi frammenti. FragmentManager emette l'output più significativo a livello di log DEBUG e VERBOSE.

Puoi abilitare il logging utilizzando quanto segue: Comando adb shell:

adb shell setprop log.tag.FragmentManager DEBUG

In alternativa, puoi attivare il logging dettagliato nel seguente modo:

adb shell setprop log.tag.FragmentManager VERBOSE

Se abiliti il logging dettagliato, puoi applicare un livello di log nella finestra Logcat. Tuttavia, questo filtra tutti i log, non solo i log FragmentManager. Di solito è meglio abilita il logging di FragmentManager solo al livello di log necessario.

Logging DEBUG

A livello di DEBUG, FragmentManager generalmente emette messaggi di log relativi a modifiche allo stato del ciclo di vita. Ogni voce di log contiene il parametro toString() eseguire il dump da Fragment. Una voce di log è costituita dalle seguenti informazioni:

  • Il nome semplice della classe dell'istanza Fragment.
  • Il codice hash dell'identità dell'istanza Fragment.
  • ID univoco del gestore di frammenti dell'istanza Fragment. È stabile attraverso le modifiche alla configurazione ed elaborano la morte e la ricreazione.
  • L'ID del container a cui viene aggiunto Fragment, ma solo se impostato.
  • Il tag Fragment, ma solo se impostato.

Di seguito è riportato un esempio di voce di log DEBUG:

D/FragmentManager: moveto ATTACHED: NavHostFragment{92d8f1d} (fd92599e-c349-4660-b2d6-0ece9ec72f7b id=0x7f080116)
  • Il corso Fragment è NavHostFragment.
  • Il codice hash dell'identità è 92d8f1d.
  • L'ID univoco è fd92599e-c349-4660-b2d6-0ece9ec72f7b.
  • L'ID contenitore è 0x7f080116.
  • Il tag è omesso perché non ne è stato impostato nessuno. Se presente, segue l'ID nel formato tag=tag_value.

Per brevità e leggibilità, gli UUID vengono abbreviati nel seguente modo: esempi.

Ecco un NavHostFragment in fase di inizializzazione e poi startDestination Fragment di tipo FirstFragment in fase di creazione e transizione a lo stato 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)

A seguito di un'interazione dell'utente, FirstFragment esce dalla fase di transizione stati del ciclo di vita. Viene quindi creata un'istanza di SecondFragment e le transizioni fino allo stato 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)

Tutte le istanze Fragment hanno come suffisso un identificatore per consentirti di monitorare diverse istanze stesso corso Fragment.

Logging VERBOSE

A livello di VERBOSE, FragmentManager generalmente emette messaggi di log relativi stato interno:

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)

Questo esempio riguarda solo il caricamento su FirstFragment. Anche la transizione SecondFragment aumenta notevolmente le voci di log. Molti dei messaggi di log a livello di VERBOSE non sono utili all'app sviluppatori. Tuttavia, vedere quando vengono apportate modifiche al back stack può essere utile il debug di alcuni problemi.

StrictMode per i frammenti

Versione 1.4.0 e successive del La libreria Jetpack Fragment include StrictMode per i frammenti. Può rilevare alcuni problemi comuni che potrebbero causare per comportarsi in modi inaspettati. Per ulteriori informazioni sull'utilizzo di StrictMode, consulta StrictMode.

Un segmento di pubblico personalizzato Policy definisce quali violazioni vengono rilevate e specifica la penalità da applicare quando vengono rilevate violazioni.

Per applicare un criterio StrictMode personalizzato, assegnalo al FragmentManager Esegui questa operazione il prima possibile. In questo caso, lo fai in modo init o nel costruttore 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());
        ...
   }
}

Nei casi in cui è necessario conoscere il Context per determinare se abilitare StrictMode, ad esempio dal valore di una risorsa booleana, rimandare l'assegnazione di un criterio StrictMode a FragmentManager utilizzando un 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());
        ...
   }
}

L'ultimo punto in cui puoi configurare StrictMode per catturare tutto il possibile violazioni sono in onCreate(), prima della chiamata a 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());
        ...
   }
}

Il criterio usato in questi esempi rileva solo le violazioni del riutilizzo dei frammenti, e l'app si interrompe ogni volta che si verifica. penaltyDeath() può essere utile per le build di debug perché non riesce abbastanza rapidamente da non poter più ignorare violazioni delle norme.

È anche possibile consentire selettivamente determinate violazioni. Il criterio utilizzato in l'esempio precedente, tuttavia, applica questa violazione per tutti gli altri frammenti di testo. Ciò è utile nei casi in cui un componente di librerie di terze parti potrebbe contengono violazioni di StrictMode.

In questi casi, puoi aggiungere temporaneamente queste violazioni alla lista consentita di il tuo StrictMode per i componenti che non sono di tua proprietà fino a quando corregge la violazione.

Per informazioni dettagliate su come configurare altre violazioni, consulta la documentazione per FragmentStrictMode.Policy.Builder

Esistono tre tipi di penalità.

  • penaltyLog() invia i dettagli delle violazioni a Logcat.
  • penaltyDeath() chiude l'app quando vengono rilevate violazioni.
  • penaltyListener() ti consente di aggiungere un listener personalizzato che viene chiamato ogni volta che vengono rilevate violazioni rilevato.

Puoi applicare qualsiasi combinazione di penalità in Policy. Se le tue norme non specifichi esplicitamente una penalità, viene applicato il valore predefinito di penaltyLog(). Se Applica una penale diversa da penaltyLog() nel tuo Policy personalizzato, poi penaltyLog() è disattivato a meno che non lo imposti esplicitamente.

penaltyListener() può essere utile quando hai una libreria di logging di terze parti per di cui vuoi registrare le violazioni. In alternativa, potresti voler attivare violazione non irreversibile rilevata nelle build di release e registrarle in un report sugli arresti anomali libreria. Questa strategia può rilevare violazioni altrimenti non rilevate.

Per impostare un criterio StrictMode globale, imposta un criterio predefinito che si applichi a tutte FragmentManager utilizzando 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());
    }
}

Le seguenti sezioni descrivono i tipi di violazioni e le possibili soluzioni alternative.

Riutilizzo dei frammenti

La violazione relativa al riutilizzo dei frammenti viene abilitata utilizzando detectFragmentReuse() e genera un FragmentReuseViolation.

Questa violazione indica il riutilizzo di un'istanza Fragment dopo la sua rimozione da FragmentManager. Questo riutilizzo può causare problemi perché Fragment potrebbe mantenere lo stato dell'uso precedente e non comportarsi in modo coerente. Se crei un nuova istanza ogni volta, è sempre nello stato iniziale quando viene aggiunta FragmentManager.

Utilizzo del tag di frammenti

La violazione dell'utilizzo del tag del frammento viene abilitata utilizzando detectFragmentTagUsage() e genera un FragmentTagUsageViolation.

Questa violazione indica che un Fragment viene gonfiato utilizzando <fragment> in un layout XML. Per risolvere il problema, gonfia il tuo Fragment all'interno <androidx.fragment.app.FragmentContainerView> anziché in <fragment> del tag. I frammenti gonfiati con un FragmentContainerView possono gestire in modo affidabile Fragment transazioni e modifiche alla configurazione. Potrebbero non funzionare come previsto se invece utilizzassi il tag <fragment>.

Conserva l'utilizzo dell'istanza

La violazione dell'utilizzo dell'istanza di conservazione viene abilitata mediante detectRetainInstanceUsage() e genera un RetainInstanceUsageViolation.

Questa violazione indica l'utilizzo di un elemento Fragment conservato, in particolare se ci sono chiamate a setRetainInstance() o getRetainInstance(), che sono entrambi deprecati.

Anziché utilizzare questi metodi per gestire le istanze Fragment conservate di te, archiviali in un ViewModel che gestisce questa situazione per te.

Imposta suggerimento visibile all'utente

La violazione dei suggerimenti visibili all'utente impostata viene attivata utilizzando detectSetUserVisibleHint() e genera un SetUserVisibleHintViolation.

Questa violazione indica una chiamata a setUserVisibleHint(), che è deprecato.

Se chiami manualmente questo metodo, richiama setMaxLifecycle() . Se sostituisci questo metodo, sposta il comportamento in onResume() quando superi true e onPause() quando viene superato con false.

Utilizzo dei frammenti di destinazione

La violazione dell'utilizzo dei frammenti di destinazione viene attivata utilizzando detectTargetFragmentUsage() e genera un TargetFragmentUsageViolation.

Questa violazione indica una chiamata a setTargetFragment(), getTargetFragment(), o getTargetRequestCode(), che sono tutti deprecati. Anziché utilizzare questi metodi, registra un FragmentResultListener. Per ulteriori informazioni sulla trasmissione dei risultati, consulta l'articolo Passare risultati tra di grandi dimensioni.

Container dei frammenti errato

È stata abilitata la violazione errata del container dei frammenti utilizzando detectWrongFragmentContainer() e genera un WrongFragmentContainerViolation.

Questa violazione indica l'aggiunta di un Fragment a un container diverso da FragmentContainerView. Come per l'utilizzo dei tag Fragment, le transazioni di frammento potrebbero non funzionare come previsto a meno che non siano ospitate all'interno di un FragmentContainerView. L'utilizzo di una vista container aiuta anche a risolvere un problema nell'API View che i frammenti che utilizzano animazioni di uscita vengono disegnati sopra tutte le altre di grandi dimensioni.