يتناول هذا الدليل الأدوات التي يمكنك استخدامها لتصحيح أخطاء الأجزاء.
تسجيل 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
، يصدر 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.
إنّ السمة Policy
المخصّصة
تحدّد الانتهاكات التي يتم رصدها وتحدّد العقوبة التي يتم فرضها
عند رصد المخالفات.
لتطبيق سياسة حصرية مُخصّصة، يجب تعيينها إلى
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
.
ثمة ثلاثة أنواع من العقوبات.
- يعرض
penaltyLog()
تفاصيل الانتهاكات إلى Logcat. penaltyDeath()
يؤدي إلى إنهاء التطبيق عند رصد المخالفات.penaltyListener()
تتيح لك إضافة أداة معالجة مخصّصة يتم استدعاؤها عندما يتم رصد انتهاكات.
يمكنك تطبيق أي مجموعة من العقوبات في 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()); } }
توضّح الأقسام التالية أنواع المخالفات والحلول الممكنة.
إعادة استخدام الأجزاء
يتم تفعيل انتهاك إعادة استخدام الجزء باستخدام 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
. يساعد استخدام عرض الحاوية أيضًا في معالجة مشكلة في واجهة برمجة تطبيقات View
تؤدّي إلى
رسم الأجزاء التي تستخدم حركات الخروج فوق كل
الأجزاء الأخرى.