このガイドでは、フラグメントのデバッグに使用できるツールについて説明します。
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 ロギング
DEBUG
レベルで、FragmentManager
は通常、ライフサイクルの状態変化に関連するログメッセージを出力します。各ログエントリには、Fragment
からの toString()
ダンプが含まれています。ログエントリは次の情報で構成されます。
Fragment
インスタンスの単純なクラス名。Fragment
インスタンスの ID ハッシュコード。Fragment
インスタンスのフラグメント マネージャーの一意の ID。これは、構成の変更や、プロセスの終了、再作成の際も安定しています。Fragment
が追加されたコンテナの ID(設定されている場合のみ)。Fragment
タグ(設定されている場合のみ)。
以下に、DEBUG
ログエントリの例を示します。
D/FragmentManager: moveto ATTACHED: NavHostFragment{92d8f1d} (fd92599e-c349-4660-b2d6-0ece9ec72f7b id=0x7f080116)
Fragment
クラスはNavHostFragment
。- ID ハッシュコードは
92d8f1d
。 - 一意の ID は
fd92599e-c349-4660-b2d6-0ece9ec72f7b
。 - コンテナ ID は
0x7f080116
。 - タグは設定されていないため、省略されています。存在する場合、
tag=tag_value
形式で ID の後に続きます。
わかりやすくするために、次の例では UUID を短縮しています。
NavHostFragment
が初期化され、FirstFragment
型の startDestination
Fragment
が作成されて、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 ロギング
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 コンストラクタで行います。
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()); ... } }
ブール値リソースの値からの場合など、StrictMode を有効にするかどうかを判断するために Context
を確認する必要があるときには、OnContextAvailableListener
を使用して、StrictMode ポリシーの FragmentManager
への割り当てを延期できます。
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()); ... } }
起こり得る違反をすべて検出するために StrictMode を構成できる最新のポイントは、super.onCreate()
の呼び出し前の 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()); ... } }
これらの例で使用されているポリシーは、フラグメントの再利用違反のみを検出し、違反が発生するたびにアプリを終了します。penaltyDeath()
は、違反を無視できないほど早くに失敗するため、デバッグビルドに役立ちます。
特定の違反を選択的に許可することもできます。ただし、上記の例で使用されているポリシーは、他のすべてのフラグメント タイプに対してこの違反を適用します。これは、サードパーティのライブラリ コンポーネントに StrictMode 違反が含まれている恐れがある場合に有用です。
このような場合、ライブラリで違反が修正されるまでの間だけ、所有していないコンポーネントの StrictMode の許可リストにこれらの違反を追加できます。
その他の違反の設定方法については、FragmentStrictMode.Policy.Builder
のドキュメントをご覧ください。
ペナルティは 3 種類あります。
penaltyLog()
は、違反の詳細を Logcat にダンプします。penaltyDeath()
は、違反が検出されたときにアプリを終了します。penaltyListener()
を使用すると、違反が検出されるたびに呼び出されるカスタム リスナーを追加できます。
Policy
ではペナルティを自由に組み合わせて適用できます。ポリシーでペナルティが明示的に指定されていない場合は、デフォルトの penaltyLog()
が適用されます。カスタム Policy
に penaltyLog()
以外のペナルティを適用すると、明示的に設定しない限り、penaltyLog()
は無効になります。
penaltyListener()
は、違反をログに記録するサードパーティのロギング ライブラリがある場合に有用です。あるいは、致命的でない違反の捕捉をリリースビルドで有効にし、クラッシュ レポート ライブラリにログを記録することもできます。この戦略を使用すると、他の方法では見過ごされる違反を検出できます。
グローバル StrictMode ポリシーを設定するには、FragmentStrictMode.setDefaultPolicy()
メソッドを使用して、すべての FragmentManager
インスタンスに適用されるデフォルト ポリシーを設定します。
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()); } }
以降のセクションでは、違反の種類と考えられる回避策について説明します。
フラグメントの再利用
detectFragmentReuse()
を使用するとフラグメントの再利用違反が有効になり、FragmentReuseViolation
がスローされます。
この違反は、FragmentManager
から削除された Fragment
インスタンスが、後に再利用されたことを示します。この再利用は、Fragment
が以前の使用時の状態を保持しており動作が一貫していないため、問題を引き起こす可能性があります。毎回新しいインスタンスを作成すれば、FragmentManager
に追加されたときは常に初期状態になります。
フラグメント タグの使用
detectFragmentTagUsage()
を使用するとフラグメント タグの使用違反が有効になり、FragmentTagUsageViolation
がスローされます。
この違反は、XML レイアウトで <fragment>
タグを使用して Fragment
がインフレートされたことを示します。この問題を解決するには、<fragment>
タグではなく、<androidx.fragment.app.FragmentContainerView>
内で Fragment
をインフレートします。FragmentContainerView
を使用してインフレートしたフラグメントは、Fragment
トランザクションと構成変更を確実に処理できます。代わりに <fragment>
タグを使用した場合、想定どおりに動作しない可能性があります。
保持インスタンスの使用
detectRetainInstanceUsage()
を使用すると保持インスタンスの使用違反が有効になり、RetainInstanceUsageViolation
がスローされます。
この違反は、特に setRetainInstance()
または getRetainInstance()
(ともに非推奨)への呼び出しがある場合に、保持されている Fragment
の使用を示すものです。
これらのメソッドを使用して、保持された Fragment
インスタンスを自分で管理するのではなく、これを処理する ViewModel
に状態を保存します。
ユーザー表示ヒントの設定
detectSetUserVisibleHint()
を使用するとユーザー表示ヒントの設定違反が有効になり、SetUserVisibleHintViolation
がスローされます。
この違反は、サポートが終了した setUserVisibleHint()
を呼び出していることを示します。
このメソッドを手動で呼び出している場合は、代わりに setMaxLifecycle()
を呼び出してください。このメソッドをオーバーライドする場合、true
を渡すときには動作を onResume()
に、false
を渡すときには動作を onPause()
に移します。
ターゲット フラグメントの使用
detectTargetFragmentUsage()
を使用するとターゲット フラグメントの使用違反が有効になり、TargetFragmentUsageViolation
がスローされます。
この違反は、サポートが終了した setTargetFragment()
、getTargetFragment()
、もしくは getTargetRequestCode()
を呼び出していることを示します。これらのメソッドを使用する代わりに、FragmentResultListener
を登録します。結果を渡す方法について詳しくは、フラグメント間で結果を渡すをご覧ください。
フラグメント コンテナの誤り
detectWrongFragmentContainer()
を使用するとフラグメント コンテナの誤り違反が有効になり、WrongFragmentContainerViolation
がスローされます。
この違反は、Fragment
を FragmentContainerView
以外のコンテナに追加していることを示します。Fragment
タグの使用と同様に、フラグメントのトランザクションは、FragmentContainerView
内でホストされている場合を除き、期待どおりに動作しない可能性があります。コンテナビューを使用すると、終了アニメーションを使用するフラグメントが他のすべてのフラグメントの上に描画されるという、View
API の問題にも対処できます。