Arayüze sahip yazılım projelerinde kullanıcı dostu tasarımlar oluşturmak müşteri potansiyelini fazlasıyla olumlu etkilemektedir. Android uygulama geliştirmede kullanılan Fragment, kullanıcı dostu olma sebebiyle Google Play uygulamasında dahi kullanılmaktadır. Yalnız Fragment karmaşık yaşam döngüsüne sahip olmasından dolayı projelerde bazen hata ayıklama zor olabilmektedir.
Projede Fragment kullanırken yaşanan sorunlardan bazılarını kodlar inceleyerek kolayca önlenebilir. Bu makale de Android uygulamalarda Fragment kullanırken sıkça yaptığımız 5 hata ve çözümünden bahsedeceğim.
1- SavedStateInstance’ı kontrol etmeden yeni bir Fragment oluşturma
Activity sınıfında, Fragment sınıfını kullanan tasarımınız varsa, onCreate içinde Fragment kullanabilirsiniz.
1 2 3 4 5 6 7 |
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) supportFragmentManager.beginTransaction() .replace(R.id.container, NewFragment()) .commit() } |
Hata
Yukarıdaki kodu kullandığınızda bir sorun oluşacaktır. Activity sonlandırılıp, geri yüklediğinde tekrarlanan yeni bir Fragment oluşturulacaktır.
Hatanın Çözümü
Oluşturduğunuz Fragment’ı her zaman saveInstanceState == null kontrol içerisine alırsak, sorun çözülür.
1 2 3 4 5 6 7 8 9 |
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) if (savedInstanceState == null) { supportFragmentManager.beginTransaction() .replace(R.id.container, NewFragment()) .commit() } } |
Bu çözüm, mevcut Fragment geri yüklenirse yeni bir Fragment’ın oluşturulmasını ve işlem görmesini engelleyecektir.
2- Activity içinde Fragment’ a erişim
Bazen Activity sınıfında Fragment’a erişmek isteyebiliriz. Erişmek istediğiniz Fragment’ı global alanda referans tutarak, erişim yapabilirsiniz.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
private var myFragment: MyFragment? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) if (savedInstanceState == null) { myFragment = NewFragment() supportFragmentManager.beginTransaction() .replace(R.id.container, myFragment) .commit() } } private fun anotherFunction() { myFragemnt?.doSomething() } |
Hata
Her bir Fragment’ın yaşam döngüsü vardır. Sistem tarafından sonlandırılır ve geri yüklenir. Bu, belirtilen orijinal Fragment’ın artık orada olmadığı anlamına gelir.
Activity’de bir Fragment referansı tutarsak, referansı doğru Fragment’a güncellememiz gerekir. Bu durumu çözmezseniz yanlış Fragment üzerinde işlem yapmanıza sebep olur.
Hatanın Çözümü
İlgili Fragment’a tag ekleyin.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) if (savedInstanceState == null) { supportFragmentManager.beginTransaction() .replace(R.id.container, NewFragment(), FragmentTag) .commit() } } private fun anotherFunction() { (supportFragmentManager.findFragmentByTag(FragmentTag) as? NewFragment)?.doSomething() } |
Erişmeniz gerektiğinde, bunun yerine fragment transaction kullanabilirsiniz. Fakat Fragment ve Activity (veya ana fragment) arasındaki bu tür iletişimi yine de en aza indirmeliyiz.
3- Fragment onSavedStateInstance metodunda View erişimi
Bazen Fragment sistem tarafından sonlandırıldığında kaydedilecek bazı görünüm bilgilerine erişmek isteriz. Örnek kod;
1 2 3 4 5 6 |
override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) binding?.myView?.let { } } |
Hata
Eğer Fragment sonlandırılmışsa ve bunun yerine başka bir Fragment ile değiştirilmişse, Fragment’ın onViewDestroy () metodu çağrılır. Fragment hala orada olduğundan onSaveInstanceState çağrılmaz. Ama Fragment’ın görünümü artık yoktur. Daha sonra, Fragment’ların sistem tarafından sonlandırılması durumunda o zaman onSaveInstanceState çağrılır. Ancak bağlamaya erişim boş olduğundan ve bu kod tarafından yapılması amaçlanan hiçbir şey çalıştırılmayacaktır. binding değeri boş olduğundan kod tarafından yapılması amaçlanan hiçbir şey çalıştırılmayacaktır.
Hatanın Çözümü
Görünümden erişmek istediğiniz her şeyi onSavedStateInstance’ten önce yapılmalı ve başka bir yerde saklanmalıdır.
4- Add ve Replace metodlarının kullanım tercihi
Bir Fragment ile ilgili işlem yapmak için, replace ve add metodlarını kullanmalıyız. Bazen hangisini kullanmamız gerektiği konusunda kafamızda sorular oluşabilir.
1 2 3 |
supportFragmentManager.beginTransaction() .add(R.id.container, myFragment) .commit() |
Add metodunu kullanmanın yararı, alttaki Fragment’ın görünümünün yok edilmemesini ve üstteki Fragment dışarı çıktığında asla yeniden oluşturulmasına gerek kalmamasını sağlayacaktır. Aşağıda size fikir verecek bazı senaryo bulunmaktadır.
- Eklediğiniz altdaki Fragment, yüklenmesi uzun zaman alan bir şeyse, üstteki Fragment dışarı çıktığında tekrar yüklenmesini önlemek ardından replace yapmak yerine üstteki Fragment’ı eklemek isteyebilirsiniz.
Hata
Yukarıda bahsedilen senaryo nadir durumlarda gerçekleşir. Dezavantajları olduğu için add metoduna olan ihtiyaç sınırlı olmalıdır. Peki neden add metodunu mümkün mertebe az kullanmalıyız?
- Add metodunu kullanmak, alttaki Fragment view elementlerini görünür tutar ve gereksiz yere daha fazla bellek dolar.
- Yığın içinde birden fazla Fragment olması ve görünür olması, hepsinin birlikte geri yüklendiği zamanlarda geri yükleme sorunlarına neden olabilir.
Hatanın Çözümü
Fragment’ları kullanırken daha çok add metodu yerine replace metodunu kullanmayı tercih etmenizi öneririm.
5- Fragment etiketlemede sınıf isminin kullanımı
Bazen daha sonra bulabilmek için Fragment’ı etiketlemek isteriz. Uygun olduğu için, sınıfın basit adını kullanarak etiketleyebiliriz.
1 2 3 4 5 6 |
supportFragmentManager.beginTransaction() .replace( R.id.container, fragment, fragment.javaClass.simpleName) .commit() |
Hata
Android’de, Proguard veya DexGuard kullanarak sınıf adını gizleriz. Ve süreç boyunca, karmaşık hale getirilmiş basit ad, diğer sınıf adlarıyla çakışabilir.Nadir olabilir, ancak gerçekleştiğinde saçınızı çekmenize neden olabilir.:)
Hatanın Çözümü
Sınıf ismi kullanmak yerine etiket olarak sabit veya kurallı bir ad kullanmayı düşünün. Bu, benzersiz olmasını daha iyi sağlar.
1 2 3 4 5 6 |
supportFragmentManager.beginTransaction() .replace( R.id.container, fragment, fragment.javaClass.canonicalName) .commit() |
Kodlarınızı inceleme esnasında yukarıdaki 5 ipucuna dikkat ederek tonlarca garip ve tekrarlanması zor sorunu önlemenize yardımcı olacağını umuyorum.
Kaynak
1-https://medium.com/mobile-app-development-publication/7-common-mistakes-easily-made-with-android-fragment-6fc85c44e783