Parçalarınızda hata ayıklama

Bu kılavuz, parçalarınızda hata ayıklamak için kullanabileceğiniz araçları ele alır.

FragmentManager günlük kaydı

FragmentManager, Logcat'e çeşitli mesajlar gönderebilir. Bu özellik varsayılan olarak devre dışıdır ancak bazen bu günlük mesajları, parçalarınızla ilgili sorunları gidermenize yardımcı olabilir. FragmentManager, DEBUG ve VERBOSE günlük düzeylerinde en anlamlı çıkışı verir.

Günlük kaydını aşağıdaki adb shell komutunu kullanarak etkinleştirebilirsiniz:

adb shell setprop log.tag.FragmentManager DEBUG

Alternatif olarak, ayrıntılı günlük kaydını aşağıdaki şekilde etkinleştirebilirsiniz:

adb shell setprop log.tag.FragmentManager VERBOSE

Ayrıntılı günlük kaydını etkinleştirirseniz Logcat penceresinde bir günlük düzeyinde filtre uygulayabilirsiniz. Ancak bu sayede yalnızca FragmentManager günlükleri değil, tüm günlükler filtrelenir. FragmentManager günlük kaydını yalnızca ihtiyaç duyduğunuz günlük düzeyinde etkinleştirmek genellikle en iyi seçenektir.

HATA AYIKLAMA günlük kaydı

DEBUG düzeyinde, FragmentManager genellikle yaşam döngüsü durumu değişiklikleriyle ilgili günlük mesajları yayınlar. Her günlük girişi, Fragment aracındaki toString() dökümünü içerir. Günlük girişi aşağıdaki bilgilerden oluşur:

  • Fragment örneğinin basit sınıf adı.
  • Fragment örneğinin kimlik karma kodu.
  • Fragment örneğinin parça yöneticisinin benzersiz kimliği. Bu, yapılandırma değişiklikleri ve süreç ölümü ve yeniden oluşturma açısından sabittir.
  • Yalnızca ayarlanmışsa Fragment öğesinin eklendiği kapsayıcının kimliği.
  • Fragment etiketi (yalnızca ayarlanmışsa).

Aşağıda örnek bir DEBUG günlük girişi verilmiştir:

D/FragmentManager: moveto ATTACHED: NavHostFragment{92d8f1d} (fd92599e-c349-4660-b2d6-0ece9ec72f7b id=0x7f080116)
  • Fragment sınıfı NavHostFragment.
  • Kimlik karma kodu: 92d8f1d.
  • Benzersiz kimlik: fd92599e-c349-4660-b2d6-0ece9ec72f7b.
  • Kapsayıcı kimliği: 0x7f080116.
  • Herhangi bir etiket ayarlanmadığından etiket atlandı. Mevcut olduğunda, tag=tag_value biçimindeki kimliği izler.

UUID'ler, kısa ve anlaşılır olması için aşağıdaki örneklerde kısaltılmıştır.

Burada, başlatılmakta olan NavHostFragment ve ardından FirstFragment türündeki startDestination Fragment öğesinin oluşturulup RESUMED durumuna geçirilmesini görebilirsiniz:

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, bir kullanıcı etkileşiminin ardından çeşitli yaşam döngüsü durumlarından çıkar. Daha sonra SecondFragment örneklenir ve RESUMED durumuna geçer:

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)

Tüm Fragment örneklerine bir tanımlayıcı tarafından sonek getirilir. Böylece aynı Fragment sınıfının farklı örneklerini izleyebilirsiniz.

AYRINTILI günlük kaydı

VERBOSE düzeyinde, FragmentManager genellikle dahili durumuyla ilgili günlük mesajları yayınlar:

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)

Bu örnekte yalnızca FirstFragment sitesindeki yükleme işlemi ele alınmaktadır. SecondFragment ürününe geçişin dahil edilmesi, günlük girişlerini önemli ölçüde artırır. VERBOSE düzeyindeki günlük mesajlarının çoğu, uygulama geliştiricilerine pek fayda sağlamaz. Bununla birlikte, arka yığında değişikliklerin ne zaman gerçekleştiğini görmek bazı sorunların hatalarını ayıklamaya yardımcı olabilir.

Parçalar için StrictMode

Jetpack Fragment kitaplığının 1.4.0 ve sonraki sürümleri, parçalar için StrictMode özelliğini içerir. Bu özellik, uygulamanızın beklenmedik şekilde davranmasına neden olabilecek bazı yaygın sorunları yakalayabilir. StrictMode ile çalışma hakkında daha fazla bilgi için StrictMode konusuna bakın.

Özel Policy hangi ihlallerin tespit edildiğini ve ihlaller tespit edildiğinde hangi cezanın uygulanacağını belirtir.

Özel bir StrictMode politikası uygulamak için politikayı FragmentManager'a atayın. Bunu mümkün olduğunca erken yapın. Bu durumda, bunu bir init bloğunda veya Java kurucusinde yaparsınız:

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

StrictMode'un etkinleştirilip etkinleştirilmeyeceğini belirlemek için Context bilmeniz gereken durumlarda (ör. bir boole kaynağının değeri) OnContextAvailableListener kullanarak FragmentManager öğesine StrictMode politikası atamayı erteleyebilirsiniz:

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

Olası tüm ihlalleri yakalamak için StrictMode'u yapılandırabileceğiniz en son nokta, super.onCreate() çağrısından önce onCreate() içindedir:

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

Bu örneklerde kullanılan bu politika, yalnızca parça yeniden kullanımı ihlallerini tespit eder ve böyle bir ihlal olduğunda uygulama sonlandırılır. penaltyDeath(), ihlalleri yoksayamayacağınız kadar hızlı bir şekilde başarısız olduğundan hata ayıklama derlemelerinde faydalı olabilir.

Belirli ihlallere seçerek de izin verebilirsiniz. Ancak önceki örnekte kullanılan politika, bu ihlali diğer tüm parça türleri için uygular. Bu, üçüncü taraf bir kitaplık bileşeninin StrictMode ihlalleri içerebileceği durumlarda yararlıdır.

Bu tür durumlarda, sahip olmadığınız bileşenler, söz konusu ihlaller tarafından düzeltilene kadar StrictMode izin verilenler listesine geçici olarak ekleyebilirsiniz.

Diğer ihlalleri yapılandırma hakkında ayrıntılı bilgi için FragmentStrictMode.Policy.Builder belgelerini inceleyin.

Üç ceza türü vardır.

  • penaltyLog(), ihlallerin ayrıntılarını Logcat'e gönderir.
  • penaltyDeath(), ihlaller tespit edildiğinde uygulamayı sonlandırır.
  • penaltyListener(), ihlaller tespit edildiğinde çağrılan özel bir işleyici eklemenize olanak tanır.

Policy içinde istediğiniz ceza kombinasyonunu uygulayabilirsiniz. Politikanız açıkça bir ceza belirtmiyorsa varsayılan olarak penaltyLog() uygulanır. Özel Policy öğenizde penaltyLog() dışında bir ceza uygularsanız penaltyLog() açıkça ayarlamadığınız sürece devre dışı bırakılır.

İhlalleri günlüğe kaydetmek istediğiniz bir üçüncü taraf günlük kaydı kitaplığınız varsa penaltyListener() yararlı olabilir. Alternatif olarak, sürüm derlemelerinde önemli olmayan ihlal yakalamayı etkinleştirebilir ve bunları bir kilitlenme raporlama kitaplığına kaydedebilirsiniz. Bu strateji, diğer durumlarda gözden kaçan ihlalleri tespit edebilir.

Genel bir StrictMode politikası ayarlamak için FragmentStrictMode.setDefaultPolicy() yöntemini kullanarak tüm FragmentManager örneklerinde geçerli olacak varsayılan bir politika belirleyin:

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

Aşağıdaki bölümlerde ihlal türleri ve olası geçici çözümler açıklanmaktadır.

Parça yeniden kullanımı

Parça yeniden kullanım ihlali detectFragmentReuse() ile etkinleştirilir ve FragmentReuseViolation hatası verir.

Bu ihlal, bir Fragment örneğinin FragmentManager ürününden kaldırıldıktan sonra yeniden kullanıldığını belirtir. Fragment, önceki kullanımından kalan durumunu koruyabilir ve tutarlı davranamayabilir. Bu yeniden kullanım, sorunlara yol açabilir. Her defasında yeni bir örnek oluşturursanız bu örnek, FragmentManager öğesine eklendiğinde her zaman ilk durumunda olur.

Parça etiketi kullanımı

Parça etiketi kullanım ihlali detectFragmentTagUsage() ile etkinleştirilir ve FragmentTagUsageViolation hatası verir.

Bu ihlal, XML düzeninde <fragment> etiketi kullanılarak bir Fragment değerinin şişirildiğini gösterir. Bu sorunu çözmek için Fragment öğenizi <fragment> etiketi yerine <androidx.fragment.app.FragmentContainerView> içinde şişirin. FragmentContainerView kullanılarak genişletilen parçalar Fragment işlemi ve yapılandırma değişikliklerini güvenilir bir şekilde işleyebilir. Bunun yerine <fragment> etiketini kullanırsanız bunlar beklendiği gibi çalışmayabilir.

Örnek kullanımını koru

Örnek kullanma ihlali, detectRetainInstanceUsage() kullanılarak etkinleştirilir ve RetainInstanceUsageViolation hatası verir.

Bu ihlal, özellikle her ikisi de kullanımdan kaldırılan setRetainInstance() veya getRetainInstance() çağrıları varsa saklanan Fragment kullanımını belirtir.

Saklanan Fragment örneklerini yönetmek için bu yöntemleri kullanmak yerine, durumu sizin için halleden bir ViewModel içinde depolayın.

Kullanıcı tarafından görülebilen ipucunu ayarla

Kullanıcı tarafından görülebilir ipucu ihlali, detectSetUserVisibleHint() ile etkinleştirilir ve SetUserVisibleHintViolation hatası verir.

Bu ihlal, kullanımdan kaldırılan bir setUserVisibleHint() çağrısına işaret eder.

Bu yöntemi manuel olarak çağırıyorsanız bunun yerine setMaxLifecycle() yöntemini çağırın. Bu yöntemi geçersiz kılarsanız true içinde geçerken davranışı onResume(), false içinde geçerken onPause() değerine taşıyın.

Hedef parça kullanımı

Hedef parça kullanımı ihlali, detectTargetFragmentUsage() ile etkinleştirilir ve bir TargetFragmentUsageViolation döndürür.

Bu ihlal, setTargetFragment(), getTargetFragment() veya getTargetRequestCode() için yapılan ve kullanımdan kaldırılan bir çağrı olduğunu gösterir. Bu yöntemleri kullanmak yerine bir FragmentResultListener kaydedin. Sonuçları geçirme hakkında daha fazla bilgi için Parçalar arasında sonuçları geçirme bölümüne bakın.

Yanlış parça kapsayıcısı

Yanlış parça kapsayıcısı ihlali, detectWrongFragmentContainer() kullanılarak etkinleştirildi ve WrongFragmentContainerViolation hatasına neden oluyor.

Bu ihlal, FragmentContainerView dışında bir kapsayıcıya Fragment eklendiğini gösterir. Fragment etiket kullanımında olduğu gibi, bir FragmentContainerView içinde barındırılmadıkça parça işlemleri beklendiği gibi çalışmayabilir. Kapsayıcı görünümü kullanmak, View API'deki çıkış animasyonlarını kullanan parçaların, diğer tüm parçaların üzerine çizilmesine neden olan bir sorunun giderilmesine de yardımcı olur.