قطعات خود را اشکال زدایی کنید

این راهنما ابزارهایی را پوشش می دهد که می توانید از آنها برای اشکال زدایی قطعات خود استفاده کنید.

ورود به سیستم 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 کمک می‌کند که باعث می‌شود قطعات با استفاده از انیمیشن‌های خروجی روی همه قطعات دیگر کشیده شوند.