این راهنما ابزارهایی را پوشش می دهد که می توانید از آنها برای اشکال زدایی قطعات خود استفاده کنید.
ورود به سیستم 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
به طور کلی پیامهای گزارش مربوط به تغییرات حالت چرخه حیات را منتشر میکند. هر ورودی log حاوی toString()
dump از 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
در حال تنظیم اولیه است و سپس Fragment
startDestination
از نوع 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
برای توسعهدهندگان اپلیکیشن کاربرد چندانی ندارند. با این حال، دیدن زمانی که تغییرات در پشته پشته رخ می دهد می تواند به رفع اشکال برخی از مشکلات کمک کند.
StrictMode برای قطعات
نسخه 1.4.0 و بالاتر کتابخانه Jetpack Fragment شامل StrictMode برای قطعات است. ممکن است برخی از مشکلات رایج را پیدا کند که ممکن است باعث شود برنامه شما به روشهای غیرمنتظرهای رفتار کند. برای اطلاعات بیشتر در مورد کار با StrictMode
، StrictMode را ببینید.
یک Policy
سفارشی تعریف میکند که کدام تخلف شناسایی میشود و مشخص میکند که چه مجازاتی در هنگام شناسایی تخلف اعمال میشود.
برای اعمال یک خط مشی StrictMode سفارشی، آن را به FragmentManager
اختصاص دهید. هر چه زودتر این کار را انجام دهید. در این مورد، شما این کار را در یک بلوک init
یا در سازنده جاوا انجام می دهید:
کاتلین
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) ... } }
جاوا
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()); ... } }
برای مواردی که برای تعیین اینکه آیا StrictMode را فعال کنید یا خیر، نیاز به دانستن Context
دارید، مانند مقدار یک منبع بولی، میتوانید با استفاده از OnContextAvailableListener
تخصیص یک خط مشی StrictMode به FragmentManager
را به تعویق بیندازید:
کاتلین
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) ... } }
جاوا
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()
:
کاتلین
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) ... } }
جاوا
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()
اعمال میشود:
کاتلین
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() } }
جاوا
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 کمک میکند که باعث میشود قطعات با استفاده از انیمیشنهای خروجی روی همه قطعات دیگر کشیده شوند.