يتناول هذا الدليل الأدوات التي يمكنك استخدامها لتصحيح الأجزاء.
تسجيل 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
في التطبيق إلا بشكل ضئيل
المطورين. ومع ذلك، فإن معرفة وقت حدوث التغييرات على المكدس الخلفي يمكن أن يساعد في
وتصحيح بعض الأخطاء.
الوضع الصارم للأجزاء
الإصدار 1.4.0 والإصدارات الأحدث من
تتضمن مكتبة Jetpack Fragment.
الوضع الصارم للأجزاء. يمكنه اكتشاف بعض المشكلات الشائعة التي قد تتسبب في
والتطبيق لديك على التصرف بطرق غير متوقعة. لمزيد من المعلومات حول العمل مع
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 في 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()
مفيدة عندما يكون لديك مكتبة تسجيل تابعة لجهة خارجية من أجل
التي تريد تسجيل انتهاكاتها بدلاً من ذلك، قد تريد تفعيل
انتهاك غير فادح يتم من خلاله رصد بُنى الإصدارات وتسجيلها في تقارير الأعطال.
المكتبة. يمكن لهذه الاستراتيجية رصد الانتهاكات التي تم تفويتها في الحالات الأخرى.
لضبط سياسة StrategyMode العامة، يجب ضبط سياسة تلقائية تنطبق على جميع
مثيلان (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
API.
إلى رسم الأجزاء التي تستخدم الرسوم المتحركة عند الخروج فوق كل
الأجزاء.