Yazılı Düşünme

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ştirmeden uygulama kullanıcı arayüzünüzü oluşturmanıza olanak tanıyan bir tanımlayıcı API sağlayarak uygulama kullanıcı arayüzünüzü yazmayı ve sürdürmeyi kolaylaştırır. Bu terminolojiye ilişkin bazı açıklamalar yapılmalıdır ancak çıkarımlar, uygulama tasarımınız açısından önemlidir.

Bildirim temelli programlama paradigması

Android görünüm hiyerarşisi geçmişte kullanıcı arayüzü widget'larından oluşan bir ağaç olarak temsil edilebilirdi. Uygulamanın durumu, kullanıcı etkileşimleri gibi nedenlerle değiştikçe kullanıcı arayüzü hiyerarşisinin mevcut verileri göstermek için güncellenmesi gerekir. Kullanıcı arayüzünü güncellemenin en yaygın yolu, findViewById() gibi işlevleri kullanarak ağacı yürütmek 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ülemelerle manuel olarak oynamak hata olasılığını artırır. Bir veri birden fazla yerde oluşturuluyorsa bu veriyi gösteren görünümlerden birini güncellemeyi unutabilirsiniz. İki 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ılmış bir düğümün değerini ayarlamaya çalışabilir. Genel olarak, yazılım bakımının karmaşıklığı, güncellenmesi gereken görünümlerin sayısıyla birlikte artar.

Son birkaç yılda sektörün tamamında bildirim temelli bir kullanıcı arayüzü modeline geçiş yapmaya başladı. Bu model, kullanıcı arayüzlerinin oluşturulması ve güncellenmesiyle ilgili mühendisliği büyük ölçüde basitleştiriyor. Bu teknik, ekranın tamamını kavramsal olarak sıfırdan yeniden üretip yalnızca gerekli değişiklikleri uygulayarak çalışır. Bu yaklaşım, durum bilgisine sahip bir görünüm hiyerarşisini manuel olarak güncellemenin karmaşıklığını önler. Compose, bildirime dayalı bir kullanıcı arayüzü çerçevesidir.

Ekranın tamamını yeniden oluşturmanın bir zorluğu, zaman, bilgi işlem gücü ve pil kullanımı açısından pahalı olmasıdır. Bu maliyeti azaltmak için Compose, kullanıcı arayüzünün hangi bölümlerinin belirli bir zamanda yeniden çizilmesi gerektiğini akıllıca seçer. Bunun, Yeniden düzenleme bölümünde açıklandığı gibi, kullanıcı arayüzü bileşenlerinizi tasarlama biçiminiz üzerinde bazı etkileri vardır.

Basit bir composable işlevi

Compose'u kullanarak, veri alan ve kullanıcı arayüzü öğeleri yayınlayan bir dizi birleştirilebilir işlev tanımlayarak kullanıcı arayüzünüzü oluşturabilirsiniz. Basit bir örnek olarak, bir String alan ve karşılama mesajı gösteren bir Text widget yayınlayan bir Greeting widget verilebilir.

"Merhaba Dünya" metnini ve bu kullanıcı arayüzünü oluşturan basit Composable işlevinin kodunu gösteren bir telefonun ekran görüntüsü

Şekil 1. Veri alan ve ekranda bir metin widget'ı oluşturmak için bu verileri kullanan basit bir birleştirilebilir işlev.

Bu işlevle ilgili dikkate değer birkaç nokta:

  • İşlev, @Composable notuyla ek açıklamaya sahiptir. Tüm Composable işlevlerinde bu ek açıklama bulunmalıdır. Bu ek açıklama, Compose derleyicisine bu işlevin verilerin kullanıcı arayüzüne dönüştürülmesi amacıyla tasarlandığını bildirir.

  • İşlev verileri alır. Birleştirilebilir işlevler, uygulama 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ı isme göre selamlayabilmek için String değerini kabul eder.

  • İşlev, kullanıcı arayüzünde metin gösterir. Bunu, metin kullanıcı arayüzü öğesini oluşturan Text()composable işlevini çağırarak yapar. Composable işlevler, diğer composable işlevleri çağırarak kullanıcı arayüzü hiyerarşisi oluşturur.

  • İşlev hiçbir şey döndürmez. Kullanıcı arayüzü widget'ları oluşturmak yerine istenen ekran durumunu tanımladıkları için kullanıcı arayüzü yayınlayan oluşturma işlevlerinin herhangi bir değer döndürmesi gerekmez.

  • Bu işlev hızlı, tekil ve yan etkisizdir.

    • İş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.
    • İşlev, kullanıcı arayüzünü özellikleri veya genel değişkenleri değiştirme gibi yan etkiler olmadan tanımlar.

    Genel olarak, tüm birleştirilebilir işlevler Yeniden derleme bölümünde açıklanan nedenlerden dolayı bu özelliklerle yazılmalıdır.

Bildirim temelli paradigma değişimi

Nesne yönelimli birçok zorunlu kullanıcı arayüzü araç setinde, bir widget ağacı oluşturarak kullanıcı arayüzünü başlatırsınız. Bunu genellikle bir XML düzen dosyasını şişirerek yaparsınız. Her widget kendi dahili durumunu korur ve uygulama mantığının widget ile etkileşim kurmasına olanak tanıyan alıcı ve belirleyici yöntemleri sunar.

Compose'un açıklayıcı yaklaşımında widget'lar nispeten durumsuzdur ve ayarlayıcı veya alıcı işlevleri göstermez. Aslında widget'lar nesne olarak gösterilmez. Kullanıcı arayüzünü, aynı composable işlevi farklı bağımsız değişkenlerle çağırarak güncellersiniz. Bu, Uygulama mimarisi kılavuzu'nda açıklandığı gibi ViewModel gibi mimari kalıplara durum bilgisi sağlamayı kolaylaştırır. Ardından, gözlemlenebilir veriler her güncellendiğinde mevcut uygulama durumunu kullanıcı arayüzüne dönüştürmekten bileşenleriniz sorumludur.

Üst düzey nesnelerden alt öğelerine kadar Compose kullanıcı arayüzünde veri akışını gösteren görsel.

Şekil 2. Uygulama mantığı, üst düzey birleştirilebilir işleve veri sağlar. Bu işlev, verileri kullanarak diğer composable'ları çağırarak kullanıcı arayüzünü açıklar ve uygun verileri bu composable'lara iletir.

Kullanıcı kullanıcı arayüzüyle etkileşimde bulunduğunda kullanıcı arayüzü onClick gibi etkinlikler oluşturur. Bu etkinlikler, uygulama mantığını bilgilendirmelidir. Uygulama mantığı da uygulamanın durumunu değiştirebilir. Durum değiştiğinde, birleştirilebilir işlevler yeni verilerle tekrar çağrılır. Bu, kullanıcı arayüzü öğelerinin yeniden çizilmesine neden olur. Bu sürece yeniden oluşturma denir.

Kullanıcı arayüzü öğelerinin, uygulama mantığı tarafından işlenen etkinlikleri tetikleyerek etkileşime nasıl tepki verdiğini gösteren resim.

Şekil 3. Kullanıcı bir kullanıcı arayüzü öğesiyle etkileşime geçerek bir etkinliğin tetiklenmesine neden oldu. Uygulama mantığı etkinliğe yanıt verir, ardından derlenebilir işlevler gerekirse yeni parametrelerle otomatik olarak tekrar çağrılır.

Dinamik içerik

Oluşturulabilir işlevler XML yerine Kotlin ile yazıldığından, diğer tüm 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, ad listesini alır ve her kullanıcı için bir karşılama mesajı oluşturur. Kompozit 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ğine sahip olursunuz. Bu güç ve esneklik, Jetpack Compose'un en önemli avantajlarından biri.

Yeniden derleme

Zorunlu kullanıcı arayüzü modelinde, bir widget'ı değiştirmek için widget'ta dahili durumunu değiştirmesi için bir belirleyici çağırırsınız. Oluştur'da, derlenebilir işlevi yeni verilerle tekrar çağırırsınız. Bunu yapmak, işlevin yeniden oluşturulmasına neden olur. İşlev tarafından yayınlanan widget'lar, gerekirse yeni verilerle yeniden çizilir. Oluşturma çerçevesi, yalnızca değişen bileşenleri akıllıca yeniden oluşturabilir.

Örneğin, bir düğme görüntüleyen bu derlenebilir işlevi düşünün:

@Composable
fun ClickCounter(clicks: Int, onClick: () -> Unit) {
    Button(onClick = onClick) {
        Text("I've been clicked $clicks times")
    }
}

Düğme her tıklandığında arayan, clicks değerini günceller. Compose, yeni değeri göstermek için Text işleviyle lambda'yı tekrar çağırır. Bu sürece yeniden oluşturma adı verilir. Değere bağlı olmayan diğer işlevler yeniden derlenmez.

Daha önce de belirttiğimiz gibi, kullanıcı arayüzü ağacının tamamını yeniden oluşturmak, hesaplama gücü ve pil ömrü kullanan, hesaplama açısından pahalı olabilir. Oluşturma, bu akıllı yeniden oluşturma özelliğiyle bu sorunu çözer.

Yeniden derleme, girişler değiştiğinde birleştirilebilir işlevlerinizi tekrar çağırma işlemidir. Bu durum, işlevin girişleri değiştiğinde ortaya çıkar. Oluştur işlevi, yeni girişlere göre yeniden oluştururken yalnızca değişmiş olabilecek işlevleri veya lambdaları çağırır, geri kalanını atlar. Değiştirilmiş parametreleri olmayan tüm işlevleri veya lambda'ları atlayarak Compose verimli bir şekilde yeniden oluşturabilir.

Bir işlevin yeniden derlenmesi atlanabilir. Bu nedenle, birleştirilebilir işlevlerin yürütülmesinden kaynaklanan yan etkilere asla güvenmeyin. Bunu yaparsanız kullanıcılar uygulamanızda garip ve öngörülemeyen davranışlarla karşılaşabilir. Yan etki, uygulamanızın geri kalanı tarafından görülebilen tüm değişikliklerdir. Örneğin, aşağıdaki işlemlerin tümü tehlikeli yan etkilerdir:

  • Paylaşılan bir nesnenin mülküne yazma
  • ViewModel'te bir gözlemlenebiliri güncelleme
  • Paylaşılan tercihleri güncelleme

Birleştirilebilir işlevler, bir animasyon oluşturulurken olduğu gibi her karede bir yeniden yürütülebilir. Oluşturulabilir işlevler, animasyonlar sırasında tıkanıklıklardan kaçınmak için hızlı olmalıdır. Paylaşılan tercihlerden okuma gibi pahalı işlemler yapmanız gerekiyorsa bunu arka planda bir coroutine'te yapın ve değer sonucunu, birleştirilebilir işleve parametre olarak iletin.

Örneğin, bu kod SharedPreferences içindeki bir değeri güncellemek için bir composable oluşturur. Kompozit, paylaşılan tercihlerden kendisi okumamalı veya yazmamalıdır. Bunun yerine bu kod, okuma ve yazma işlemini arka plan koordinatında bir ViewModel öğesine taşır. Uygulama mantığı, güncellemeyi tetiklemek için mevcut değeri bir geri çağırma işleviyle iletir.

@Composable
fun SharedPrefsToggle(
    text: String,
    value: Boolean,
    onValueChanged: (Boolean) -> Unit
) {
    Row {
        Text(text)
        Checkbox(checked = value, onCheckedChange = onValueChanged)
    }
}

Bu dokümanda, Oluştur'u kullanırken dikkat etmeniz gereken bazı noktalar ele alınmaktadır:

  • Yeniden derleme, mümkün olduğunca fazla birleştirilebilir işlevi ve lambda'yı atlar.
  • Yeniden düzenleme iyimser olduğu için iptal edilebilir.
  • Bir birleştirilebilir işlev, animasyondaki her kare kadar sıklıkla oldukça sık çalışabilir.
  • Kompozit işlevler paralel olarak yürütülebilir.
  • Oluşturulabilir işlevler herhangi bir sırada yürütülebilir.

Aşağıdaki bölümlerde, yeniden derlemeyi desteklemek için birleştirilebilir işlevlerin nasıl oluşturulacağı ele alınmaktadır. Her durumda, en iyi uygulama, birleştirilebilir işlevlerinizi hızlı, tekil ve yan etkisiz tutmaktır.

Yeniden derleme mümkün olduğunca atlar

Kullanıcı arayüzünüzün bölümleri geçersiz olduğunda Compose, yalnızca güncellenmesi gereken bölümleri yeniden derlemek için elinden geleni yapar. Bu, kullanıcı arayüzü ağacında bir düğmenin bileşenini yeniden çalıştırırken, düğmenin üstündeki veya altındaki bileşenlerden hiçbirini çalıştırmadan atlayabileceği anlamına gelir.

Her birleştirilebilir işlev ve lambda kendi kendine yeniden derlenebilir. Aşağıda, yeniden oluşturmanın bir liste oluştururken 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 derleme sırasında yürütülecek tek şey olabilir. header değiştiğinde Compose, üst öğelerinden hiçbirini yürütmeden Column lambda işlevine atlayabilir. Ayrıca, Column yürütüldüğünde, names değişmediyse Oluştur, LazyColumn öğelerini atlamayı seçebilir.

Yine, composable işlevlerin veya lambda'ların çalıştırılmasında yan etki bulunmamalıdır. Bir yan etki gerçekleştirmeniz gerektiğinde bunu geri çağırma işlevinden tetikleyin.

Yeniden oluşturma iyimser

Oluşturma, bir derlenebilir öğenin parametrelerinin değişmiş olabileceğini düşündüğünde yeniden oluşturma işlemini başlatır. Yeniden oluşturma işlemi iyimserdir. Bu, Oluştur'un, parametreler tekrar değişmeden önce yeniden oluşturma işlemini tamamlamayı beklediği anlamına gelir. Bir parametre, yeniden derleme tamamlanmadan önce değişirse Derle, yeniden derlemeyi iptal edip yeni parametreyle yeniden başlatabilir.

Yeniden oluşturma iptal edildiğinde Compose, kullanıcı arayüzü ağacını yeniden oluşturmadan siler. Kullanıcı arayüzünün görüntülenmesine bağlı yan etkileriniz varsa kompozisyon iptal edilse bile yan etki uygulanır. Bu da uygulama durumunun tutarsız olmasına yol açabilir.

Tüm composable işlevlerin ve lambda'ların iyimser yeniden düzenleme yapmak için tarafsız ve yan etki içermeyen olduğundan emin olun.

Kompozit işlevler oldukça sık çalışabilir

Bazı durumlarda, composable işlev bir kullanıcı arayüzü animasyonunun her karesi için çalışabilir. İşlev, cihaz depolama alanından okuma gibi pahalı işlemler gerçekleştirirse kullanıcı arayüzü kilitlenmesine neden olabilir.

Örneğin, widget'ınız cihaz ayarlarını okumaya çalıştıysa bu ayarları saniyede yüzlerce kez okuyabilir. Bu da uygulamanızın performansı üzerinde feci etkiler yaratabilir.

Kompozit işleviniz veri gerektiriyorsa veriler için parametreler tanımlamalıdır. Ardından, pahalı çalışmaları, oluşturma dışında başka bir ileti dizisine taşıyabilir ve mutableStateOf veya LiveData kullanarak verileri Oluştur'a iletebilirsiniz.

Kompozit işlevler paralel olarak çalıştırılabilir

Compose, composable işlevleri paralel olarak çalıştırarak yeniden yapılandırmayı optimize edebilir. Bu, Compose'un birden çok çekirdekten yararlanmasını ve ekranda olmayan composable işlevlerini daha düşük öncelikte çalıştırmasını sağlar.

Bu optimizasyon, birleştirilebilir bir işlevin arka plan iş parçacıkları havuzunda çalışabileceği anlamına gelir. Bir composable işlev 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 davranması için tüm derlenebilir 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 etkiler tetikleyin.

Bir birleştirilebilir işlev çağrıldığında, çağrı, arayandan farklı bir iş parçacığında gerçekleşebilir. Bu nedenle, hem iş parçacığı açısından güvenli olmadığı hem de birleştirilebilir lambda'nın izin verilmeyen bir yan etkisi olduğu için birleştirilebilir lambda'daki değişkenleri değiştiren koddan kaçınılmalıdır.

Bir listeyi ve sayısını gösteren bir derlenebilir öğeyi gösteren örneği aşağıda 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 etkisizdir ve giriş listesini kullanıcı arayüzüne dönüştürür. Bu kod, küçük bir liste görüntülemek için mükemmeldir. Ancak işlev bir yerel değişkene yazarsa 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, items her yeniden derlemeyle değiştirilir. Bu, bir animasyonun her karesi veya liste güncellendiğinde olabilir. Her iki durumda da kullanıcı arayüzü yanlış sayıyı görüntüler. Bu nedenle, bu tür yazmalar Compose'da desteklenmez. Bu yazma işlemlerini yasaklayarak çerçevenin composable lambda'lar yürütmesi için iş parçacıklarını değiştirmesine izin veririz.

Kompozit işlevler herhangi bir sırada yürütülebilir

Bir composable işlevin koduna bakarsanız kodun göründüğü sırayla çalıştırıldığını varsayabilirsiniz. Ancak bu durumun doğruluğu garanti edilmez. Bir birleştirilebilir işlev, diğer birleştirilebilir işlevlere yönelik çağrılar içeriyorsa bu işlevler herhangi bir sırada çalışabilir. Oluşturma işlemi, bazı kullanıcı arayüzü öğelerinin diğerlerinden daha yüksek önceliğe sahip olduğunu algılayıp bunları önce çizebilir.

Örneğin, sekme düzeninde üç ekran çizmek için şuna benzer bir kodunuz olduğunu varsayalım:

@Composable
fun ButtonRow() {
    MyFancyNavigation {
        StartScreen()
        MiddleScreen()
        EndScreen()
    }
}

StartScreen, MiddleScreen ve EndScreen'ye yapılan aramalar herhangi bir sırada gerçekleşebilir. Bu, örneğin StartScreen()'ün bazı genel değişkenleri ayarlamasına (yan etki) ve MiddleScreen()'ün bu değişiklikten yararlanmasına izin veremezsiniz. Bunun yerine, bu işlevlerin her birinin bağımsız olması gerekir.

Daha fazla bilgi

Compose ve derlenebilir işlevler hakkında daha fazla bilgi edinmek için aşağıdaki ek kaynaklara göz atın.

Videolar

Şu anda öneri yok.

Google Hesabınızda deneyin.