Eseguire il debug dei tuoi frammenti

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

Logging di FragmentManager

FragmentManager può emettere vari messaggi in Logcat. L'opzione è disabilitata per impostazione predefinita, ma a volte questi messaggi di log possono aiutarti a risolvere i problemi relativi ai frammenti. FragmentManager emette l'output più significativo a livello di log di DEBUG e VERBOSE.

Puoi abilitare il logging utilizzando il seguente comando adb shell:

adb shell setprop log.tag.FragmentManager DEBUG

In alternativa, puoi attivare la registrazione dettagliata come segue:

adb shell setprop log.tag.FragmentManager VERBOSE

Se abiliti il logging dettagliato, puoi applicare un filtro a livello di log nella finestra Logcat. Tuttavia, in questo modo vengono filtrati tutti i log, non solo i log FragmentManager. In genere è preferibile abilitare il logging di FragmentManager solo al livello di log necessario.

Logging di DEBUG

A livello di DEBUG, FragmentManager generalmente emette messaggi di log relativi alle variazioni dello stato del ciclo di vita. Ogni voce di log contiene il dump toString() del Fragment. Una voce di log è costituita dalle seguenti informazioni:

  • Il nome semplice della classe dell'istanza Fragment.
  • Il codice hash di identità dell'istanza Fragment.
  • L'ID univoco del gestore di frammenti dell'istanza Fragment. Questo è stabile nelle modifiche alla configurazione e nei processi di interruzione e ricreazione.
  • L'ID del contenitore a cui viene aggiunto Fragment, ma solo se impostato.
  • Il tag Fragment, ma solo se impostato.

Di seguito è riportata una voce di log DEBUG di esempio:

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

Per brevità e leggibilità, gli UUID sono accorciati nei seguenti esempi.

Ecco un elemento NavHostFragment in fase di inizializzazione, quindi la creazione di startDestination Fragment di tipo FirstFragment e la transizione allo 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 utente, FirstFragment esce dai vari stati del ciclo di vita. Viene quindi creata un'istanza di SecondFragment e si passa 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 sono suffissi da un identificatore in modo da poter monitorare diverse istanze della stessa classe Fragment.

Logging VERBOSE

A livello di VERBOSE, FragmentManager generalmente emette messaggi di log sul suo 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. L'inclusione della transizione a SecondFragment aumenta notevolmente le voci di log. Molti dei messaggi di log a livello di VERBOSE sono poco utili per gli sviluppatori di app. Tuttavia, vedere quando si verificano modifiche allo stack di back-forward può essere utile per il debug di alcuni problemi.

StrictMode per frammenti

La versione 1.4.0 e successive della libreria Jetpack Fragment include StrictMode per i frammenti. Può rilevare alcuni problemi comuni che potrebbero causare un comportamento imprevisto della tua app. Per ulteriori informazioni sull'utilizzo di StrictMode, consulta StrictMode.

Un elemento Policy personalizzato definisce quali violazioni vengono rilevate e specifica quale sanzione viene applicata quando vengono rilevate le violazioni.

Per applicare una norma StrictMode personalizzata, assegnala a FragmentManager. Esegui questa operazione il prima possibile. In questo caso, devi eseguire l'operazione in un blocco 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());
        ...
   }
}

Per i casi in cui devi conoscere Context per determinare se abilitare StrictMode, ad esempio dal valore di una risorsa booleana, puoi rimandare l'assegnazione di un criterio StrictMode a FragmentManager utilizzando 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 rilevare tutte le possibili violazioni si trova 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());
        ...
   }
}

Questo criterio utilizzato in questi esempi rileva solo le violazioni relative al riutilizzo dei frammenti e l'app viene chiusa ogni volta che si verificano. penaltyDeath() può essere utile nelle build di debug perché l'operazione non riesce in modo abbastanza rapido da non essere possibile ignorare le violazioni.

È anche possibile consentire in modo selettivo determinate violazioni. Il criterio utilizzato nell'esempio precedente, tuttavia, applica questa violazione per tutti gli altri tipi di frammenti. Questa opzione è utile nei casi in cui un componente di libreria di terze parti potrebbe contenere violazioni StrictMode.

In questi casi, puoi aggiungere temporaneamente le violazioni alla lista consentita dell'impostazione StrictMode per i componenti che non sono di tua proprietà finché la libreria non risolve la violazione.

Per maggiori dettagli su come configurare altre violazioni, consulta la documentazione relativa a FragmentStrictMode.Policy.Builder.

Esistono tre tipi di sanzioni.

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

Puoi applicare qualsiasi combinazione di sanzioni nel tuo Policy. Se il tuo criterio non specifica esplicitamente una sanzione, il valore predefinito è penaltyLog(). Se applichi una sanzione diversa da penaltyLog() nella Policy personalizzata, penaltyLog() viene disattivato, a meno che non la imposti esplicitamente.

penaltyListener() può essere utile se hai una libreria di logging di terze parti in cui vuoi registrare le violazioni. In alternativa, potresti voler abilitare il rilevamento delle violazioni non irreversibili nelle build di release e registrarle in una libreria di segnalazione degli arresti anomali. Questa strategia può rilevare violazioni che sono altrimenti sfuggite.

Per impostare un criterio StrictMode globale, imposta un criterio predefinito che si applichi a tutte le istanze di FragmentManager utilizzando il metodo 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 per il 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'utilizzo precedente e non comportarsi in modo coerente. Se crei una nuova istanza ogni volta, questa sarà sempre nello stato iniziale quando la aggiungi a FragmentManager.

Utilizzo tag frammento

La violazione dell'utilizzo dei tag di frammento viene attivata utilizzando detectFragmentTagUsage() e genera un FragmentTagUsageViolation.

Questa violazione indica che un valore Fragment viene aumentato in modo artificioso utilizzando il tag <fragment> in un layout XML. Per risolvere questo problema, gonfia Fragment all'interno di <androidx.fragment.app.FragmentContainerView> anziché nel tag <fragment>. I frammenti gonfiati utilizzando un FragmentContainerView possono gestire in modo affidabile le transazioni e le modifiche alla configurazione di Fragment. Potrebbero non funzionare come previsto se utilizzi il tag <fragment>.

Conserva l'utilizzo delle istanze

La violazione dell'utilizzo della conservazione delle istanze è abilitata utilizzando detectRetainInstanceUsage() e genera un RetainInstanceUsageViolation.

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

Anziché utilizzare questi metodi per gestire autonomamente le istanze Fragment conservate, archivia lo stato in un elemento ViewModel che gestisce questa operazione per te.

Imposta suggerimento visibile all'utente

La violazione del suggerimento visibile dall'utente impostata viene attivata utilizzando detectSetUserVisibleHint() e genera un SetUserVisibleHintViolation.

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

Se chiami manualmente questo metodo, chiama invece setMaxLifecycle(). Se esegui l'override di questo metodo, sposta il comportamento in onResume() durante la trasmissione di true e onPause() durante la trasmissione di false.

Utilizzo frammento di destinazione

La violazione dell'utilizzo dei frammenti target è abilitata utilizzando detectTargetFragmentUsage() e genera un TargetFragmentUsageViolation.

Questa violazione indica una chiamata a setTargetFragment(), getTargetFragment() o getTargetRequestCode(), tutte deprecate. Anziché utilizzare questi metodi, registra un FragmentResultListener. Per ulteriori informazioni sul passaggio dei risultati, consulta Trasmettere risultati tra frammenti.

Contenitore frammento errato

La violazione errata del contenitore dei frammenti viene attivata utilizzando detectWrongFragmentContainer() e genera un WrongFragmentContainerViolation.

Questa violazione indica l'aggiunta di un elemento Fragment a un contenitore diverso da FragmentContainerView. Come per l'utilizzo dei tag Fragment, le transazioni con frammenti potrebbero non funzionare come previsto, a meno che non siano ospitate all'interno di un FragmentContainerView. L'utilizzo di una vista container consente anche di risolvere un problema nell'API View che causa il tracciamento di frammenti che utilizzano animazioni di uscita sopra tutti gli altri frammenti.