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.