Jetpack Compose, Android için modern bir bildirime dayalı kullanıcı arayüzü araç setidir. Compose, ön uç görünümlerini zorunlu olarak değiştirmenize gerek kalmadan uygulama kullanıcı arayüzünüzü oluşturmanıza olanak tanıyan bir bildirimsel API sağlayarak uygulama kullanıcı arayüzünüzü yazmayı ve bakımını yapmayı kolaylaştırır. Bu terminolojinin biraz açıklanması gerekiyor ancak sonuçları uygulama tasarımınız açısından önemlidir.
Bildirimsel programlama paradigması
Geçmişte, Android görünüm hiyerarşisi bir kullanıcı arayüzü widget'ları ağacı olarak gösterilebiliyordu. Uygulamanın durumu, kullanıcı etkileşimleri gibi nedenlerle değiştiğinde mevcut verileri göstermek için kullanıcı arayüzü hiyerarşisinin güncellenmesi gerekir.
Kullanıcı arayüzünü güncellemenin en yaygın yolu, findViewById()
gibi işlevleri kullanarak ağaçta ilerlemek ve button.setText(String)
, container.addChild(View)
veya img.setImageBitmap(Bitmap)
gibi yöntemleri çağırarak düğümleri değiştirmektir. Bu yöntemler, widget'ın dahili durumunu değiştirir.
Görüntülemeleri manuel olarak değiştirmek hata olasılığını artırır. Bir veri parçası birden fazla yerde oluşturuluyorsa bunu gösteren görünümlerden birini güncellemeyi unutmak kolaydır. Ayrıca, iki güncelleme beklenmedik bir şekilde çakıştığında yasa dışı durumlar oluşturmak da kolaydır. Örneğin, bir güncelleme, kullanıcı arayüzünden yeni kaldırılan bir düğümün değerini ayarlamaya çalışabilir. Genel olarak, yazılım bakımı karmaşıklığı, güncellenmesi gereken görünümlerin sayısıyla birlikte artar.
Son birkaç yıldır sektörün tamamı, kullanıcı arayüzü oluşturma ve güncelleme ile ilgili mühendisliği büyük ölçüde basitleştiren bildirim temelli bir kullanıcı arayüzü modeline geçmeye başladı. Bu teknik, tüm ekranı baştan kavramsal olarak yeniden oluşturup yalnızca gerekli değişiklikleri uygulayarak çalışır. Bu yaklaşım, durum bilgisi olan bir görünüm hiyerarşisinin manuel olarak güncellenmesinin karmaşıklığını önler. Compose, bildirime dayalı bir kullanıcı arayüzü çerçevesidir.
Ekranın tamamını yeniden oluşturmanın zorluklarından biri, zaman, bilgi işlem gücü ve pil kullanımı açısından maliyetli olabilmesidir. Compose, bu maliyeti azaltmak için kullanıcı arayüzünün hangi bölümlerinin herhangi bir zamanda yeniden çizilmesi gerektiğini akıllıca seçer. Bu durum, Yeniden Oluşturma bölümünde ele alındığı gibi, kullanıcı arayüzü bileşenlerinizi tasarlama şekliniz üzerinde bazı etkiler yaratır.
Basit bir composable işlev
Compose'u kullanarak, veri alan ve kullanıcı arayüzü öğeleri yayan bir dizi birleştirilebilir işlev tanımlayarak kullanıcı arayüzünüzü oluşturabilirsiniz. Basit bir örnek, String
alan ve bir selamlama mesajı gösteren Text
widget'ı yayan bir Greeting
widget'ıdır.
1.şekil Verilerin iletildiği ve ekranda bir metin widget'ı oluşturmak için kullanılan basit bir composable işlev.
Bu işlevle ilgili dikkat edilmesi gereken birkaç nokta:
İşlev,
@Composable
ek açıklamasıyla açıklanmıştır. Tüm Composable işlevlerinde bu ek açıklama bulunmalıdır. Bu ek açıklama, Compose derleyicisine işlevin verileri kullanıcı arayüzüne dönüştürmek için tasarlandığını bildirir.İşlev, verileri alır. Composable işlevler, uygulamanın mantığının kullanıcı arayüzünü tanımlamasına olanak tanıyan parametreleri kabul edebilir. Bu durumda, widget'ımız kullanıcıyı adıyla karşılayabilmek için
String
kabul eder.İşlev, kullanıcı arayüzünde metin gösterir. Bu işlem, metin kullanıcı arayüzü öğesini oluşturan
Text()
composable işlevi çağrılarak yapılır. Composable işlevler, diğer composable işlevleri çağırarak kullanıcı arayüzü hiyerarşisi oluşturur.İşlev herhangi bir değer döndürmüyor. Kullanıcı arayüzü yayan işlevlerin herhangi bir şey döndürmesi gerekmez. Bunun nedeni, kullanıcı arayüzü widget'ları oluşturmak yerine istenen ekran durumunu açıklamalarıdır.
Bu işlev hızlıdır, idempotent'tir ve yan etkileri yoktur.
- İşlev, aynı bağımsız değişkenle birden çok kez çağrıldığında aynı şekilde davranır ve genel değişkenler veya
random()
çağrıları gibi diğer değerleri kullanmaz. - Bu işlev, özellikleri veya genel değişkenleri değiştirme gibi yan etkiler olmadan kullanıcı arayüzünü tanımlar.
Genel olarak, Yeniden oluşturma bölümünde belirtilen nedenlerden dolayı tüm composable işlevler bu özelliklerle yazılmalıdır.
- İşlev, aynı bağımsız değişkenle birden çok kez çağrıldığında aynı şekilde davranır ve genel değişkenler veya
Bildirimsel paradigma değişikliği
Birçok zorunlu nesne yönelimli kullanıcı arayüzü araç setinde, kullanıcı arayüzünü bir widget ağacı oluşturarak başlatırsınız. Bu işlem genellikle bir XML düzen dosyasını genişleterek yapılır. Her widget kendi dahili durumunu korur ve uygulama mantığının widget ile etkileşim kurmasına olanak tanıyan getter ve setter yöntemlerini kullanıma sunar.
Compose'un bildirim temelli yaklaşımında widget'lar nispeten durumsuz olup ayarlayıcı veya alıcı işlevlerini kullanıma sunmaz. Aslında widget'lar nesne olarak gösterilmez.
Aynı composable işlevi farklı bağımsız değişkenlerle çağırarak kullanıcı arayüzünü güncellersiniz. Bu, ViewModel
gibi mimari kalıplara durum sağlamayı kolaylaştırır. Bu konu, Uygulama mimarisi kılavuzu'nda açıklanmıştır. Daha sonra, gözlemlenebilir veriler her güncellendiğinde mevcut uygulama durumunu kullanıcı arayüzüne dönüştürmekle composable'larınız sorumludur.
Şekil 2. Uygulama mantığı, en üst düzey composable işlevine veri sağlar. Bu işlev, diğer composable'ları çağırarak kullanıcı arayüzünü tanımlamak için verileri kullanır, uygun verileri bu composable'lara ve hiyerarşide aşağıya doğru iletir.
Kullanıcı, kullanıcı arayüzüyle etkileşim kurduğunda kullanıcı arayüzü onClick
gibi etkinlikler oluşturur.
Bu etkinlikler, uygulama mantığını bilgilendirmelidir. Bu mantık daha sonra uygulamanın durumunu değiştirebilir.
Durum değiştiğinde, composable işlevler yeni verilerle tekrar çağrılır. Bu durum, kullanıcı arayüzü öğelerinin yeniden çizilmesine neden olur. Bu sürece yeniden oluşturma denir.
3.Şekil Kullanıcı, bir kullanıcı arayüzü öğesiyle etkileşime girdiğinde bir etkinlik tetiklenir. Uygulama mantığı etkinliğe yanıt verir. Ardından, gerekirse composable işlevler yeni parametrelerle otomatik olarak tekrar çağrılır.
Dinamik içerik
Composable işlevler XML yerine Kotlin ile yazıldığından diğer Kotlin kodları kadar dinamik olabilir. Örneğin, bir kullanıcı listesini karşılayan bir kullanıcı arayüzü oluşturmak istediğinizi varsayalım:
@Composable fun Greeting(names: List<String>) { for (name in names) { Text("Hello $name") } }
Bu işlev, bir ad listesi alır ve her kullanıcı için bir selamlama oluşturur.
Composable işlevler oldukça karmaşık olabilir. Belirli bir kullanıcı arayüzü öğesini göstermek isteyip istemediğinize karar vermek için if
ifadelerini kullanabilirsiniz. Döngüleri kullanabilirsiniz. Yardımcı işlevleri çağırabilirsiniz. Temel dilin tüm esnekliğinden yararlanabilirsiniz.
Bu güç ve esneklik, Jetpack Compose'un temel avantajlarından biridir.
Yeniden oluşturma
Zorunlu bir kullanıcı arayüzü modelinde, bir widget'ı değiştirmek için widget'ta bir ayarlayıcı çağırarak widget'ın iç durumunu değiştirirsiniz. Compose'da, composable işlevi yeni verilerle tekrar çağırırsınız. Bu işlem, işlevin yeniden oluşturulmasına neden olur. İşlev tarafından yayınlanan widget'lar, gerekirse yeni verilerle yeniden çizilir. Compose çerçevesi, yalnızca değişen bileşenleri akıllı bir şekilde yeniden oluşturabilir.
Örneğin, bir düğme görüntüleyen şu composable işlevi ele alalım:
@Composable fun ClickCounter(clicks: Int, onClick: () -> Unit) { Button(onClick = onClick) { Text("I've been clicked $clicks times") } }
Arayan, düğmeyi her tıkladığında clicks
değerini günceller.
Compose, yeni değeri göstermek için lambda'yı Text
işleviyle tekrar çağırır. Bu işleme yeniden oluşturma denir. Değere bağlı olmayan diğer işlevler yeniden oluşturulmaz.
Daha önce de belirttiğimiz gibi, kullanıcı arayüzü ağacının tamamını yeniden oluşturmak hesaplama açısından maliyetli olabilir. Bu da işlem gücü ve pil ömrünü etkiler. Compose, bu sorunu akıllı yeniden oluşturma özelliğiyle çözüyor.
Yeniden oluşturma, girişler değiştiğinde composable işlevlerinizi tekrar çağırma işlemidir. Bu durum, işlevin girişleri değiştiğinde gerçekleşir. Compose, yeni girişlere göre yeniden oluşturma yaptığında yalnızca değişmiş olabilecek işlevleri veya lambda'ları çağırır ve geri kalanları atlar. Compose, parametreleri değişmemiş tüm işlevleri veya lambda'ları atlayarak verimli bir şekilde yeniden oluşturabilir.
Bir işlevin yeniden oluşturulması atlanabileceğinden, birleştirilebilir işlevlerin yürütülmesinden kaynaklanan yan etkilerden asla yararlanmayın. Bu durumda kullanıcılar uygulamanızda garip ve tahmin edilemeyen davranışlarla karşılaşabilir. Yan etki, uygulamanızın geri kalanında görülebilen herhangi bir değişikliktir. Örneğin, aşağıdaki işlemlerin tümü tehlikeli yan etkilerdir:
- Paylaşılan bir nesnenin özelliğine yazma
ViewModel
konumunda bir gözlemlenebilir öğeyi güncelleme- Paylaşılan tercihleri güncelleme
Bir animasyon oluşturulurken olduğu gibi, birleştirilebilir işlevler her karede yeniden yürütülebilir. Animasyonlar sırasında takılmayı önlemek için birleştirilebilir işlevler hızlı olmalıdır. Paylaşılan tercihlerden okuma gibi maliyetli işlemler yapmanız gerekiyorsa bunu arka plan coroutine'inde yapın ve değer sonucunu parametre olarak composable işlevine iletin.
Örneğin, bu kod, SharedPreferences
içindeki bir değeri güncellemek için composable oluşturur. Composable, paylaşılan tercihlerden okuma veya yazma işlemi yapmamalıdır. Bunun yerine, bu kod okuma ve yazma işlemlerini arka planda çalışan bir ViewModel
rutinine taşır. Uygulama mantığı, güncellemeyi tetiklemek için geri çağırma ile geçerli değeri iletir.
@Composable fun SharedPrefsToggle( text: String, value: Boolean, onValueChanged: (Boolean) -> Unit ) { Row { Text(text) Checkbox(checked = value, onCheckedChange = onValueChanged) } }
Bu belgede, Oluştur özelliğini kullanırken dikkat etmeniz gereken bazı noktalar ele alınmaktadır:
- Yeniden oluşturma, mümkün olduğunca çok sayıda composable işlevi ve lambda'yı atlar.
- Yeniden oluşturma işlemi iyimserdir ve iptal edilebilir.
- Bir composable işlevi, animasyonun her karesi kadar sık çalıştırılabilir.
- Composable işlevler paralel olarak yürütülebilir.
- Birleştirilebilir işlevler herhangi bir sırada yürütülebilir.
Aşağıdaki bölümlerde, yeniden oluşturmayı desteklemek için birleştirilebilir işlevlerin nasıl oluşturulacağı ele alınacaktır. Her durumda, en iyi uygulama, birleştirilebilir işlevlerinizi hızlı, idempotent ve yan etkisiz tutmaktır.
Yeniden oluşturma işlemi mümkün olduğunca fazla atlama yapar.
Kullanıcı arayüzünüzün bazı bölümleri geçersiz olduğunda Compose, yalnızca güncellenmesi gereken bölümleri yeniden oluşturmak için elinden geleni yapar. Bu nedenle, kullanıcı arayüzü ağacında üstündeki veya altındaki composable'ları çalıştırmadan tek bir Button composable'ını yeniden çalıştırmak için atlayabilir.
Her composable işlev ve lambda kendi başına yeniden oluşturulabilir. Aşağıda, bir liste oluşturulurken yeniden oluşturma işleminin bazı öğeleri nasıl atlayabileceğini gösteren bir örnek verilmiştir:
/** * Display a list of names the user can click with a header */ @Composable fun NamePicker( header: String, names: List<String>, onNameClicked: (String) -> Unit ) { Column { // this will recompose when [header] changes, but not when [names] changes Text(header, style = MaterialTheme.typography.bodyLarge) HorizontalDivider() // LazyColumn is the Compose version of a RecyclerView. // The lambda passed to items() is similar to a RecyclerView.ViewHolder. LazyColumn { items(names) { name -> // When an item's [name] updates, the adapter for that item // will recompose. This will not recompose when [header] changes NamePickerItem(name, onNameClicked) } } } } /** * Display a single name the user can click. */ @Composable private fun NamePickerItem(name: String, onClicked: (String) -> Unit) { Text(name, Modifier.clickable(onClick = { onClicked(name) })) }
Bu kapsamların her biri, yeniden oluşturma sırasında yürütülecek tek şey olabilir.
header
değiştiğinde Compose, üst öğelerinden herhangi birini yürütmeden Column
lambda'sına atlayabilir. Ayrıca Column
yürütülürken names
değişmediyse LazyColumn
öğeleri atlanabilir.
Yine, tüm birleştirilebilir işlevlerin veya lambda'ların yürütülmesi yan etkisiz olmalıdır. Bir yan etki gerçekleştirmeniz gerektiğinde bunu bir geri çağırma işlevinden tetikleyin.
Yeniden oluşturma iyimserdir
Yeniden oluşturma, Compose'un bir composable'ın parametrelerinin değişmiş olabileceğini düşündüğü her zaman başlar. Yeniden oluşturma iyimserdir. Bu,Compose'un parametreler tekrar değişmeden önce yeniden oluşturmayı tamamlamayı beklediği anlamına gelir. Bir parametre, yeniden oluşturma işlemi tamamlanmadan önce değişirse Compose, yeniden oluşturma işlemini iptal edip yeni parametreyle yeniden başlatabilir.
Yeniden oluşturma işlemi iptal edildiğinde Compose, yeniden oluşturma işlemindeki kullanıcı arayüzü ağacını atar. Kullanıcı arayüzünün görüntülenmesine bağlı yan etkileriniz varsa kompozisyon iptal edilse bile yan etki uygulanır. Bu durum, uygulamanın tutarsız bir durumda olmasına neden olabilir.
İyimser yeniden oluşturmayı işlemek için tüm composable işlevlerin ve lambda'ların idempotent ve yan etkisiz olduğundan emin olun.
Composable işlevler oldukça sık çalışabilir
Bazı durumlarda, composable işlevler bir kullanıcı arayüzü animasyonunun her karesi için çalışabilir. İşlev, cihaz depolama alanından okuma gibi maliyetli işlemler gerçekleştiriyorsa kullanıcı arayüzünde takılmaya neden olabilir.
Örneğin, widget'ınız cihaz ayarlarını okumaya çalışırsa bu ayarları saniyede yüzlerce kez okuyabilir ve bu durum uygulamanızın performansını olumsuz etkileyebilir.
Composable işlevinizin veriye ihtiyacı varsa veriler için parametreler tanımlaması gerekir. Ardından, pahalı işlemleri oluşturma dışında başka bir iş parçacığına taşıyabilir ve verileri mutableStateOf
veya LiveData
kullanarak Compose'a iletebilirsiniz.
Composable işlevler paralel olarak çalıştırılabilir
Compose, composable işlevleri paralel olarak çalıştırarak yeniden oluşturmayı optimize edebilir. Bu sayede Compose, birden fazla çekirdekten yararlanabilir ve ekranda olmayan composable işlevlerini daha düşük bir öncelikte çalıştırabilir.
Bu optimizasyon, bir composable işlevin arka plan iş parçacıkları havuzunda yürütülebileceği anlamına gelir.
Bir composable işlevi, ViewModel
üzerinde bir işlevi çağırırsa Compose, bu işlevi aynı anda birkaç iş parçacığından çağırabilir.
Uygulamanızın doğru şekilde çalışmasını sağlamak için tüm composable işlevlerin yan etkisi olmamalıdır. Bunun yerine, her zaman kullanıcı arayüzü iş parçacığında yürütülen onClick
gibi geri çağırmalardan yan etkileri tetikleyin.
Bir composable işlev çağrıldığında, çağıran işlevden farklı bir iş parçacığında çağrılabilir. Bu nedenle, hem iş parçacığı açısından güvenli olmadığı hem de composable lambda'nın izin verilmeyen bir yan etkisi olduğu için composable lambda'daki değişkenleri değiştiren kodlardan kaçınılmalıdır.
Bir listeyi ve sayısını gösteren bir composable'ı gösteren örneği burada bulabilirsiniz:
@Composable fun ListComposable(myList: List<String>) { Row(horizontalArrangement = Arrangement.SpaceBetween) { Column { for (item in myList) { Text("Item: $item") } } Text("Count: ${myList.size}") } }
Bu kod, yan etkisi olmayan ve giriş listesini kullanıcı arayüzüne dönüştüren bir koddur. Bu, küçük bir listeyi görüntülemek için harika bir kod. Ancak işlev yerel bir değişkene yazıyorsa bu kod iş parçacığı açısından güvenli veya doğru olmaz:
@Composable fun ListWithBug(myList: List<String>) { var items = 0 Row(horizontalArrangement = Arrangement.SpaceBetween) { Column { for (item in myList) { Card { Text("Item: $item") items++ // Avoid! Side-effect of the column recomposing. } } } Text("Count: $items") } }
Bu örnekte, her yeniden oluşturma işleminde items
değiştirilir. Bu, animasyonun her karesi veya listenin güncellendiği zaman olabilir. Her iki durumda da kullanıcı arayüzünde yanlış sayı gösterilir. Bu nedenle, bu tür yazma işlemleri Oluşturma'da desteklenmez. Bu yazma işlemlerini yasaklayarak, çerçevenin işlenebilir lambda'ları yürütmek için iş parçacıklarını değiştirmesine izin veririz.
Composable işlevler herhangi bir sırada yürütülebilir.
Bir composable işlevinin koduna baktığınızda, kodun göründüğü sırayla çalıştırıldığını düşünebilirsiniz. Ancak bu durumun geçerli olacağı garanti edilmez. Bir composable işlev, diğer composable işlevlere yapılan çağrıları içeriyorsa bu işlevler herhangi bir sırada çalışabilir. Oluşturma özelliği, bazı kullanıcı arayüzü öğelerinin diğerlerinden daha yüksek öncelikli olduğunu tanıma ve bunları önce çizme seçeneğine sahiptir.
Örneğin, bir sekme düzeninde üç ekran çizmek için aşağıdaki gibi bir kodunuz olduğunu varsayalım:
@Composable fun ButtonRow() { MyFancyNavigation { StartScreen() MiddleScreen() EndScreen() } }
StartScreen
, MiddleScreen
ve EndScreen
numaralarına yapılan aramalar herhangi bir sırada gerçekleşebilir. Bu nedenle, örneğin StartScreen()
'nın bazı genel değişkenleri (yan etki) ayarlaması ve MiddleScreen()
'nin bu değişiklikten yararlanması mümkün değildir. Bunun yerine, bu işlevlerin her birinin bağımsız olması gerekir.
Daha fazla bilgi
Compose'da düşünme ve composable işlevler hakkında daha fazla bilgi edinmek için aşağıdaki ek kaynaklara göz atın.
Videolar
Sizin için önerilenler
- Not: JavaScript kapalıyken bağlantı metni gösterilir.
- Jetpack Compose için Kotlin
- State ve Jetpack Compose
- Jetpack Compose mimari katmanları