本指南將說明如何使用工具偵錯片段。
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
例項的身分雜湊碼。- 片段管理員的
Fragment
例項專屬 ID。這個 ID 在各項設定變更及程序終止並重建期間保持不變。 - 加入
Fragment
的容器 ID,但僅限已設定時。 Fragment
標記,但僅限已設定時。
以下提供 DEBUG
記錄項目範例:
D/FragmentManager: moveto ATTACHED: NavHostFragment{92d8f1d} (fd92599e-c349-4660-b2d6-0ece9ec72f7b id=0x7f080116)
Fragment
類別為NavHostFragment
- 身分雜湊碼為
92d8f1d
。 - 唯一識別碼為
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
例項都會加上一個 ID 後綴字串,方便您追蹤同一個 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()); ... } }
如果您必須知道 Context
才能判斷是否要啟用 StrictMode (例如,根據布林值資源的值),可以使用 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 以擷取所有可能違規事件的最晚時間點,在於 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
的說明文件。
懲罰類型有三種。
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()); } }
以下各節說明違規事件類型和可能變通辦法。
Fragment (片段) 重複使用
系統利用 detectFragmentReuse()
啟用片段重複使用違規事項偵測,然後擲回 FragmentReuseViolation
。
此違規事項表示從 FragmentManager
移除了某個 Fragment
例項後,又重複使用該例項。Fragment
可能會保留先前使用的狀態而表現出不一致的行為,因此這樣重複使用可能會引起問題。如果每次建立一個新例項,加入 FragmentManager
時就一律會是初始狀態。
片段標記使用
系統會利用 detectFragmentTagUsage()
啟用片段標記使用違規事件偵測,並擲回 FragmentTagUsageViolation
。
此違規事項表示在 XML 版面配置中,使用了 <fragment>
標記加載 Fragment
。如果要解決此問題,請在 <androidx.fragment.app.FragmentContainerView>
內加載 Fragment
,而不是在 <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 中的問題,這個問題會導致使用離開動畫的片段,繪製在所有其他片段上方。