В этом руководстве рассматриваются инструменты, которые можно использовать для отладки фрагментов .
Ведение журнала FragmentManager
FragmentManager
может отправлять в Logcat различные сообщения. По умолчанию это отключено, но иногда эти сообщения журнала могут помочь вам устранить проблемы с вашими фрагментами. FragmentManager
выдает наиболее значимые выходные данные на уровнях журнала DEBUG
и VERBOSE
.
Вы можете включить ведение журнала, используя следующую команду adb shell
:
adb shell setprop log.tag.FragmentManager DEBUG
Альтернативно вы можете включить подробное ведение журнала следующим образом:
adb shell setprop log.tag.FragmentManager VERBOSE
Если вы включите подробное ведение журнала, вы сможете применить фильтр уровня журнала в окне Logcat. Однако при этом фильтруются все журналы, а не только журналы FragmentManager
. Обычно лучше включать ведение журнала FragmentManager
только на том уровне журнала, который вам нужен.
ведение журнала отладки
На уровне DEBUG
FragmentManager
обычно выдает сообщения журнала, относящиеся к изменениям состояния жизненного цикла. Каждая запись журнала содержит дамп toString()
из Fragment
. Запись журнала состоит из следующей информации:
- Простое имя класса экземпляра
Fragment
. - Идентификатор хеш-кода экземпляра
Fragment
. - Уникальный идентификатор экземпляра фрагмента диспетчера
Fragment
. Это стабильно при изменении конфигурации, а также при смерти и восстановлении процесса. - Идентификатор контейнера, в который добавляется
Fragment
, но только если он установлен. - Тег
Fragment
, но только если он установлен.
Ниже приведен пример записи журнала DEBUG
:
D/FragmentManager: moveto ATTACHED: NavHostFragment{92d8f1d} (fd92599e-c349-4660-b2d6-0ece9ec72f7b id=0x7f080116)
- Класс
Fragment
—NavHostFragment
. - Идентификационный хеш-код —
92d8f1d
. - Уникальный идентификатор:
fd92599e-c349-4660-b2d6-0ece9ec72f7b
. - Идентификатор контейнера —
0x7f080116
. - Тег опущен, поскольку он не был установлен. Если он присутствует, он следует за идентификатором в формате
tag=tag_value
.
Для краткости и удобства чтения UUID в следующих примерах сокращены.
Вот инициализируется NavHostFragment
, а затем создается Fragment
startDestination
типа FirstFragment
и переходит в состояние 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)
После взаимодействия с пользователем FirstFragment
выходит из различных состояний жизненного цикла. Затем создается экземпляр SecondFragment
и он переходит в состояние 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)
Все экземпляры Fragment
имеют суффикс идентификатора, чтобы вы могли отслеживать разные экземпляры одного и того же класса Fragment
.
ПОДРОБНОЕ ведение журнала
На уровне VERBOSE
FragmentManager
обычно выдает сообщения журнала о своем внутреннем состоянии:
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)
В этом примере рассматривается только загрузка FirstFragment
. Включение перехода на SecondFragment
значительно увеличивает количество записей в журнале. Многие из сообщений журнала уровня VERBOSE
малопригодны разработчикам приложений. Однако наблюдение за тем, когда происходят изменения в обратном стеке, может помочь в отладке некоторых проблем.
StrictMode для фрагментов
Версия 1.4.0 и выше библиотеки Jetpack Fragment включает StrictMode для фрагментов. Он может обнаружить некоторые распространенные проблемы, которые могут привести к неожиданному поведению вашего приложения. Дополнительные сведения о работе со StrictMode
см. в разделе StrictMode .
Пользовательская Policy
определяет, какие нарушения обнаруживаются, и определяет, какое наказание применяется при обнаружении нарушений.
Чтобы применить пользовательскую политику StrictMode, назначьте ее FragmentManager
. Сделайте это как можно раньше. В этом случае вы делаете это в блоке init
или в конструкторе Java:
Котлин
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) ... } }
Ява
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()); ... } }
В случаях, когда вам необходимо знать Context
, чтобы определить, следует ли включать StrictMode, например, по значению логического ресурса, вы можете отложить назначение политики StrictMode для FragmentManager
с помощью OnContextAvailableListener
:
Котлин
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) ... } }
Ява
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()); ... } }
Последний момент, когда вы можете настроить StrictMode для обнаружения всех возможных нарушений, — это onCreate()
, перед вызовом super.onCreate()
:
Котлин
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) ... } }
Ява
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()); ... } }
Эта политика, используемая в этих примерах, обнаруживает только нарушения повторного использования фрагментов, и приложение завершает работу всякий раз, когда они происходят. penaltyDeath()
может быть полезен в отладочных сборках, поскольку он дает сбой достаточно быстро, и вы не можете игнорировать нарушения.
Также возможно выборочно допускать определенные нарушения. Однако политика, использованная в предыдущем примере, применяет это нарушение для всех других типов фрагментов. Это полезно в случаях, когда компонент сторонней библиотеки может содержать нарушения StrictMode.
В таких случаях вы можете временно добавить эти нарушения в список разрешений вашего StrictMode для компонентов, которыми вы не владеете, пока библиотека не устранит их нарушение.
Подробную информацию о настройке других нарушений см. в документации FragmentStrictMode.Policy.Builder
.
Существует три вида штрафов.
-
penaltyLog()
передает подробную информацию о нарушениях в Logcat. -
penaltyDeath()
завершает работу приложения при обнаружении нарушений. -
penaltyListener()
позволяет добавить собственный прослушиватель, который вызывается при обнаружении нарушений.
Вы можете применять любую комбинацию штрафов в своем Policy
. Если ваша политика явно не определяет штраф, применяется значение по умолчанию penaltyLog()
. Если вы применяете в своей пользовательской Policy
штраф, отличный от penaltyLog()
, то penaltyLog()
отключается, если вы не установили его явно.
penaltyListener()
может быть полезен, если у вас есть сторонняя библиотека журналирования, в которую вы хотите регистрировать нарушения. В качестве альтернативы вы можете включить отслеживание нефатальных нарушений в сборках выпуска и регистрировать их в библиотеке отчетов о сбоях. Эта стратегия позволяет обнаружить нарушения, которые в противном случае были бы пропущены.
Чтобы установить глобальную политику StrictMode, установите политику по умолчанию, которая применяется ко всем экземплярам FragmentManager
, с помощью метода FragmentStrictMode.setDefaultPolicy()
:
Котлин
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() } }
Ява
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()); } }
В следующих разделах описаны типы нарушений и возможные обходные пути.
Повторное использование фрагментов
Нарушение повторного использования фрагмента включается с помощью detectFragmentReuse()
и выдает FragmentReuseViolation
.
Это нарушение указывает на повторное использование экземпляра Fragment
после его удаления из FragmentManager
. Такое повторное использование может вызвать проблемы, поскольку Fragment
может сохранить состояние предыдущего использования и вести себя нестабильно. Если вы каждый раз создаете новый экземпляр, он всегда находится в исходном состоянии при добавлении в FragmentManager
.
Использование тега фрагмента
Нарушение использования тега фрагмента включается с помощью detectFragmentTagUsage()
и выдает FragmentTagUsageViolation
.
Это нарушение указывает на то, что Fragment
раздувается с помощью тега <fragment>
в макете XML. Чтобы решить эту проблему, раздуйте свой Fragment
внутри <androidx.fragment.app.FragmentContainerView>
а не в теге <fragment>
. Фрагменты, раздутые с помощью FragmentContainerView
могут надежно обрабатывать транзакции Fragment
и изменения конфигурации. Они могут работать не так, как ожидалось, если вместо этого вы используете тег <fragment>
.
Сохранять использование экземпляра
Нарушение использования экземпляра сохранения включается с помощью detectRetainInstanceUsage()
и выдает RetainInstanceUsageViolation
.
Это нарушение указывает на использование сохраненного Fragment
, в частности, если есть вызовы setRetainInstance()
или getRetainInstance()
, которые оба устарели.
Вместо того, чтобы использовать эти методы для самостоятельного управления сохраненными экземплярами Fragment
, сохраните состояние в ViewModel
, который сделает это за вас.
Установить видимую пользователю подсказку
Нарушение подсказки, установленной пользователем, включается с помощью detectSetUserVisibleHint()
и выдает SetUserVisibleHintViolation
.
Это нарушение указывает на вызов метода setUserVisibleHint()
, который устарел.
Если вы вызываете этот метод вручную, вместо этого вызовите setMaxLifecycle()
. Если вы переопределите этот метод, переместите поведение в onResume()
при передаче true
и onPause()
при передаче false
.
Использование целевого фрагмента
Нарушение использования целевого фрагмента включается с помощью detectTargetFragmentUsage()
и выдает TargetFragmentUsageViolation
.
Это нарушение указывает на вызов методов setTargetFragment()
, getTargetFragment()
или getTargetRequestCode()
, которые устарели. Вместо использования этих методов зарегистрируйте FragmentResultListener
. Дополнительные сведения о передаче результатов см. в разделе Передача результатов между фрагментами .
Неправильный контейнер фрагментов
Нарушение контейнера неправильного фрагмента включается с помощью detectWrongFragmentContainer()
и выдается WrongFragmentContainerViolation
.
Это нарушение указывает на добавление Fragment
в контейнер, отличный от FragmentContainerView
. Как и в случае с использованием тега Fragment
, транзакции фрагментов могут работать не так, как ожидалось, если они не размещены внутри FragmentContainerView
. Использование представления контейнера также помогает решить проблему в API View
, из-за которой фрагменты, использующие анимацию выхода, рисуются поверх всех остальных фрагментов.