Modern kullanıcı arayüzleri nadiren statiktir. Kullanıcı arayüzün durumu, kullanıcı arayüzüyle veya uygulamanın yeni veriler görüntülemesi gerektiğinde etkileşime girdiğinde.
Bu belgede, kullanıcı arayüzü üretimi ve yönetimi için yönergeler açıklanmaktadır durumu. Belgenin sonunda şunları yapmalısınız:
- Kullanıcı arayüzü durumu oluşturmak için hangi API'leri kullanmanız gerektiğini bilin. Bu işlem, devlet sahiplerinizin elinde bulunan durum değişikliği kaynaklarının niteliği, tek yönlü veri akışı ilkelerini uygulayın.
- Kullanıcı arayüzü durumu üretiminin kapsamını nasıl belirlemeniz gerektiğini sistem kaynakları.
- Kullanıcı arayüzü kullanımı için kullanıcı arayüzü durumunu nasıl göstereceğinizi öğrenin.
Esasen, eyalet üretimi bu değişikliklerin artımlı uygulanmasıdır. bunu değiştirebilirsiniz. Durum her zaman vardır ve etkinliklerin sonucunda değişir. İlgili içeriği oluşturmak için kullanılan etkinlikler ve durum arasındaki farklar aşağıdaki tabloda özetlenmiştir:
Etkinlikler | Eyalet |
---|---|
Geçici, öngörülemez ve sınırlı bir süre boyunca var olan bir değişimdir. | Her zaman vardır. |
Durum üretimi girişleri. | Durum üretimi çıktısı. |
Kullanıcı arayüzü veya diğer kaynakların ürünü. | Kullanıcı arayüzü tarafından kullanılır. |
Yukarıdakileri özetleyen mükemmel bir hatırlatma: durum; nasıl meydana geldiğine bakalım. İlgili içeriği oluşturmak için kullanılan diyagram, olaylar bir zaman çizelgesinde gerçekleştikçe, durumdaki değişikliklerin görselleştirilmesine yardımcı olur. Her etkinlik, uygun eyalet sahibi tarafından işlenir ve durum değişikliği:
![Etkinlikler ve durum](https://developer.android.google.cn/static/images/topic/architecture/ui-layer/events-vs-state.png?authuser=9&hl=tr)
Etkinliklerin kaynağı:
- Kullanıcılar: Uygulamanın kullanıcı arayüzüyle etkileşim kurarken.
- Diğer durum değişikliği kaynakları: Kullanıcı arayüzünden uygulama verileri sunan API'ler. veya atıştırmalık çubuğu zaman aşımı etkinlikleri, kullanım alanları veya kod depoları bulunur.
Kullanıcı arayüzü durumu üretim ardışık düzeni
Android uygulamalarında durum üretimi, bir işleme ardışık düzeni olarak düşünülebilir şunları içerir:
- Girişler: Durum değişikliğinin kaynakları. Bu ayarlar şunlardan biri olabilir:
- Kullanıcı arayüzü katmanında yerel: Bunlar, bir kullanıcının
"yapılacak" başlığı API’larda da gösterilebilir.
Kullanıcı arayüzü durumundaki değişiklikleri tetikleyen kullanıcı arayüzü mantığına erişim. Örneğin,
Jetpack Compose'daki
DrawerState
ile ilgiliopen
yöntemi çağrılıyor. - Kullanıcı arayüzü katmanının harici: Bunlar, alan adından veya verilerden gelen kaynaklardır
katmanlardan daha az katmanla seçim yapabilirsiniz. Örneğin, sona ermiş haberler
NewsRepository
veya başka bir etkinlikten yükleniyor. - Yukarıdakilerin bir karışımı.
- Kullanıcı arayüzü katmanında yerel: Bunlar, bir kullanıcının
"yapılacak" başlığı API’larda da gösterilebilir.
Kullanıcı arayüzü durumundaki değişiklikleri tetikleyen kullanıcı arayüzü mantığına erişim. Örneğin,
Jetpack Compose'daki
- Eyalet sahipleri: İş mantığı ve/veya geçerli olan türler Durum değişikliğinin kaynaklarına kullanıcı arayüzü mantığı ve kullanıcı etkinliklerini üreterek işler Kullanıcı arayüzü durumu.
- Çıkış: Kullanıcılara anlayabiliyorsunuz.
![Durum üretim ardışık düzeni](https://developer.android.google.cn/static/images/topic/architecture/ui-layer/state-production-pipeline.png?authuser=9&hl=tr)
Durum üretim API'leri
Eyalet üretiminde kullanılan aşamaya bağlı olarak iki ana API vardır. mevcut iş hattınız:
Ardışık düzen aşaması | API |
---|---|
Giriş | Kullanıcı arayüzü iş parçacığının olumsuz etkilenmemesi amacıyla kullanıcı arayüzü iş parçacığı üzerinde çalışmak için eşzamansız API'ler kullanmanız gerekir. Örneğin, Kotlin'deki Kotlinler veya Akışlar, RxJava ya da Java Programlama Dili'ndeki geri çağırmalar. |
Çıkış | Durum değiştiğinde kullanıcı arayüzünü geçersiz kılmak ve yeniden oluşturmak için gözlemlenebilir veri sahibi API'lerini kullanmanız gerekir. Örneğin, StateFlow, Compose State veya LiveData. Gözlemlenebilir veri sahipleri, kullanıcı arayüzünün her zaman ekranda görüntülenecek bir kullanıcı arayüzü durumuna sahip olacağını garanti eder |
Giriş için eşzamansız API'nin seçilmesi, bu iki yöntemden daha fazla etkiye sahiptir: gözlemlenebilir API seçiminden çok durum üretim hattının yapısı çıktı. Bunun nedeni, girişlerin etkili olabilecek ardışık düzene uygulanması gerekir.
Durum üretim ardışık düzeni montajı
Sonraki bölümlerde, birçok farklı işletme türüne en uygun devlet üretim tekniklerini ve eşleşen çıkış API'leri gösterilir. Her eyalet üretim ardışık düzeni bir giriş ve çıkışlarının birleşiminden oluşur ve şöyle olmalıdır:
- Yaşam döngüsüne duyarlı: Kullanıcı arayüzünün görünür veya etkin olmadığı durumlarda, devlet üretim ardışık düzeni, açıkça incelenmediği sürece gereklidir.
- Kullanımı kolay: Kullanıcı arayüzü, üretilen kullanıcı arayüzünü kolayca oluşturabilmelidir. durumu. Eyalet üretim ardışık düzeninin çıktısıyla ilgili dikkat edilmesi gereken noktalar View sistemi veya Jetpack Compose gibi farklı View API'lerinde değişiklik gösterebilir.
Durum üretim ardışık düzenlerindeki girişler
Durum üretim hattındaki girişler, kendi durum kaynaklarını sağlayabilir şununla değiştir:
- Eşzamanlı veya eşzamansız olabilecek tek seferlik işlemler (ör.
suspend
işlevlerine çağrı. - Akış API'leri (ör.
Flows
). - Yukarıdakilerin tümü.
Aşağıdaki bölümlerde durum üretim ardışık düzenini nasıl derleyebileceğiniz açıklanmaktadır yukarıdaki girişlerin her biri için.
Durum değişikliği kaynakları olarak tek seferlik API'ler
MutableStateFlow
API'yi gözlemlenebilir ve değişebilir.
durum kapsayıcısıdır. Jetpack Compose uygulamalarında şu içerikleri de kullanabilirsiniz:
mutableStateOf
, özellikle de
Metin API'leri oluşturun. Her iki API de güvenli olan yöntemler sunar.
olmalarına bakılmaksızın barındırdıkları değerlerde yapılacak atomik güncellemeler
senkronize edilebilir.
Örneğin, basit bir zar atma uygulamasında durum güncellemelerini ele alabilirsiniz. Her zar
Kullanıcının zarı eşzamanlı
Random.nextInt()
yöntemini kullanır ve sonuç,
Kullanıcı arayüzü durumu.
Durum Akışı
data class DiceUiState(
val firstDieValue: Int? = null,
val secondDieValue: Int? = null,
val numberOfRolls: Int = 0,
)
class DiceRollViewModel : ViewModel() {
private val _uiState = MutableStateFlow(DiceUiState())
val uiState: StateFlow<DiceUiState> = _uiState.asStateFlow()
// Called from the UI
fun rollDice() {
_uiState.update { currentState ->
currentState.copy(
firstDieValue = Random.nextInt(from = 1, until = 7),
secondDieValue = Random.nextInt(from = 1, until = 7),
numberOfRolls = currentState.numberOfRolls + 1,
)
}
}
}
Oluşturma Durumu
@Stable
interface DiceUiState {
val firstDieValue: Int?
val secondDieValue: Int?
val numberOfRolls: Int?
}
private class MutableDiceUiState: DiceUiState {
override var firstDieValue: Int? by mutableStateOf(null)
override var secondDieValue: Int? by mutableStateOf(null)
override var numberOfRolls: Int by mutableStateOf(0)
}
class DiceRollViewModel : ViewModel() {
private val _uiState = MutableDiceUiState()
val uiState: DiceUiState = _uiState
// Called from the UI
fun rollDice() {
_uiState.firstDieValue = Random.nextInt(from = 1, until = 7)
_uiState.secondDieValue = Random.nextInt(from = 1, until = 7)
_uiState.numberOfRolls = _uiState.numberOfRolls + 1
}
}
Eşzamansız çağrılarda kullanıcı arayüzü durumunu değiştirme
Eşzamansız sonuç gerektiren durum değişiklikleri için
uygun CoroutineScope
. Bu sayede,
CoroutineScope
iptal edildi. Eyalet sahibi daha sonra
askıya alma yöntemi çağrısı, kullanıcı arayüzü durumunu göstermek için kullanılan gözlemlenebilir API'ye
Örneğin, AddEditTaskViewModel
Mimari örneği. Askıya alma saveTask()
yöntemi
bir görevi eşzamansız olarak kaydeder.update
MutableStateFlow, durum değişikliğini kullanıcı arayüzü durumuna yayar.
Durum Akışı
data class AddEditTaskUiState(
val title: String = "",
val description: String = "",
val isTaskCompleted: Boolean = false,
val isLoading: Boolean = false,
val userMessage: String? = null,
val isTaskSaved: Boolean = false
)
class AddEditTaskViewModel(...) : ViewModel() {
private val _uiState = MutableStateFlow(AddEditTaskUiState())
val uiState: StateFlow<AddEditTaskUiState> = _uiState.asStateFlow()
private fun createNewTask() {
viewModelScope.launch {
val newTask = Task(uiState.value.title, uiState.value.description)
try {
tasksRepository.saveTask(newTask)
// Write data into the UI state.
_uiState.update {
it.copy(isTaskSaved = true)
}
}
catch(cancellationException: CancellationException) {
throw cancellationException
}
catch(exception: Exception) {
_uiState.update {
it.copy(userMessage = getErrorMessage(exception))
}
}
}
}
}
Oluşturma Durumu
@Stable
interface AddEditTaskUiState {
val title: String
val description: String
val isTaskCompleted: Boolean
val isLoading: Boolean
val userMessage: String?
val isTaskSaved: Boolean
}
private class MutableAddEditTaskUiState : AddEditTaskUiState() {
override var title: String by mutableStateOf("")
override var description: String by mutableStateOf("")
override var isTaskCompleted: Boolean by mutableStateOf(false)
override var isLoading: Boolean by mutableStateOf(false)
override var userMessage: String? by mutableStateOf<String?>(null)
override var isTaskSaved: Boolean by mutableStateOf(false)
}
class AddEditTaskViewModel(...) : ViewModel() {
private val _uiState = MutableAddEditTaskUiState()
val uiState: AddEditTaskUiState = _uiState
private fun createNewTask() {
viewModelScope.launch {
val newTask = Task(uiState.value.title, uiState.value.description)
try {
tasksRepository.saveTask(newTask)
// Write data into the UI state.
_uiState.isTaskSaved = true
}
catch(cancellationException: CancellationException) {
throw cancellationException
}
catch(exception: Exception) {
_uiState.userMessage = getErrorMessage(exception))
}
}
}
}
Arka plandaki iş parçacıklarından kullanıcı arayüzü durumunu değiştirme
Eş yordamların üretimin ana sevk görevlisinde başlatılması tercih edilir
durumunu gösterir. Yani, kod snippet'lerindeki withContext
bloğunun dışında
bölümüne göz atın. Ancak, kullanıcı arayüzü durumunu farklı bir arka planda güncellemeniz gerekirse
aşağıdaki API'leri kullanarak bunu yapabilirsiniz:
- Eş yordamları çalıştırmak için
withContext
yöntemini kullanın farklı eşzamanlı bağlam MutableStateFlow
kullanılırkenupdate
yöntemini şu şekilde kullanın: her zamanki gibi.- Oluşturma Durumunu kullanırken
Snapshot.withMutableSnapshot
kullanarak Eş zamanlı bağlamda Durum güncellemelerini garanti edebilir.
Örneğin aşağıdaki DiceRollViewModel
snippet'inde
SlowRandom.nextInt()
, işlem yükü açısından yoğun bir suspend
fonksiyonunu çağırın.
Durum Akışı
class DiceRollViewModel(
private val defaultDispatcher: CoroutineScope = Dispatchers.Default
) : ViewModel() {
private val _uiState = MutableStateFlow(DiceUiState())
val uiState: StateFlow<DiceUiState> = _uiState.asStateFlow()
// Called from the UI
fun rollDice() {
viewModelScope.launch() {
// Other Coroutines that may be called from the current context
…
withContext(defaultDispatcher) {
_uiState.update { currentState ->
currentState.copy(
firstDieValue = SlowRandom.nextInt(from = 1, until = 7),
secondDieValue = SlowRandom.nextInt(from = 1, until = 7),
numberOfRolls = currentState.numberOfRolls + 1,
)
}
}
}
}
}
Oluşturma Durumu
class DiceRollViewModel(
private val defaultDispatcher: CoroutineScope = Dispatchers.Default
) : ViewModel() {
private val _uiState = MutableDiceUiState()
val uiState: DiceUiState = _uiState
// Called from the UI
fun rollDice() {
viewModelScope.launch() {
// Other Coroutines that may be called from the current context
…
withContext(defaultDispatcher) {
Snapshot.withMutableSnapshot {
_uiState.firstDieValue = SlowRandom.nextInt(from = 1, until = 7)
_uiState.secondDieValue = SlowRandom.nextInt(from = 1, until = 7)
_uiState.numberOfRolls = _uiState.numberOfRolls + 1
}
}
}
}
}
Durum değişikliği kaynakları olarak API'lerin akışı
Akışlarda zaman içinde birden çok değer oluşturan durum değişikliği kaynakları için Tüm kaynakların çıktılarının çıktılarını tutarlı bir bütün halinde toplamak, basit bir yaklaşım ortaya koyduk.
Kotlin Akışları'nı kullanırken bunu birleştirme işlevini kullanın. Bunun bir örneğini "Artık Android'de" sample'ı ekleyin:
class InterestsViewModel(
authorsRepository: AuthorsRepository,
topicsRepository: TopicsRepository
) : ViewModel() {
val uiState = combine(
authorsRepository.getAuthorsStream(),
topicsRepository.getTopicsStream(),
) { availableAuthors, availableTopics ->
InterestsUiState.Interests(
authors = availableAuthors,
topics = availableTopics
)
}
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000),
initialValue = InterestsUiState.Loading
)
}
StateFlows
oluşturmak için stateIn
operatörünün kullanılması, kullanıcı arayüzünün daha ayrıntılı olmasını sağlar.
resmi üretim hattının faaliyetleri üzerinde kontrol sahibi olmayı
yalnızca kullanıcı arayüzü görünür olduğunda etkin olmalıdır.
- Ardışık düzenin yalnızca etkin olması gerekiyorsa
SharingStarted.WhileSubscribed()
kullanın yaşam döngüsüne duyarlı bir şekilde akışı toplarken kullanıcı arayüzü görünür olduğunda şekilde ele alacağız. - Ardışık düzenin
SharingStarted.Lazily
kullanıcı arayüzüne geri dönebilir. Yani, kullanıcı arayüzü geri yığınında tıklayın.
Akışa dayalı durum kaynaklarını toplamanın geçerli olmadığı durumlarda, akış Kotlin Flows gibi API'ler, dijital reklam izlemede olduğu gibi birleştirme, birleştirme vb. akışları kullanıcı arayüzü durumuna işleme konusunda yardım alabilirsiniz.
Durum değişikliği kaynakları olarak tek seferlik ve akış API'leri
Durum üretim hattının her iki tek seferlik çağrıya bağlı olduğu durumlarda ve akışlar, durum değişikliği kaynağı olarak akışlar ise belirleyici kısıtlamadır. Bu nedenle, tek seferlik çağrıları akışlara dönüştürün. API'ler veya çıkışları akışlara bağlayın ve açıklanan şekilde işlemeyi devam ettirin. "İçerikler ve Denemeler"i seçin.
Akışlar söz konusu olduğunda genellikle bir veya daha fazla gizli arka plan
Durum değişikliklerini yaymak için MutableStateFlow
örnek. Ayrıca transkriptinizi
Oluştur durumundan anlık görüntü akışları oluşturabilirsiniz.
Şuradan TaskDetailViewModel
kullanabilirsiniz:
architecture-samples deposu aşağıdaki gibidir:
Durum Akışı
class TaskDetailViewModel @Inject constructor(
private val tasksRepository: TasksRepository,
savedStateHandle: SavedStateHandle
) : ViewModel() {
private val _isTaskDeleted = MutableStateFlow(false)
private val _task = tasksRepository.getTaskStream(taskId)
val uiState: StateFlow<TaskDetailUiState> = combine(
_isTaskDeleted,
_task
) { isTaskDeleted, task ->
TaskDetailUiState(
task = taskAsync.data,
isTaskDeleted = isTaskDeleted
)
}
// Convert the result to the appropriate observable API for the UI
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000),
initialValue = TaskDetailUiState()
)
fun deleteTask() = viewModelScope.launch {
tasksRepository.deleteTask(taskId)
_isTaskDeleted.update { true }
}
}
Oluşturma Durumu
class TaskDetailViewModel @Inject constructor(
private val tasksRepository: TasksRepository,
savedStateHandle: SavedStateHandle
) : ViewModel() {
private var _isTaskDeleted by mutableStateOf(false)
private val _task = tasksRepository.getTaskStream(taskId)
val uiState: StateFlow<TaskDetailUiState> = combine(
snapshotFlow { _isTaskDeleted },
_task
) { isTaskDeleted, task ->
TaskDetailUiState(
task = taskAsync.data,
isTaskDeleted = isTaskDeleted
)
}
// Convert the result to the appropriate observable API for the UI
.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000),
initialValue = TaskDetailUiState()
)
fun deleteTask() = viewModelScope.launch {
tasksRepository.deleteTask(taskId)
_isTaskDeleted = true
}
}
Durum üretim ardışık düzenlerindeki çıkış türleri
Kullanıcı arayüzü durumu için çıkış API'sinin seçimi ve gösterilme şekli büyük ölçüde uygulamanızın kullanıcı arayüzünü oluşturmak için kullandığı API'ye bağlıdır. Android uygulamalarında, Görünümler veya Jetpack Compose'u seçebilir. Dikkat edilmesi gereken noktalar şunlardır:
- Yaşam döngüsüne duyarlı bir şekilde okuma durumu.
- Eyaletin bir veya daha fazla alana durumlardan biridir.
Aşağıdaki tabloda durum üretiminiz için hangi API'lerin kullanılacağı özetlenmiştir. iş akış düzeni oluşturmaya devam eder:
Giriş | Tüketici | Çıkış |
---|---|---|
Tek seferlik API'ler | Görüntüleme sayısı | StateFlow veya LiveData |
Tek seferlik API'ler | Oluştur | StateFlow veya Oluştur State |
Akış API'leri | Görüntüleme sayısı | StateFlow veya LiveData |
Akış API'leri | Oluştur | StateFlow |
Tek seferlik ve akış API'leri | Görüntüleme sayısı | StateFlow veya LiveData |
Tek seferlik ve akış API'leri | Oluştur | StateFlow |
Durum üretim ardışık düzeni başlatma
Durum üretim ardışık düzenlerinin başlatılması, başlangıç koşullarının ayarlanmasını içerir
Ardışık düzenin çalışmasını sağlar. Bu işlem, başlangıçtaki giriş değerlerinin sağlanmasını içerebilir
Ardışık düzenin başlatılması açısından kritik öneme sahiptir, örneğin bir id
görüntüleme veya eşzamansız yükleme başlatma.
Mümkün olduğunda durum üretim ardışık düzenini gecikmeli olarak ilk kullanıma hazırlamanız gerekir.
koruyabilirsiniz.
Pratikte bu, çoğu zaman söz konusu ürünün tüketicisi
çıktı. Flow
API'leri
stateIn
içindeki started
bağımsız değişkeni
yöntemidir. Bunun geçerli olmadığı durumlarda,
idempotent tanımlayın
Durum üretim ardışık düzenini açıkça başlatmak için initialize()
işlevi
aşağıdaki snippet'te gösterildiği gibidir:
class MyViewModel : ViewModel() {
private var initializeCalled = false
// This function is idempotent provided it is only called from the UI thread.
@MainThread
fun initialize() {
if(initializeCalled) return
initializeCalled = true
viewModelScope.launch {
// seed the state production pipeline
}
}
}
Örnekler
Aşağıdaki Google örnekleri, Amerika Birleşik Devletleri'ndeki kullanıcı arayüzü katmanı. Uygulamadaki bu rehberliği görmek için bu yöntemleri inceleyin:
Sizin için önerilenler
- Not: JavaScript kapalıyken bağlantı metni gösterilir
- Kullanıcı arayüzü katmanı
- Çevrimdışına öncelik veren bir uygulama geliştirme
- Eyalet sahipleri ve Kullanıcı Arayüzü Durumu {:#mad-arch}