Compose State'i kullanıcı arayüzü dışında kullanma

Jetpack Compose State<T> ve MutableState<T>, Compose anlık görüntü sistemi tarafından desteklenen genel amaçlı değer tutuculardır. En yaygın kullanım amacı kullanıcı arayüzü yeniden oluşturma işlemini tetiklemek olsa da anlık görüntü sistemi, kullanıcı arayüzü oluşturma işlemine bağlı değildir.

AppState kitaplıkları (Transform ve StateStore) sayesinde ViewModel'ler, yöneticiler, depolar ve arka plan hizmetleri genelinde Compose State öğelerini tutabilir, türetebilir, gözlemleyebilir ve kalıcı hale getirebilirsiniz.

Oluşturma durumunu kullanıcı arayüzünün ötesinde neden kullanmalısınız?

Kullanıcı arayüzü composable'ı, transform veya derivedStateOf gibi durumu gözlemleyen bir blokta Compose State değerini okuduğunuzda Compose çalışma zamanı, mimarinizin tüm katmanlarında iki önemli avantaj sağlar:

  • Otomatik ve dinamik bağımlılık izleme: Bir durumun .value değerini okumak (veya by özellik yetkilendirmesini kullanmak), bu durumu otomatik olarak bloğun bağımlılığı olarak kaydeder. İzleme dinamik olduğundan bağımlılıklar, çalışma zamanında hangi kod yollarının yürütüldüğüne göre belirlenir. Koşullu bir dal kullanılmazsa (örneğin, if kontrolünden veya erken return işleminden sonra) bu daldaki durumlar okunmaz ve bunlarda yapılan değişiklikler gereksiz yeniden hesaplamaları tetiklemez.
  • Tutarlılık ve ayrıntı düzeyi: Ayrı, ayrıntılı MutableState özellikleri tanımlayabilir ve çok özellikli yazma işlemlerini Snapshot.withMutableSnapshot ile sarmalayabilirsiniz. Ara güncellemeler, değiştirilebilir anlık görüntüye izole edilir ve blok tamamlandığında atomik olarak uygulanır. Bu sayede, anlık görüntü içinde okuma yapan gözlemciler hiçbir zaman tutarsız ara durum görmez ve yalnızca okudukları belirli özellikler değiştiğinde yeniden hesaplama yapar. StateFlow'nin aksine, tek bir monolitik StateFlow<UiState> (herhangi bir alan değiştiğinde her toplayıcıyı geçersiz kılar) ile ayrı StateFlow özellikleri (tek bir atomik işlemde birlikte güncellenemez) arasında seçim yapmanız gerekmez.

Mimariniz için doğru durum API'sini seçme

Bir durum parçasının ne kadar süreyle etkin kalması gerektiğine ve hangi bileşenlerin bu duruma erişmesi gerektiğine bağlı olarak Compose State'u nasıl tutacağınızı ve kalıcı hale getireceğinizi seçin:

Eyalet kategorisi Yaşam döngüsü ve kapsam Örnekler Önerilen Compose State API'leri
Canlı yayın durumu Her zaman bellekte depolanır. Başlangıçta yeni sorgulanır ve işlem sonlandırma sırasında kalıcı olmaz. Ağ bağlantısı durumu, Bluetooth bağlantısı durumu, öğe seçimi gibi geçici kullanıcı arayüzü durumu.
Geçici kullanıcı arayüzü durumu Yapılandırma değişikliklerinden ve sistem tarafından başlatılan işlem sonlandırmadan etkilenmez, süresiz olarak saklanmaz; tek bir ekran veya etkinlikle sınırlıdır. Genişletilmiş veya daraltılmış kullanıcı arayüzü öğeleri, kaydırma konumu, ekranda arama sorgusu veya filtre açma/kapatma düğmeleri
Geçici oturum durumu Arka plan işlemi sonlandırıldığında silinmez, süresiz olarak saklanmaz; etkinlikler, hizmetler veya widget'lar arasında paylaşılır. Etkin odaklanma zamanlayıcısı, çok adımlı ödeme sepeti veya devam eden form taslağı.
Kalıcı durum Diskte süresiz olarak saklanır. Kullanıcı tercihleri ve ayarları (DataStore), yapılandırılmış uygulama verileri ve medya kitaplıkları (Room). transform içinde collectAsState kullanılarak toplanan DataStore veya Room akışları

ViewModel'de Compose durumunu kullanma

ViewModel içinde, bağımsız ekran girişlerini ayrıntılı MutableState özellikleri olarak tanımlayabilir, SavedStateHandle.saveable ile işlem sonlandırma boyunca kalıcı hale getirebilir, Snapshot.withMutableSnapshot ile birden fazla özelliği atomik olarak güncelleyebilir ve bunları transform içinde depo Flow akışlarıyla birleştirebilirsiniz:

sealed interface TaskListState {
    data object Loading : TaskListState
    data class Success(
        val tasks: List<Task>,
        val selectedTaskIds: Set<String>,
        val isOnline: Boolean = true,
    ) : TaskListState
}

@HiltViewModel
class TaskListViewModel @Inject constructor(
    private val tasksRepository: TasksRepository,
    savedStateHandle: SavedStateHandle,
) : ViewModel() {

    var selectedTaskIds by mutableStateOf(emptySet<String>())
        private set

    var searchQuery by savedStateHandle.saveable { mutableStateOf("") }
        private set

    fun updateSearchQuery(query: String) {
        Snapshot.withMutableSnapshot {
            searchQuery = query
            selectedTaskIds = emptySet()
        }
    }

    val tasksState: State<TaskListState> = transform<TaskListState>(
        initialValue = TaskListState.Loading,
        scope = viewModelScope,
    ) {
        val tasks = remember(searchQuery) {
            tasksRepository.getTasks(searchQuery)
        }.collectAsState(initial = null).value
            ?: return@transform TaskListState.Loading

        TaskListState.Success(
            tasks = tasks,
            selectedTaskIds = selectedTaskIds,
        )
    }
}

Bu ViewModel belgesindeki temel örüntüler:

  • Reaktif kaydedilmiş durum için SavedStateHandle.saveable kullanın: mutableStateOf ile searchQuery öğesini SavedStateHandle.saveable öğesine devretmek (androidx.lifecycle:lifecycle-viewmodel-compose üzerinden), değeri işlem sonlandırma boyunca MutableState olarak korur. Böylece transform, okumaları otomatik olarak izler. Yalnızca SavedStateHandle.saved kullanmaktan kaçının. Bu, Compose State yerine düz bir Kotlin özelliği döndürür ve değiştirildiğinde yeniden oluşturmayı tetiklemez.
  • Snapshot.withMutableSnapshot ile atomik çoklu özellik yazma: Snapshot.withMutableSnapshot içinde searchQuery ve selectedTaskIds değerlerini güncellemek, yazma işlemlerini izole eder ve her iki değişikliği de tek bir atomik anlık görüntü işleminde uygular. Böylece gözlemciler (transform dahil), yeni searchQuery değerini eski selectedTaskIds değeriyle eşlenmiş olarak görmek yerine her iki güncellemeyi de tek bir yeniden oluşturma geçişinde birlikte görür.
  • remember(key) ile yeniden oluşturma işlemlerinde akışları yeniden kullanma: remember içindeki tasksRepository.getTasks(searchQuery) öğesini anahtar olarak searchQuery ile sarmalamak, selectedTaskIds değiştiğinde ve transform bloğunu yeniden oluşturduğunda Compose'un sorguyu yeniden başlatmak yerine mevcut veritabanı Flow öğesini yeniden kullanmasını sağlar.

Uygulamanızda canlı sistem durumunu paylaşma

Birden fazla ViewModel, etkinlik veya hizmet arasında paylaşılan canlı sistem durumu (ör. ağ veya Bluetooth bağlantı durumu) için işlem sonlandırma sırasında eski durumu diske kaydetmek istemezsiniz. Bunun yerine, başlangıçta geçerli sistem durumunu sorgulayın, uygulama kapsamlı bir singleton içinde özel bir mutableStateOf özelliğinde tutun ve salt okunur bir State<T> gösterin:

interface NetworkMonitor {
    val isOnline: State<Boolean>
}

@Singleton
class LiveNetworkMonitor @Inject constructor(
    @ApplicationContext context: Context,
) : NetworkMonitor {
    private val connectivityManager =
        context.getSystemService(ConnectivityManager::class.java)

    private val _isOnline = mutableStateOf(checkCurrentConnectivity())
    override val isOnline: State<Boolean> = _isOnline

    private val callback = object : ConnectivityManager.NetworkCallback() {
        override fun onCapabilitiesChanged(
            network: Network,
            networkCapabilities: NetworkCapabilities,
        ) {
            _isOnline.value = networkCapabilities.hasCapability(
                NetworkCapabilities.NET_CAPABILITY_INTERNET
            )
        }

        override fun onLost(network: Network) {
            _isOnline.value = false
        }
    }

    init {
        connectivityManager.registerDefaultNetworkCallback(callback)
    }

    private fun checkCurrentConnectivity(): Boolean {
        val network = connectivityManager.activeNetwork ?: return false
        val capabilities =
            connectivityManager.getNetworkCapabilities(network) ?: return false
        return capabilities.hasCapability(
            NetworkCapabilities.NET_CAPABILITY_INTERNET
        )
    }
}

MutableState, herhangi bir iş parçacığından tutarlı okuma ve güncellemeleri destekler. Bu sayede NetworkCallback, _isOnline.value öğesini doğrudan arka plan geri çağırma iş parçacığından güncelleyebilir. Arka plan geri çağırması birden fazla MutableState özelliğini birlikte güncelliyorsa yazma işlemlerini Snapshot.withMutableSnapshot içine sarmalayarak atomik olarak uygulanmalarını sağlayın.

Uygulamanızdaki herhangi bir bileşen, networkMonitor.isOnline.value değerini okuyabilir. Bu okuma işlemi tek seferlik bir kontrol olarak, kullanıcı arayüzü composable'ı içinde veya listener, derivedStateOf ya da transform gibi bir durum gözlemleme bloğu içinde yapılabilir. Örneğin, TaskListViewModel, transform bloğundaki networkMonitor.isOnline.value değerini okuyabilir:

val tasksState: State<TaskListState> = transform<TaskListState>(
    initialValue = TaskListState.Loading,
    scope = viewModelScope,
) {
    val tasks = remember(searchQuery) {
        tasksRepository.getTasks(searchQuery)
    }.collectAsState(initial = null).value
        ?: return@transform TaskListState.Loading

    TaskListState.Success(
        tasks = tasks,
        selectedTaskIds = selectedTaskIds,
        isOnline = networkMonitor.isOnline.value,
    )
}

transform, searchQuery, selectedTaskIds ve Flow deposuyla birlikte networkMonitor.isOnline.value öğesini de izlediğinden, bağlantıda yapılan herhangi bir değişiklik, veritabanını yeniden sorgulamadan TaskListState.Success öğesini otomatik olarak yeniden hesaplar.