คู่มือนี้ครอบคลุมเครื่องมือที่คุณสามารถใช้เพื่อแก้ไขข้อบกพร่อง ส่วนย่อย
การบันทึก 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
กำลังเริ่มต้น จากนั้นเป็น startDestination
กำลังสร้าง Fragment
ประเภท 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
ที่ระดับ 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
จำนวนมากไม่ค่อยมีประโยชน์ต่อแอป
อย่างไรก็ตาม การทราบว่าเมื่อใดที่การเปลี่ยนแปลงใดๆ ใน Back Stack เกิดขึ้นอาจช่วย
การแก้ไขข้อบกพร่องบางอย่าง
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 เช่น จากค่าทรัพยากรบูลีน คุณสามารถ
เลื่อนการกำหนดนโยบาย StrictMode ให้กับ FragmentManager
โดยใช้
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()); ... } }
จุดล่าสุดที่คุณสามารถกำหนดค่า 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
บทลงโทษมี 3 ประเภท
penaltyLog()
ส่งออกรายละเอียดของการละเมิดไปยัง LogcatpenaltyDeath()
จะหยุดแอปเมื่อตรวจพบการละเมิดpenaltyListener()
ช่วยให้คุณเพิ่ม Listener แบบกำหนดเอง ซึ่งจะถูกเรียกทุกครั้งที่มีการละเมิด ตรวจพบ
คุณใช้การลงโทษแบบใดก็ได้ในPolicy
หากนโยบายของคุณรองรับ
ไม่ได้ระบุบทลงโทษอย่างชัดแจ้ง ระบบจะใช้ค่าเริ่มต้นเป็น penaltyLog()
หากคุณ
ใช้บทลงโทษอื่นที่ไม่ใช่ penaltyLog()
ใน Policy
ที่กำหนดเองของคุณ
penaltyLog()
จะถูกปิดใช้ เว้นแต่คุณจะตั้งค่านี้ไว้อย่างชัดแจ้ง
penaltyListener()
จะมีประโยชน์เมื่อคุณมีไลบรารีการบันทึกของบุคคลที่สามเพื่อ
ซึ่งคุณต้องการบันทึกการละเมิด หรือคุณอาจต้องการเปิดใช้
การละเมิดที่ไม่ร้ายแรงที่ตรวจพบในบิลด์ที่เผยแพร่และบันทึกการละเมิดไปยังรายงานข้อขัดข้อง
ไลบรารี กลยุทธ์นี้สามารถตรวจจับการละเมิดที่อาจพลาดไป
หากต้องการตั้งค่านโยบาย StrictMode ส่วนกลาง ให้ตั้งค่านโยบายเริ่มต้นที่ใช้กับ
FragmentManager
อินสแตนซ์ที่ใช้
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()); } }
ส่วนต่อไปนี้จะอธิบายประเภทของการละเมิดและวิธีแก้ไขเฉพาะหน้าที่เป็นไปได้
การใช้ Fragment ซ้ำ
คุณเปิดใช้การละเมิดการใช้ Fragment ซ้ำได้โดยใช้
detectFragmentReuse()
และส่งข้อความ
FragmentReuseViolation
การละเมิดนี้แสดงถึงการนำอินสแตนซ์ Fragment
มาใช้ซ้ำหลังจากนำออก
จาก FragmentManager
การใช้ซ้ำนี้อาจทำให้เกิดปัญหาเนื่องจาก Fragment
อาจ
คงสถานะจากการใช้งานก่อนหน้าไว้และทำงานไม่สม่ำเสมอ หากคุณสร้าง
อินสแตนซ์ใหม่ทุกครั้ง อินสแตนซ์จะอยู่ในสถานะเริ่มต้นเสมอเมื่อมีการเพิ่มลงใน
FragmentManager
การใช้แท็ก Fragment
มีการเปิดใช้การละเมิดการใช้แท็กส่วนย่อยโดยใช้
detectFragmentTagUsage()
และส่งข้อความ
FragmentTagUsageViolation
การละเมิดนี้ชี้ว่า Fragment
สูงเกินจริงโดยใช้ <fragment>
ในการจัดวาง XML ในการแก้ไขปัญหานี้ ให้เพิ่ม Fragment
ของคุณให้สูงเกินจริง
<androidx.fragment.app.FragmentContainerView>
แทนที่จะเป็นใน<fragment>
แท็ก ส่วนย่อยที่พองขึ้นโดยใช้ FragmentContainerView
สามารถจัดการได้อย่างน่าเชื่อถือ
การเปลี่ยนแปลงธุรกรรมและการกำหนดค่า Fragment
รายการ ซึ่งอาจไม่ได้ผลเป็น
จะเกิดขึ้นหากคุณใช้แท็ก <fragment>
แทน
รักษาการใช้งานอินสแตนซ์
มีการเปิดใช้การละเมิดการใช้งานอินสแตนซ์ของการเก็บรักษาโดยใช้
detectRetainInstanceUsage()
และส่งข้อความ
RetainInstanceUsageViolation
การละเมิดนี้ระบุถึงการใช้งาน Fragment
ที่เก็บรักษาไว้ โดยเฉพาะอย่างยิ่ง หาก
มีการเรียกไปยัง
setRetainInstance()
หรือ
getRetainInstance()
,
ซึ่งเลิกใช้งานแล้วทั้งคู่
แทนที่จะใช้วิธีการเหล่านี้เพื่อจัดการอินสแตนซ์ Fragment
ที่เก็บรักษาไว้
เก็บสถานะร้านค้าใน
ViewModel
ที่จัดการเรื่องนี้ให้คุณ
ตั้งค่าคำแนะนำที่ปรากฏต่อผู้ใช้
เปิดใช้การละเมิดคำแนะนำที่มองเห็นได้โดยผู้ใช้ของ Set โดยใช้
detectSetUserVisibleHint()
และส่งข้อความ
SetUserVisibleHintViolation
การละเมิดนี้ระบุถึงการเรียก
setUserVisibleHint()
ซึ่งเลิกใช้งานแล้ว
หากคุณเรียกใช้วิธีการนี้ด้วยตนเอง ให้โทร
setMaxLifecycle()
แทน หากคุณลบล้างเมธอดนี้ ให้ย้ายลักษณะการทำงานไปยัง
onResume()
เมื่อผ่านใน true
และ
onPause()
เมื่อผ่านใน false
การใช้งาน Fragment เป้าหมาย
มีการเปิดใช้การละเมิดการใช้งานเป้าหมาย Fragment ด้วย
detectTargetFragmentUsage()
และส่งข้อความ
TargetFragmentUsageViolation
การละเมิดนี้ระบุถึงการเรียก
setTargetFragment()
getTargetFragment()
,
หรือ getTargetRequestCode()
ซึ่งทั้งหมดเลิกใช้งานแล้ว แทนที่จะใช้วิธีการเหล่านี้ ให้ลงทะเบียน
FragmentResultListener
สำหรับข้อมูลเพิ่มเติมเกี่ยวกับการส่งผลลัพธ์ โปรดดูส่งผลลัพธ์ระหว่าง
ส่วนย่อย
คอนเทนเนอร์ Fragment ไม่ถูกต้อง
มีการเปิดใช้การละเมิดคอนเทนเนอร์ของ Fragment ที่ไม่ถูกต้องโดยใช้
detectWrongFragmentContainer()
และส่งข้อความ
WrongFragmentContainerViolation
การละเมิดนี้บ่งชี้ถึงการเพิ่ม Fragment
ลงในคอนเทนเนอร์อื่นที่ไม่ใช่
FragmentContainerView
เช่นเดียวกับการใช้แท็ก Fragment
ธุรกรรมที่เป็นส่วนย่อยอาจไม่ทำงานตามที่คาดไว้ เว้นแต่จะโฮสต์ภายใน
FragmentContainerView
การใช้มุมมองคอนเทนเนอร์ยังช่วยแก้ปัญหาใน View
API ที่
ทำให้เศษส่วนที่ใช้ภาพเคลื่อนไหวออกถูกวาดทับบนอื่นๆ ทั้งหมด
ส่วนย่อย