Este guia aborda ferramentas que podem ser usadas para depurar fragmentos.
Gerar registros do FragmentManager
O FragmentManager
pode emitir várias mensagens para o
Logcat. Esse recurso fica desativado por padrão,
mas às vezes essas mensagens de registro podem ajudar a resolver
problemas com os fragmentos. O FragmentManager
emite a saída mais significativa
nos níveis de registro DEBUG
e VERBOSE
.
É possível ativar a geração de registros usando o seguinte
comando adb shell
:
adb shell setprop log.tag.FragmentManager DEBUG
Você também pode ativar o registro detalhado desta forma:
adb shell setprop log.tag.FragmentManager VERBOSE
Se você ativar o registro detalhado, poderá aplicar um filtro de nível
de registro na janela do Logcat. No entanto, isso
filtra todos os registros, não apenas aqueles do FragmentManager
. Geralmente, é melhor
ativar a geração de registros do FragmentManager
apenas no nível de registro necessário.
Gerar registros DEBUG
No nível DEBUG
, o FragmentManager
geralmente emite mensagens de registro relacionadas a
mudanças no estado do ciclo de vida. Cada entrada de registro contém o despejo
toString()
do Fragment
.
Uma entrada de registro consiste nas seguintes informações:
- O nome de classe simples da instância do
Fragment
. - O código hash de identidade
da instância do
Fragment
. - O ID exclusivo do gerenciador de fragmentos da instância do
Fragment
. Ele é estável entre as mudanças de configuração e o encerramento e recriação de processos. - O ID do contêiner em que o
Fragment
é adicionado, mas apenas se definido. - A tag do
Fragment
, mas apenas se definida.
Confira a seguir um exemplo de entrada de registro DEBUG
:
D/FragmentManager: moveto ATTACHED: NavHostFragment{92d8f1d} (fd92599e-c349-4660-b2d6-0ece9ec72f7b id=0x7f080116)
- A classe do
Fragment
éNavHostFragment
. - O código hash de identidade é
92d8f1d
. - O ID exclusivo é
fd92599e-c349-4660-b2d6-0ece9ec72f7b
. - O ID do contêiner é
0x7f080116
. - A tag foi omitida porque nenhuma foi definida. Quando presente, ela segue
o ID no formato
tag=tag_value
.
Para simplificar e facilitar a leitura, os UUIDs são encurtados nos exemplos a seguir.
Confira uma classe NavHostFragment
sendo inicializada e, em seguida, o startDestination
Fragment
do tipo FirstFragment
sendo criado e fazendo a transição para
o estado 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)
Após uma interação do usuário, o FirstFragment
sai dos
vários estados do ciclo de vida. Em seguida, o SecondFragment
é instanciado e faz a transição
para o estado 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)
Todas as instâncias do Fragment
são sufixadas por um identificador para que você possa rastrear
instâncias diferentes da
mesma classe do Fragment
.
Gerar registros VERBOSE
No nível VERBOSE
, o FragmentManager
geralmente emite mensagens de registro sobre o próprio
estado 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)
Esse exemplo abrange apenas o carregamento no FirstFragment
. Incluir a transição para
SecondFragment
aumenta consideravelmente as entradas de registro.
Muitas das mensagens de registro de nível VERBOSE
são pouco úteis para desenvolvedores
de apps. No entanto, verificar quando ocorrem as mudanças na backstack pode ajudar a
depurar alguns problemas.
StrictMode para fragmentos
As versões 1.4.0 e mais recentes da
biblioteca Jetpack Fragment incluem
o StrictMode para fragmentos. Ele detecta alguns problemas comuns que podem fazer com que o
app se comporte de maneiras inesperadas. Para mais informações sobre como trabalhar com o
StrictMode
, consulte a página dele.
Uma
Policy
personalizada define quais violações são detectadas e especifica qual penalidade é aplicada
quando a detecção ocorre.
Para aplicar uma política StrictMode personalizada, é necessário que ela seja atribuída ao
FragmentManager
.
Faça a atribuição o quanto antes. Nesse caso, isso é feito em um
bloco init
ou no construtor 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()); ... } }
Nos casos em que você precisa conhecer o Context
para determinar se
o StrictMode será ou não ativado, por exemplo, com o valor de um recurso booleano, é possível
adiar a atribuição de uma política StrictMode para o FragmentManager
usando um 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()); ... } }
O ponto mais recente em que é possível configurar o StrictMode para captar todas as violações
possíveis está no
onCreate()
,
antes da chamada para 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()); ... } }
A política usada nesses exemplos detecta apenas violações de reutilização de fragmento,
e o app é encerrado sempre que elas ocorrem. O penaltyDeath()
pode ser
útil em builds de depuração porque ele falha rápido o suficiente para que você não
ignore violações.
Também é possível autorizar seletivamente violações específicas. No entanto, a política usada no exemplo anterior aplica essa violação a todos os outros tipos de fragmento. Isso é útil nos casos em que um componente de biblioteca de terceiros pode conter violações do StrictMode.
Nesses casos, você pode adicionar temporariamente essas violações à lista de permissões do StrictMode para componentes que não pertencem a você até que a biblioteca corrija a violação.
Para saber como configurar outras violações, consulte a documentação de
FragmentStrictMode.Policy.Builder
.
Há três tipos de penalidade.
penaltyLog()
despeja detalhes de violações no Logcat.penaltyDeath()
encerra o app quando as violações são detectadas.penaltyListener()
permite adicionar um listener personalizado que é chamado sempre que violações são detectadas.
Você pode aplicar qualquer combinação de penalidades na sua Policy
. Se a política não
especificar explicitamente uma penalidade, um padrão penaltyLog()
será aplicado. Se você
aplicar uma penalidade diferente de penaltyLog()
na sua Policy
personalizada,
o penaltyLog()
será desativado, a menos que você o defina explicitamente.
O penaltyListener()
pode ser útil quando você tem uma biblioteca de registros de terceiros na
qual quer registrar violações. Como alternativa, você pode ativar
a detecção de violações não fatais em builds de lançamento e registrá-las em uma biblioteca de relatórios
de erros. Essa estratégia pode detectar violações que seriam perdidas.
Para definir uma política global StrictMode, defina uma política padrão que se aplique a todas as
instâncias do FragmentManager
usando o
método
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()); } }
As seções a seguir descrevem os tipos de violação e possíveis soluções.
Reutilizar fragmentos
A violação de reutilização de fragmento é ativada usando
detectFragmentReuse()
e gera uma
FragmentReuseViolation
.
Essa violação indica a reutilização de uma instância do Fragment
após a remoção dele
do FragmentManager
. Essa reutilização pode causar problemas, porque o Fragment
pode
reter o estado do uso anterior e não se comportar de forma consistente. Se você criar uma
nova instância todas as vezes, ela estará sempre no estado inicial quando adicionada ao
FragmentManager
.
Uso de tags de fragmento
A violação de uso da tag de fragmento é ativada usando
detectFragmentTagUsage()
e gera uma
FragmentTagUsageViolation
.
Essa violação indica que um Fragment
é inflado usando a tag <fragment>
em um layout XML. Para resolver isso, infle seu Fragment
dentro de
<androidx.fragment.app.FragmentContainerView>
em vez de usar a tag
<fragment>
. Fragmentos inflados com uma FragmentContainerView
lidam com
transações de Fragment
e mudanças de configuração de maneira confiável. Eles podem não funcionar como
esperado se você usar a tag <fragment>
.
Uso da instância de retenção
A violação de uso da instância de retenção é ativada com
detectRetainInstanceUsage()
e gera uma
RetainInstanceUsageViolation
.
Essa violação indica o uso de um Fragment
retido, principalmente se
houver chamadas para
setRetainInstance()
ou
getRetainInstance()
,
ambos descontinuados.
Em vez de usar esses métodos para gerenciar instâncias Fragment
retidas
por conta própria, armazene o estado em um
ViewModel
que faça isso para você.
Definir dica visível para o usuário
A violação de definição de uma dica visível ao usuário é ativada usando
detectSetUserVisibleHint()
e gera uma
SetUserVisibleHintViolation
.
Essa violação indica uma chamada para
setUserVisibleHint()
,
que foi descontinuado.
Se você estiver chamando esse método manualmente, chame
setMaxLifecycle()
. Se você substituir esse método, mova o comportamento para
onResume()
ao transmitir true
e
onPause()
ao transmitir false
.
Uso do fragmento de destino
A violação de uso do fragmento de destino é ativada usando
detectTargetFragmentUsage()
e gera uma
TargetFragmentUsageViolation
.
Essa violação indica uma chamada para
setTargetFragment()
,
getTargetFragment()
ou getTargetRequestCode()
,
que foram descontinuados. Em vez de usar esses métodos, registre um
FragmentResultListener
. Para mais informações sobre a transmissão de resultados, consulte Transmitir resultados entre
fragmentos.
Contêiner de fragmentos incorreto
A violação de contêiner de fragmento incorreto é ativada usando
detectWrongFragmentContainer()
e gera uma
WrongFragmentContainerViolation
.
Essa violação indica a adição de um Fragment
a um contêiner diferente de
FragmentContainerView
. Assim como no uso de tags Fragment
,
as transações de fragmentos podem não funcionar como esperado, a menos que hospedadas em um
FragmentContainerView
. Usar uma visualização de contêiner também ajuda a resolver um problema na API View
que
faz com que fragmentos que usam animações de saída sejam desenhados sobre todos os
outros.