আপনার ফ্র্যাগমেন্ট ডিবাগ করুন

এই গাইডে এমন টুল সম্পর্কে আলোচনা করা হয়েছে যেগুলি ব্যবহার করে আপনি ফ্র্যাগমেন্ট ডিবাগ করতে পারবেন।

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 সাধারণত লাইফসাইকেল স্টেট পরিবর্তনের সাথে সম্পর্কিত লগ মেসেজ প্রকাশ করে। প্রতিটি লগ এন্ট্রিতে Fragment থেকে toString() ডাম্প থাকে। লগ এন্ট্রিতে নিম্নলিখিত তথ্য থাকে:

  • 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 শুরু করা হচ্ছে এবং তারপরে FirstFragment ধরনের startDestination Fragment তৈরি করা হচ্ছে এবং 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

Jetpack Fragment লাইব্রেরির ভার্সন 1.4.0 এবং তার পরবর্তী ভার্সনে ফ্র্যাগমেন্টের জন্য StrictMode অন্তর্ভুক্ত আছে। এটি কিছু সাধারণ সমস্যা ধরতে পারে যার কারণে আপনার অ্যাপ অপ্রত্যাশিতভাবে আচরণ করতে পারে। StrictMode-এর সাথে কাজ করার বিষয়ে আরও তথ্যের জন্য, StrictMode দেখুন।

কাস্টম Policy কোন কোন লঙ্ঘন শনাক্ত করা হয়েছে তা নির্ধারণ করে এবং লঙ্ঘন শনাক্ত করা হলে কী শাস্তি প্রয়োগ করা হবে তা নির্দিষ্ট করে।

কাস্টম StrictMode নীতি প্রয়োগ করতে, এটি FragmentManager-এ অ্যাসাইন করুন। যত তাড়াতাড়ি সম্ভব এটি করুন। এই ক্ষেত্রে, আপনি এটি একটি init ব্লক বা Java কনস্ট্রাক্টরে করেন:

Kotlin

class ExampleActivity : AppCompatActivity() {

    init {
        supportFragmentManager.strictModePolicy =
            FragmentStrictMode.Policy.Builder()
                .penaltyDeath()
                .detectFragmentReuse()
                .allowViolation(FirstFragment::class.java,
                                FragmentReuseViolation::class.java)
                .build()
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val binding = ActivityExampleBinding.inflate(layoutInflater)
        setContentView(binding.root)
        ...
   }
}

জাভা

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 ব্যবহার করে FragmentManager-এ StrictMode নীতি অ্যাসাইন করা স্থগিত করতে পারেন:

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)
        ...
   }
}

জাভা

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());
        ...
   }
}

super.onCreate()-এ কল করার আগে, onCreate()-এ আপনি StrictMode কনফিগার করতে পারেন যাতে সম্ভাব্য সব লঙ্ঘন ধরা যায়:

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)
        ...
   }
}

জাভা

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() প্রযোজ্য হয়। আপনার কাস্টম Policy-এ penaltyLog() ছাড়া অন্য কোনও পেনাল্টি প্রয়োগ করলে, penaltyLog() বন্ধ হয়ে যায়, যদি না আপনি এটি স্পষ্টভাবে সেট করেন।

penaltyListener() তখন উপযোগী হতে পারে যখন আপনার কাছে থার্ড-পার্টি লগিং লাইব্রেরি থাকে যেখানে আপনি লঙ্ঘনের লগ রাখতে চান। অন্যথায়, আপনি রিলিজ বিল্ডে নন-ফেটাল লঙ্ঘন ক্যাচিং চালু করতে এবং ক্র্যাশ রিপোর্টিং লাইব্রেরিতে সেগুলি লগ করতে চাইতে পারেন। এই স্ট্র্যাটেজি এমন লঙ্ঘন শনাক্ত করতে পারে যা অন্যথায় মিস হয়ে যায়।

গ্লোবাল StrictMode নীতি সেট করতে, একটি ডিফল্ট নীতি সেট করুন যা FragmentStrictMode.setDefaultPolicy() পদ্ধতি ব্যবহার করে সব FragmentManager ইনস্ট্যান্সের ক্ষেত্রে প্রযোজ্য হয়:

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()
    }
}

জাভা

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 থ্রো করে।

এই লঙ্ঘন থেকে বোঝা যায় যে FragmentManager থেকে Fragment ইনস্ট্যান্স সরিয়ে দেওয়ার পরে সেটি আবার ব্যবহার করা হয়েছে। এইভাবে আবার ব্যবহার করলে সমস্যা হতে পারে কারণ Fragment আগের ব্যবহারের স্ট্যাটাস ধরে রাখতে পারে এবং সামঞ্জস্যপূর্ণ আচরণ নাও করতে পারে। আপনি প্রতিবার একটি নতুন ইনস্ট্যান্স তৈরি করলে, এটি FragmentManager-এ যোগ করার সময় সর্বদা প্রাথমিক অবস্থায় থাকে।

ফ্র্যাগমেন্ট ট্যাগ ব্যবহার

ফ্র্যাগমেন্ট ট্যাগ ব্যবহারের লঙ্ঘন detectFragmentTagUsage() ব্যবহার করে চালু করা হয়েছে এবং FragmentTagUsageViolation থ্রো করে।

এই লঙ্ঘন থেকে বোঝা যায় যে XML লেআউটে <fragment> ট্যাগ ব্যবহার করে Fragment ইনফ্লেট করা হয়েছে। এই সমস্যার সমাধান করতে, <fragment> ট্যাগের পরিবর্তে <androidx.fragment.app.FragmentContainerView>-এর মধ্যে আপনার Fragment ইনফ্লেট করুন। FragmentContainerView ব্যবহার করে ফ্র্যাগমেন্ট ইনফ্লেট করা হলে তা নির্ভরযোগ্যভাবে Fragment ট্রানজ্যাকশন ও কনফিগারেশন পরিবর্তন ম্যানেজ করতে পারে। আপনি এর পরিবর্তে <fragment> ট্যাগ ব্যবহার করলে, এগুলি প্রত্যাশা অনুযায়ী কাজ নাও করতে পারে।

ইন্সট্যান্সের ব্যবহার বজায় রাখুন

রিটেন ইনস্ট্যান্স ব্যবহার সংক্রান্ত লঙ্ঘন detectRetainInstanceUsage() ব্যবহার করে চালু করা হয়েছে এবং RetainInstanceUsageViolation থ্রো করে।

এই লঙ্ঘন থেকে বোঝা যায় যে রিটেন করা Fragment ব্যবহার করা হয়েছে, বিশেষত, যদি setRetainInstance() বা getRetainInstance()-এ কল করা হয়, যেগুলি আর ব্যবহার করা হয় না।

আপনি নিজে রিটেন করা Fragment ইনস্ট্যান্স ম্যানেজ করার জন্য এইসব পদ্ধতি ব্যবহার করার পরিবর্তে, ViewModel-এ স্টেট স্টোর করুন যা আপনার জন্য এটি ম্যানেজ করে।

ব্যবহারকারীর জন্য দৃশ্যমান ইঙ্গিত সেট করুন

detectSetUserVisibleHint() ব্যবহার করে সেট করা ব্যবহারকারীর দৃশ্যমান ইঙ্গিত সংক্রান্ত লঙ্ঘন চালু করা হয়েছে এবং এটি SetUserVisibleHintViolation দেখায়।

এই লঙ্ঘন থেকে বোঝা যায় যে setUserVisibleHint()-এ কল করা হয়েছে, যা বন্ধ করে দেওয়া হয়েছে।

আপনি যদি ম্যানুয়ালি এই মেথড কল করেন, তাহলে পরিবর্তে setMaxLifecycle() কল করুন। আপনি এই পদ্ধতি ওভাররাইড করলে, true-এ পাস করার সময় আচরণটি onResume()-এ এবং false-এ পাস করার সময় onPause()-এ সরিয়ে দিন।

টার্গেট ফ্র্যাগমেন্টের ব্যবহার

টার্গেট ফ্র্যাগমেন্ট ব্যবহারের লঙ্ঘন detectTargetFragmentUsage() ব্যবহার করে চালু করা হয়েছে এবং TargetFragmentUsageViolation থ্রো করে।

এই নীতি লঙ্ঘন থেকে বোঝা যায় যে setTargetFragment(), getTargetFragment(), অথবা getTargetRequestCode()-এ কল করা হয়েছে, যেগুলি সবই বাতিল করা হয়েছে। এইসব পদ্ধতি ব্যবহার করার পরিবর্তে, একটি FragmentResultListener রেজিস্টার করুন। ফলাফল পাস করা সম্পর্কে আরও তথ্যের জন্য, ফ্র্যাগমেন্টের মধ্যে ফলাফল পাস করা দেখুন।

ভুল ফ্র্যাগমেন্ট কন্টেনার

ভুল ফ্র্যাগমেন্ট কন্টেনার সংক্রান্ত লঙ্ঘন detectWrongFragmentContainer() ব্যবহার করে চালু করা হয়েছে এবং WrongFragmentContainerViolation থ্রো করে।

এই লঙ্ঘনের অর্থ হল, FragmentContainerView ছাড়া অন্য কোনও কন্টেনারে Fragment যোগ করা হয়েছে। Fragment ট্যাগ ব্যবহার করার মতো, ফ্র্যাগমেন্ট ট্রানজ্যাকশন প্রত্যাশা অনুযায়ী কাজ নাও করতে পারে, যদি না সেটি FragmentContainerView-এর মধ্যে হোস্ট করা হয়। কন্টেনার ভিউ ব্যবহার করলে View API-এর একটি সমস্যার সমাধান করতেও সাহায্য করে, যার কারণে এক্সিট অ্যানিমেশন ব্যবহার করা ফ্র্যাগমেন্টগুলি অন্য সব ফ্র্যাগমেন্টের উপরে আঁকা হয়।