Bestelerin yaşam döngüsü

Bu sayfada, composable'ların yaşam döngüsü ve Compose'un bir composable'ın yeniden oluşturulması gerekip gerekmediğine nasıl karar verdiği hakkında bilgi edineceksiniz.

Yaşam döngüsüne genel bakış

Durumu yönetme dokümanlarında belirtildiği gibi, bir kompozisyon, uygulamanızın kullanıcı arayüzünü tanımlar ve composable'lar çalıştırılarak oluşturulur. Kompozisyon, kullanıcı arayüzünüzü tanımlayan composable'ların ağaç yapısıdır.

Jetpack Compose, ilk kompozisyon sırasında composable'larınızı ilk kez çalıştırdığında, kullanıcı arayüzünüzü bir kompozisyonda tanımlamak için çağırdığınız composable'ları takip eder. Ardından, uygulamanızın durumu değiştiğinde Jetpack Compose bir yeniden oluşturma planlar. Yeniden oluşturma, Jetpack Compose'un durum değişikliklerine yanıt olarak değişmiş olabilecek composable'ları yeniden yürütmesi ve ardından değişiklikleri yansıtmak için Composition'ı güncellemesidir.

Bir beste yalnızca ilk beste ile üretilebilir ve yeniden beste yapılarak güncellenebilir. Bir kompozisyonu değiştirmenin tek yolu yeniden kompozisyondur.

Bir composable'ın yaşam döngüsünü gösteren diyagram

1.şekil Composition'daki bir composable'ın yaşam döngüsü. Bu işlev, Composition'a girer, 0 veya daha fazla kez yeniden oluşturulur ve Composition'dan çıkar.

Yeniden oluşturma genellikle bir State<T> nesnesinde yapılan bir değişiklikle tetiklenir. Compose, bunları izler ve kompozisyonda bu belirli State<T> değerini okuyan tüm composable'ları ve çağırdıkları, atlanamayan tüm composable'ları çalıştırır.

Bir composable birden fazla kez çağrılırsa Composition'a birden fazla örnek yerleştirilir. Her görüşmenin beste içinde kendi yaşam döngüsü vardır.

@Composable
fun MyComposable() {
    Column {
        Text("Hello")
        Text("World")
    }
}

Önceki kod snippet&#39;indeki öğelerin hiyerarşik düzenini gösteren şema

Şekil 2. Beste içinde MyComposable öğesinin temsili. Bir composable birden fazla kez çağrılırsa Composition'a birden fazla örnek yerleştirilir. Farklı renkteki bir öğe, ayrı bir örnek olduğunu gösterir.

Composition'da composable'ın anatomisi

Composition'daki bir composable'ın örneği, çağrı sitesi ile tanımlanır. Compose derleyicisi, her çağrı sitesini ayrı olarak değerlendirir. Birden fazla çağrı sitesinden çağrılan composable'lar, Composition'da composable'ın birden fazla örneğini oluşturur.

Yeniden oluşturma sırasında bir composable, önceki oluşturma sırasında çağırdığından farklı composable'lar çağırırsa Compose, hangi composable'ların çağrıldığını veya çağrılmadığını belirler. Her iki oluşturmada da çağrılan composable'lar için Compose, girişleri değişmediyse bunları yeniden oluşturmaktan kaçınır.

Yan etkilerin bestelenebilir öğeleriyle ilişkilendirilmesi için kimliğin korunması çok önemlidir. Böylece yan etkiler, her yeniden oluşturma işleminde yeniden başlatılmak yerine başarılı bir şekilde tamamlanabilir.

Aşağıdaki örneği inceleyelim:

@Composable
fun LoginScreen(showError: Boolean) {
    if (showError) {
        LoginError()
    }
    LoginInput() // This call site affects where LoginInput is placed in Composition
}

@Composable
fun LoginInput() { /* ... */ }

@Composable
fun LoginError() { /* ... */ }

Yukarıdaki kod snippet'inde LoginScreen, LoginError composable'ını koşullu olarak çağırır ve LoginInput composable'ını her zaman çağırır. Her çağrının, derleyicinin benzersiz şekilde tanımlamak için kullanacağı benzersiz bir çağrı sitesi ve kaynak konumu vardır.

showError işareti true olarak değiştirilirse önceki kodun nasıl yeniden oluşturulduğunu gösteren diyagram. LoginError composable&#39;ı eklenir ancak diğer composable&#39;lar yeniden oluşturulmaz.

3.Şekil Durum değiştiğinde ve yeniden oluşturma gerçekleştiğinde Kompozisyon'daki LoginScreen öğesinin temsili. Aynı renk, yeniden oluşturulmadığı anlamına gelir.

LoginInput önce çağrılmaktan ikinci çağrılmaya geçse de LoginInput örneği yeniden oluşturma işlemlerinde korunur. Ayrıca, LoginInput, yeniden oluşturma sırasında değişen parametreler içermediğinden LoginInput çağrısı Compose tarafından atlanır.

Akıllı yeniden oluşturma özelliğine yardımcı olmak için ek bilgiler ekleme

Bir composable'ı birden çok kez çağırmak, onu Composition'a da birden çok kez ekler. Aynı çağrı sitesinden bir composable birden çok kez çağrıldığında Compose, bu composable'a yapılan her çağrıyı benzersiz şekilde tanımlayacak bilgilere sahip olmadığından örnekleri farklı tutmak için çağrı sitesine ek olarak yürütme sırası kullanılır. Bu davranış bazen yeterli olsa da bazı durumlarda istenmeyen davranışlara neden olabilir.

@Composable
fun MoviesScreen(movies: List<Movie>) {
    Column {
        for (movie in movies) {
            // MovieOverview composables are placed in Composition given its
            // index position in the for loop
            MovieOverview(movie)
        }
    }
}

Yukarıdaki örnekte, Compose, Composition'da örneğin farklı kalmasını sağlamak için çağrı sitesine ek olarak yürütme sırasını kullanır. Listenin alt kısmına yeni bir movie eklenirse Oluşturma, listedeki konumları değişmediği için kompozisyonda zaten bulunan örnekleri yeniden kullanabilir. Bu nedenle, bu örnekler için movie girişi aynıdır.

Listenin en altına yeni bir öğe eklendiğinde önceki kodun nasıl yeniden oluşturulduğunu gösteren şema. Listedeki diğer öğelerin konumu değişmez ve bu öğeler yeniden oluşturulmaz.

Şekil 4. Listeye yeni bir öğe eklendiğinde Kompozisyon'daki MoviesScreen öğesinin gösterimi. Composition'daki MovieOverview composable'ları yeniden kullanabilirsiniz. MovieOverview içindeki aynı renk, composable'ın yeniden oluşturulmadığı anlamına gelir.

Ancak movies listesi, listenin üst veya ortasına öğe eklenerek, öğeler kaldırılarak ya da yeniden sıralanarak değişirse giriş parametresinin listedeki konumu değişen tüm MovieOverview çağrılarında yeniden oluşturmaya neden olur. Örneğin, MovieOverview, yan etki kullanarak bir film resmi getiriyorsa bu durum son derece önemlidir. Efekt devam ederken yeniden oluşturma işlemi gerçekleşirse bu işlem iptal edilir ve yeniden başlatılır.

@Composable
fun MovieOverview(movie: Movie) {
    Column {
        // Side effect explained later in the docs. If MovieOverview
        // recomposes, while fetching the image is in progress,
        // it is cancelled and restarted.
        val image = loadNetworkImage(movie.url)
        MovieHeader(image)

        /* ... */
    }
}

Listeye yeni bir öğe eklendiğinde önceki kodun nasıl yeniden oluşturulduğunu gösteren şema. Listedeki diğer tüm öğelerin konumu değişir ve yeniden oluşturulması gerekir.

5.şekil Listeye yeni bir öğe eklendiğinde Kompozisyon'daki MoviesScreen öğesinin gösterimi. MovieOverview composables yeniden kullanılamaz ve tüm yan etkiler yeniden başlar. MovieOverview içinde farklı bir renk, composable'ın yeniden oluşturulduğu anlamına gelir.

İdeal olarak, MovieOverview örneğinin kimliğinin, kendisine iletilen movie kimliğine bağlı olduğunu düşünmek isteriz. Film listesini yeniden sıralarsak her MovieOverview composable'ı farklı bir film örneğiyle yeniden oluşturmak yerine, ideal olarak Composition ağacındaki örnekleri de benzer şekilde yeniden sıralarız. Compose, çalışma zamanına ağacın belirli bir bölümünü tanımlamak için hangi değerleri kullanmak istediğinizi söylemenizi sağlar: key composable'ı.

Bir kod bloğunu, bir veya daha fazla değerin iletildiği anahtar composable'ı ile sarmalayarak bu değerleri birleştirip kompozisyondaki örneği tanımlamak için kullanabilirsiniz. Bir key için değerin küresel olarak benzersiz olması gerekmez. Yalnızca çağrı sitesindeki composable'ların çağrıları arasında benzersiz olması gerekir. Dolayısıyla bu örnekte her movie, movies arasında benzersiz olan bir key içermelidir. Bu key, uygulamadaki başka bir yerde bulunan diğer bazı composable'larla paylaşılabilir.

@Composable
fun MoviesScreenWithKey(movies: List<Movie>) {
    Column {
        for (movie in movies) {
            key(movie.id) { // Unique ID for this movie
                MovieOverview(movie)
            }
        }
    }
}

Yukarıdaki bilgiler sayesinde, listedeki öğeler değişse bile Compose, MovieOverview için yapılan ayrı ayrı çağrıları tanır ve bunları yeniden kullanabilir.

Listeye yeni bir öğe eklendiğinde önceki kodun nasıl yeniden oluşturulduğunu gösteren şema. Liste öğeleri anahtarlarla tanımlandığından, konumları değişmiş olsa bile Compose bunları yeniden oluşturmaz.

6.şekil Listeye yeni bir öğe eklendiğinde Kompozisyon'daki MoviesScreen öğesinin gösterimi. MovieOverview composable'ların benzersiz anahtarları olduğundan Compose, hangi MovieOverview örneklerinin değişmediğini tanır ve bunları yeniden kullanabilir. Bu örneklerin yan etkileri yürütülmeye devam eder.

Bazı composable'lar, key composable'ı için yerleşik destek sunar. Örneğin, LazyColumn, items DSL'sinde özel bir key belirtilmesini kabul eder.

@Composable
fun MoviesScreenLazy(movies: List<Movie>) {
    LazyColumn {
        items(movies, key = { movie -> movie.id }) { movie ->
            MovieOverview(movie)
        }
    }
}

Girişler değişmediyse atlama

Yeniden oluşturma sırasında, girişleri önceki oluşturma işleminden değişmemişse uygun olan bazı composable işlevlerin yürütülmesi tamamen atlanabilir.

Bir composable işlev, aşağıdaki durumlar hariç atlanmaya uygundur:

  • İşlevin Unit olmayan bir dönüş türü var.
  • İşlev, @NonRestartableComposable veya @NonSkippableComposable ile açıklama eklenmiş
  • Gerekli bir parametre kararlı olmayan bir türde

Son koşulu gevşeten deneysel bir derleyici modu olan Strong Skipping vardır.

Bir türün kararlı olarak kabul edilebilmesi için aşağıdaki sözleşmeye uyması gerekir:

  • İki örnek için equals işlevinin sonucu, aynı iki örnek için her zaman aynı olacaktır.
  • Türün herkese açık bir özelliği değişirse Composition bilgilendirilir.
  • Tüm genel mülk türleri de kararlıdır.

Bu sözleşmeye dahil olan ve Compose derleyicisinin, @Stable ek açıklaması kullanılarak açıkça kararlı olarak işaretlenmemiş olsalar bile kararlı olarak değerlendireceği bazı önemli ortak türler vardır:

  • Tüm temel değer türleri: Boolean, Int, Long, Float, Char vb.
  • Yaylı Çalgılar
  • Tüm işlev türleri (lambda'lar)

Bu türlerin tümü değişmez oldukları için kararlı sözleşmeye uyabilir. Değişmez türler asla değişmediğinden, değişikliğin bileşimini asla bildirmeleri gerekmez. Bu nedenle, bu sözleşmeyi takip etmek çok daha kolaydır.

Sabit ancak değiştirilebilir olan önemli bir tür, Compose'un MutableState türüdür. Bir değer MutableState içinde tutuluyorsa Compose, State öğesinin .value özelliğindeki değişikliklerden haberdar olacağından genel olarak durum nesnesinin kararlı olduğu kabul edilir.

Bir composable'a parametre olarak iletilen tüm türler kararlı olduğunda, parametre değerleri, kullanıcı arayüzü ağacındaki composable konumuna göre eşitlik açısından karşılaştırılır. Önceki çağrıdan bu yana tüm değerler değişmediyse yeniden oluşturma atlanır.

Compose, bir türün kararlı olduğunu yalnızca kanıtlayabildiği takdirde kabul eder. Örneğin, bir arayüz genellikle kararlı olarak kabul edilmez ve uygulanması değişmez olabilecek, değiştirilebilir genel özelliklere sahip türler de kararlı değildir.

Compose, bir türün kararlı olduğunu çıkaramıyorsa ancak Compose'un bunu kararlı olarak ele almasını zorlamak istiyorsanız türü @Stable ek açıklamasıyla işaretleyin.

// Marking the type as stable to favor skipping and smart recompositions.
@Stable
interface UiState<T : Result<T>> {
    val value: T?
    val exception: Throwable?

    val hasError: Boolean
        get() = exception != null
}

Yukarıdaki kod snippet'inde UiState bir arayüz olduğundan Compose, normalde bu türü kararlı olarak değerlendirmeyebilir. @Stable Açıklamasını ekleyerek Compose'a bu türün kararlı olduğunu bildirirsiniz. Böylece Compose, akıllı yeniden oluşturmaları tercih edebilir. Bu, arayüz parametre türü olarak kullanılıyorsa Compose'un tüm uygulamalarını kararlı olarak değerlendireceği anlamına da gelir.