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.