Oluşturma'daki yan etkiler

Yan etki, uygulamanın durumunda kapsamı derlenebilir. composables nedeniyle yaşam döngüsü ve özellikleri gibi yeniden besteler, composable'ların farklı sıralarda yeniden kompozisyonlarını yürütme ya da atılabilecek yeniden kompozisyonlar, composable'ların ideal olarak yan etkiye sahip olmalıdır ücretsiz.

Ancak bazen yan etkiler, örneğin tek seferlik bir veya belirli bir anda başka bir ekrana gitme gibi bir etkinlik durum koşulu. Bu işlemler, kontrol edilen bir alanına sahip bir e-posta adresi kullanabilirsiniz. Bu sayfada Jetpack Compose'un sunduğu farklı yan etki API'leri hakkında bilgi edineceksiniz.

Durum ve sonuç kullanım alanları

Oluşturma Konusunda Düşünme bölümünde ele alındığı gibi composable'ların yan etki içermeyen olması gerekir. Bir sonraki aşamaya (bkz. Yönetim resmi belgeler dokümanı) dosyadaki Efekt Efekti'ni Bu yan etkilerin öngörülebilir şekilde yürütülmesi için API'leri devreye sokun.

Compose'da ortaya çıkan farklı olasılık efektleri nedeniyle rahatlıkla aşırı kullanılır. Bu öğelerde yaptığınız işin kullanıcı arayüzüyle Yönetim durumu bölümünde açıklandığı gibi tek yönlü veri akışını bozmaz belgelerine göz atın.

LaunchedEffect: Askıya alma işlevlerini bir composable kapsamında çalıştırma

Bir composable'ın ömrü boyunca iş yapmak ve işlevlerini kullanmak için LaunchedEffect composable'dan bahsetmek istiyorum. LaunchedEffect Beste'ye girdiğinde, ile örtüşen bir işlemdir. Eş yordam LaunchedEffect besteden ayrılırsa iptal edilir. LaunchedEffect ise yeniden oluşturabilirsiniz (bkz. Yeniden başlatma Efektler bölümünde yer alırsa mevcut eş yordam, iptal edildi ve yeni askıya alma işlevi yeni bir eş yordamda kullanıma sunulacak.

Örneğin, burada alfa değerini yapılandırılabilir gecikme:

// Allow the pulse rate to be configured, so it can be sped up if the user is running
// out of time
var pulseRateMs by remember { mutableStateOf(3000L) }
val alpha = remember { Animatable(1f) }
LaunchedEffect(pulseRateMs) { // Restart the effect when the pulse rate changes
    while (isActive) {
        delay(pulseRateMs) // Pulse the alpha every pulseRateMs to alert the user
        alpha.animateTo(0f)
        alpha.animateTo(1f)
    }
}

Yukarıdaki kodda, animasyonda, askıya alma işlevi delay gereken süreyi bekleyin. Ardından, çalıştırılan alfa testte kullanarak tekrar sıfıra dönün ve animateTo. Bu işlem, composable'ın kullanım ömrü boyunca tekrarlanır.

rememberCoroutineScope: composable dışında bir eş yordam başlatmak için besteye duyarlı bir kapsam elde edin

LaunchedEffect, composable bir işlev olduğundan yalnızca composable işlevleri. Bir composable'ın dışında bir eş yordam başlatmak için, Ancak bu ayardan ayrıldıktan sonra otomatik olarak iptal edilecek şekilde bileşim, kullanım rememberCoroutineScope. Ayrıca, dönüşüm hunisinin yaşam döngüsünü kontrol etmeniz gerektiğinde rememberCoroutineScope manuel olarak bir veya daha fazla eş yordamı manuel olarak gerçekleşmesidir.

rememberCoroutineScope, şunu döndüren bir composable işlevdir: CoroutineScope, bestenin çağrıldığı noktaya bağlıdır. İlgili içeriği oluşturmak için kullanılan kapsam iptal edilecek.

Önceki örneğe göre, bir Snackbar değerini göstermek için bu kodu kullanabilirsiniz Kullanıcı bir Button düğmesine dokunduğunda:

@Composable
fun MoviesScreen(snackbarHostState: SnackbarHostState) {

    // Creates a CoroutineScope bound to the MoviesScreen's lifecycle
    val scope = rememberCoroutineScope()

    Scaffold(
        snackbarHost = {
            SnackbarHost(hostState = snackbarHostState)
        }
    ) { contentPadding ->
        Column(Modifier.padding(contentPadding)) {
            Button(
                onClick = {
                    // Create a new coroutine in the event handler to show a snackbar
                    scope.launch {
                        snackbarHostState.showSnackbar("Something happened!")
                    }
                }
            ) {
                Text("Press me")
            }
        }
    }
}

rememberUpdatedState: Efektte, değer değişirse yeniden başlatılmaması gereken bir değere başvuru

Anahtar parametrelerden biri değiştiğinde LaunchedEffect yeniden başlatılır. Ancak, bazı durumlarda etkinizi yaratmak için kullanabileceğiniz efektin yeniden başlatılmasını istemezsiniz. Bunu yapmak için de bu değere referans oluşturmak için rememberUpdatedState kullanılması gerekir. kaydedilip güncellenebilir. Bu yaklaşım, uzun ömürlü, uzun ömürlü, uzun ömürlü operasyonlara, ayrıntılarına kadar yeniden başlat.

Örneğin, uygulamanızda, bir süre sonra kaybolan bir LandingScreen olduğunu varsayalım. gerekir. LandingScreen yeniden oluşturulsa bile bir süre bekleyen efekt ve geçen sürenin yeniden başlatılmaması gerektiğini bildirir:

@Composable
fun LandingScreen(onTimeout: () -> Unit) {

    // This will always refer to the latest onTimeout function that
    // LandingScreen was recomposed with
    val currentOnTimeout by rememberUpdatedState(onTimeout)

    // Create an effect that matches the lifecycle of LandingScreen.
    // If LandingScreen recomposes, the delay shouldn't start again.
    LaunchedEffect(true) {
        delay(SplashWaitTimeMillis)
        currentOnTimeout()
    }

    /* Landing screen content */
}

Telefon araması sitesinin yaşam döngüsüyle eşleşen bir efekt oluşturmak için Unit veya true gibi hiçbir zaman değişmeyen sabit değer parametre olarak iletilir. yukarıdaki kod LaunchedEffect(true) kullanılır. onTimeout lambda always, LandingScreen tarafından yeniden oluşturulan en son değeri içerir ile sarmalanması gerekir. onTimeout, rememberUpdatedState işleviyle sarmalanmalıdır. Kodda döndürülen State, currentOnTimeout, etkisi.

DisposableEffect: Temizlik gerektiren efektler

Tuşlar değiştikten sonra temizlenmesi gereken yan etkiler veya composable'ın Beste'den ayrılması, DisposableEffect. DisposableEffect tuşları değişirse composable'ın bu öğeleri imha etmesi gerekir. geçerli etkisinin olup olmadığını kontrol edin ve efekti tekrar çağırarak sıfırlayın.

Örneğin, Lifecycle etkinlik üzerinde bir LifecycleObserver. Bu etkinlikleri Compose'da dinlemek için DisposableEffect kullanarak kaydolun ve ve gerektiğinde gözlemcinin kaydını iptal edebilir.

@Composable
fun HomeScreen(
    lifecycleOwner: LifecycleOwner = LocalLifecycleOwner.current,
    onStart: () -> Unit, // Send the 'started' analytics event
    onStop: () -> Unit // Send the 'stopped' analytics event
) {
    // Safely update the current lambdas when a new one is provided
    val currentOnStart by rememberUpdatedState(onStart)
    val currentOnStop by rememberUpdatedState(onStop)

    // If `lifecycleOwner` changes, dispose and reset the effect
    DisposableEffect(lifecycleOwner) {
        // Create an observer that triggers our remembered callbacks
        // for sending analytics events
        val observer = LifecycleEventObserver { _, event ->
            if (event == Lifecycle.Event.ON_START) {
                currentOnStart()
            } else if (event == Lifecycle.Event.ON_STOP) {
                currentOnStop()
            }
        }

        // Add the observer to the lifecycle
        lifecycleOwner.lifecycle.addObserver(observer)

        // When the effect leaves the Composition, remove the observer
        onDispose {
            lifecycleOwner.lifecycle.removeObserver(observer)
        }
    }

    /* Home screen content */
}

Yukarıdaki kodda bu efekt, observer öğesini lifecycleOwner. lifecycleOwner değişirse efekt kaldırılır ve yeni lifecycleOwner ile yeniden başlatıldı.

DisposableEffect, nihai ifade olarak onDispose ifadesini içermelidir kendi kod bloğunda yer alır. Aksi takdirde, IDE'de derleme zamanı hatası gösterilir.

SideEffect: Oluştur durumunu, Compose olmayan koda yayınla

Oluşturma durumunu, oluşturma tarafından yönetilmeyen nesnelerle paylaşmak için SideEffect composable'dan bahsetmek istiyorum. SideEffect kullanmak, etkinin şu süreden sonra yürütülmesini garanti eder: en iyi yöntemin ne olduğunu öğreneceğiz. Diğer yandan, başarılı bir yeniden besteleme garanti edilmeden önce bir etki göstermesi, composable'a yazın.

Örneğin, analiz kitaplığınız kullanıcılarınızı segmentlere ayırmanıza özel meta veriler ekleyerek popülasyon (bu örnekte "kullanıcı özellikleri") ve sonraki tüm Analytics etkinliklerine katkıda bulunur. Verinin kullanıcı türünü analiz kitaplığınıza eklemek için SideEffect değerini kullanın.

@Composable
fun rememberFirebaseAnalytics(user: User): FirebaseAnalytics {
    val analytics: FirebaseAnalytics = remember {
        FirebaseAnalytics()
    }

    // On every successful composition, update FirebaseAnalytics with
    // the userType from the current User, ensuring that future analytics
    // events have this metadata attached
    SideEffect {
        analytics.setUserProperty("userType", user.userType)
    }
    return analytics
}

produceState: Oluşturma olmayan durumu, Oluştur durumuna dönüştürür

produceState değerleri bir kampanyaya aktarabilen Besteye ayarlanan State döndürüldü. Şunun için kullanın: Oluşturma dışı durumu Oluşturma durumuna dönüştürebilirsiniz (örneğin, kuruluş dışından URL'yi bir abonelik temelli kullanıcı durumu (örneğin, Flow, LiveData veya RxJava) Beste.

produceState besteye girdiğinde yapımcı başlatılır ve besteden ayrıldığında iptal edilir. Döndürülen State şunları içerir: aynı değerin ayarlanması yeniden besteyi tetiklemez.

produceState bir eş yordam oluştursa da, gözlemlemek için de kullanılabilir çalışmayan veri kaynaklarıdır. Bu kaynağa aboneliği kaldırmak için şunu kullanın: "the" awaitDispose işlevini kullanın.

Aşağıdaki örnekteproduceState ağ. loadNetworkImage composable işlevi, şunları yapabilecek bir State döndürür: başka composable'larda kullanılıyor.

@Composable
fun loadNetworkImage(
    url: String,
    imageRepository: ImageRepository = ImageRepository()
): State<Result<Image>> {

    // Creates a State<T> with Result.Loading as initial value
    // If either `url` or `imageRepository` changes, the running producer
    // will cancel and will be re-launched with the new inputs.
    return produceState<Result<Image>>(initialValue = Result.Loading, url, imageRepository) {

        // In a coroutine, can make suspend calls
        val image = imageRepository.load(url)

        // Update State with either an Error or Success result.
        // This will trigger a recomposition where this State is read
        value = if (image == null) {
            Result.Error
        } else {
            Result.Success(image)
        }
    }
}

derivedStateOf: Bir veya daha fazla durum nesnesini başka bir duruma dönüştürür

Compose'da yeniden düzenleme gerçekleşir. her gözlemlenen durum nesnesi veya composable giriş değiştiğinde. Durum nesnesi veya giriş, kullanıcı arayüzünün güncellenmesi gerekenden daha sık değişiyorsa Bu durum, gereksiz yeniden besteleme yapmaya neden olur.

derivedStateOf kullanmanız gerekir. bir composable'a yaptığınız girişler gerekenden daha sık değiştiğinde işlev çok önemli. Bu durum genellikle şunlar gibi sık sık bir değişiklik olduğunda ortaya çıkar: ancak composable'ın yalnızca ilettiğinde buna tepki vermesi gerekiyor. sağlayabilirsiniz. derivedStateOf, oluşturduğunuz yeni Oluştur durum nesnesini yalnızca ihtiyacınız olduğu kadar güncellendiğini görebilirsiniz. Bu şekilde, Kotlin Akışlarına benzer şekilde distinctUntilChanged() operatörümüzü kullanabilirsiniz.

Doğru kullanım

Aşağıdaki snippet, derivedStateOf için uygun bir kullanım alanını göstermektedir:

@Composable
// When the messages parameter changes, the MessageList
// composable recomposes. derivedStateOf does not
// affect this recomposition.
fun MessageList(messages: List<Message>) {
    Box {
        val listState = rememberLazyListState()

        LazyColumn(state = listState) {
            // ...
        }

        // Show the button if the first visible item is past
        // the first item. We use a remembered derived state to
        // minimize unnecessary compositions
        val showButton by remember {
            derivedStateOf {
                listState.firstVisibleItemIndex > 0
            }
        }

        AnimatedVisibility(visible = showButton) {
            ScrollToTopButton()
        }
    }
}

Bu snippet'te firstVisibleItemIndex, ilk görünür öğe her zaman değişir anlamına gelir. Kaydırırken değer 0, 1, 2, 3, 4, 5 vb. olur. Bununla birlikte, yeniden düzenleme yalnızca değer 0 değerinden büyük olduğunda gerçekleştirilmelidir. Güncelleme sıklığındaki bu uyumsuzluk, güncelleme sıklığındaki bu uyuşmazlık, derivedStateOf

Hatalı kullanım

İki Compose durum nesnesini birleştirdiğinizde "türetme durumu" olduğundan derivedStateOf parametresini kullanmalısınız. Ancak bu aşağıdaki snippet'te gösterildiği gibi tamamen ek yük oluşturur ve gerekli değildir:

// DO NOT USE. Incorrect usage of derivedStateOf.
var firstName by remember { mutableStateOf("") }
var lastName by remember { mutableStateOf("") }

val fullNameBad by remember { derivedStateOf { "$firstName $lastName" } } // This is bad!!!
val fullNameCorrect = "$firstName $lastName" // This is correct

Bu snippet'te, fullName eklentisinin de firstName ve lastName. Bu nedenle, aşırı bir yeniden oluşturma işlemi gerçekleşmez ve derivedStateOf gerekli değil.

snapshotFlow: Compose'un Durumunu Akışlara dönüştür

snapshotFlow kullanın State<T> değerini dönüştürmek için soğuk Akışa dönüştürebilir. snapshotFlow, toplanıp yayıldığında blokunu çalıştırır okunan State nesnenin sonucu. State nesneden biri snapshotFlow bloğunun içinde nasıl dönüştüğünü öğrenirseniz, Akış yeni değeri ya da yeni değer şuna eşit değilse toplayıcısına önceki yayınlanan değer (bu davranış, Flow.distinctUntilChanged).

Aşağıdaki örnekte, kullanıcı ekranı kaydırdığında kaydeden bir yan efekt gösterilmektedir listedeki ilk öğeyi analize yapıştırın:

val listState = rememberLazyListState()

LazyColumn(state = listState) {
    // ...
}

LaunchedEffect(listState) {
    snapshotFlow { listState.firstVisibleItemIndex }
        .map { index -> index > 0 }
        .distinctUntilChanged()
        .filter { it == true }
        .collect {
            MyAnalyticsService.sendScrolledPastFirstItemEvent()
        }
}

Yukarıdaki kodda, listState.firstVisibleItemIndex, önceki hâliyle akış operatörlerinin gücünden yararlanabilir.

Yeniden başlatma efektleri

Oluşturma'daki bazı efektler, ör. LaunchedEffect, produceState veya DisposableEffect, çalışan efekti iptal edip yeni tuşlarla yeni bir tane başlatın.

Bu API'lerin tipik biçimi şu şekildedir:

EffectName(restartIfThisKeyChanges, orThisKey, orThisKey, ...) { block }

Bu davranışın incelikleri nedeniyle, parametrelerin yalnızca yeniden başlatmak için kullanılanlar doğru olanlar değil:

  • Efektleri olması gerekenden daha az yeniden başlatmak uygulamanızda hatalara neden olabilir.
  • Efektleri olması gerekenden daha fazla yeniden başlatmak verimsiz olabilir.

Genel bir kural olarak, kodu, composable efekte parametre olarak eklenmelidir. Bunların dışında, efektin yeniden başlatılmasını zorunlu kılmak için daha fazla parametre eklenebilir. Eğer bir değişken etkinin yeniden başlatılmasına neden olmamalıdır, değişken rememberUpdatedState içinde. Değişken hiçbir zaman herhangi bir anahtar olmadan remember içine yerleştirildiği için değişkeni bir anahtar olarak geçirebilir.

Yukarıda gösterilen DisposableEffect kodunda efekt, lifecycleOwner bloğunda kullanılır, çünkü bu öğelerde yapılan herhangi bir değişiklik efektini kullanın.

@Composable
fun HomeScreen(
    lifecycleOwner: LifecycleOwner = LocalLifecycleOwner.current,
    onStart: () -> Unit, // Send the 'started' analytics event
    onStop: () -> Unit // Send the 'stopped' analytics event
) {
    // These values never change in Composition
    val currentOnStart by rememberUpdatedState(onStart)
    val currentOnStop by rememberUpdatedState(onStop)

    DisposableEffect(lifecycleOwner) {
        val observer = LifecycleEventObserver { _, event ->
            /* ... */
        }

        lifecycleOwner.lifecycle.addObserver(observer)
        onDispose {
            lifecycleOwner.lifecycle.removeObserver(observer)
        }
    }
}

DisposableEffect olarak currentOnStart ve currentOnStop gerekli değil kullanımı nedeniyle Beste'de hiçbir zaman değişmediğinden rememberUpdatedState. Parametre olarak lifecycleOwner değerini iletmezseniz ve değişir, HomeScreen yeniden oluşturur, ancak DisposableEffect bertaraf edilmez yeniden başlatıldı. Yanlış lifecycleOwner olduğundan bu durum sorunlara yol açar. kullanmaya devam edecek.

Anahtar olarak sabitler

true gibi bir sabit değeri efekt tuşu olarak kullanabilirsiniz: telefon araması sitesinin yaşam döngüsünü takip etmesini sağlayın. RACI matrisinde yukarıda gösterilen LaunchedEffect örneğine benzer. Ancak bunu yapmadan önce iki kere düşünüp ihtiyacınızın bu olduğundan emin olun.

ziyaret edin.