このガイドでは、フラグメントのデバッグに使用できるツールについて説明します。
FragmentManager ロギング
FragmentManager
は、さまざまなメッセージを Logcat に出力できます。これは、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
インスタンスの、FragmentManager
の一意の ID。これは、構成の変更や、プロセスの終了、再作成の際も安定しています。Fragment
が追加されたコンテナの ID(設定されている場合のみ)。Fragment
タグ(設定されている場合のみ)。
D/FragmentManager: moveto ATTACHED: NavHostFragment{92d8f1d} (fd92599e-c349-4660-b2d6-0ece9ec72f7b id=0x7f080116)
この Fragment は次のように識別できます。
Fragment
クラスはNavHostFragment.
。- ID ハッシュコードは
92d8f1d
。 - 一意の ID は
fd92599e-c349-4660-b2d6-0ece9ec72f7b
。 - コンテナ ID は
0x7f080116
。 - タグは設定されていないため、省略されています。存在していれば、ID の後に
tag=tag_value
の形式で続きます。
わかりやすくするために、次の例では 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 が含まれています。これにより、アプリが予期しない動作をする原因となる一般的な問題を捕捉できます。
デフォルトでは、Fragment StrictMode には何も捕捉しない LAX
ポリシーがあります。ただし、カスタム ポリシーを作成することは可能です。カスタム 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()); ... } }
たとえばブール値リソースの値から、厳格モードを有効にするかどうかを判断するために Context
を確認する必要がある場合は、OnContextAvailableListener
を使用して、FragmentManager
への StrictMode ポリシーの割り当てを延期できます。
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()); ... } }
起こり得る違反をすべて捕捉するために厳格モードを設定する必要がある最新のポイントは onCreate()
ですが、ここでは 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()); ... } }
この例で使用されているポリシーは、フラグメントの再利用違反のみを検出し、違反が発生するたびにアプリを終了します。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
がスローされます。
この違反は、保持されている Fragment
を使用していることを示します。具体的には、setRetainInstance()
または getRetainInstance()
の呼び出しがある場合です(どちらもサポートが終了しています)。
これらのメソッドを使用して保持された Fragment
インスタンスを自分で管理するのではなく、これを処理する ViewModel
に状態を保存する必要があります。
ユーザー表示ヒントの設定
detectSetUserVisibleHint()
を使用するとユーザー表示ヒントの設定違反が有効になり、SetUserVisibleHintViolation
がスローされます。
この違反は、サポートが終了した setUserVisibleHint()
を呼び出していることを示します。
このメソッドを手動で呼び出している場合は、代わりに setMaxLifecycle()
を呼び出す必要があります。このメソッドをオーバーライドする場合、true
を渡すと onResume()
に、false
を渡すと onPause()
に、動作を移す必要があります。
ターゲット フラグメントの使用
detectTargetFragmentUsage()
を使用するとターゲット フラグメントの使用違反が有効になり、TargetFragmentUsageViolation
がスローされます。
この違反は、サポートが終了した setTargetFragment()
、getTargetFragment()
、getTargetRequestCode()
を呼び出していることを示します。これらのメソッドを使用する代わりに、FragmentResultListener
を登録する必要があります。詳細については、フラグメント間で結果を受け渡すをご覧ください。
フラグメント コンテナの誤り
detectWrongFragmentContainer()
を使用するとフラグメント コンテナの誤り違反が有効になり、WrongFragmentContainerViolation
がスローされます。
この違反は、Fragment
を FragmentContainerView
以外のコンテナに追加していることを示します。フラグメント タグの使用と同様に、FragmentContainerView
内でホストされている場合を除き、Fragment トランザクションが想定どおりに動作しない可能性があります。また、終了アニメーションを使用するフラグメントが他のすべてのフラグメントの上に描画されるという、View
API の問題にも対処できます。