मॉर्डन यूज़र इंटरफ़ेस (यूआई) आम तौर पर स्टैटिक नहीं होते. जब उपयोगकर्ता यूज़र इंटरफ़ेस (यूआई) से इंटरैक्ट करता है या जब ऐप्लिकेशन को नया डेटा दिखाना होता है, तब यूज़र इंटरफ़ेस (यूआई) की स्थिति बदल जाती है.
इस दस्तावेज़ में, यूज़र इंटरफ़ेस (यूआई) की स्थिति के प्रोडक्शन और मैनेजमेंट के लिए दिशा-निर्देश दिए गए हैं. इसका मकसद, आपको इन बातों को समझने में मदद करना है:
- यूज़र इंटरफ़ेस (यूआई) की स्थिति को बनाने के लिए, किन एपीआई का इस्तेमाल करना चाहिए. यह इस बात पर निर्भर करता है कि आपके स्टेट होल्डर में, स्टेट में बदलाव करने के लिए कौनसे सोर्स उपलब्ध हैं. साथ ही, यह एकतरफ़ा डेटा फ़्लो के सिद्धांतों के मुताबिक काम करता है.
- सिस्टम के संसाधनों का ध्यान रखते हुए, यूज़र इंटरफ़ेस (यूआई) की स्थिति के प्रोडक्शन को कैसे स्कोप किया जाए.
- यूज़र इंटरफ़ेस (यूआई) के इस्तेमाल के लिए, यूज़र इंटरफ़ेस (यूआई) की स्थिति को कैसे दिखाया जाए.
असल में, स्टेट प्रोडक्शन, यूज़र इंटरफ़ेस (यूआई) की स्थिति में इन बदलावों को धीरे-धीरे लागू करने की प्रोसेस है. स्टेट हमेशा मौजूद रहती है और इवेंट की वजह से इसमें बदलाव होता है. नीचे दी गई टेबल में, इवेंट और स्टेट के बीच के अंतर के बारे में बताया गया है:
| इवेंट | स्टेट |
|---|---|
| ट्रांज़िएंट और अनप्रेडिक्टेबल होते हैं. साथ ही, ये कुछ समय के लिए ही मौजूद रहते हैं. | हमेशा मौजूद रहती है. |
| स्टेट प्रोडक्शन के इनपुट होते हैं. | स्टेट प्रोडक्शन का आउटपुट होता है. |
| यूज़र इंटरफ़ेस (यूआई) या अन्य सोर्स का प्रॉडक्ट होता है. | यूज़र इंटरफ़ेस (यूआई) इसका इस्तेमाल करता है. |
ऊपर दी गई जानकारी को याद रखने के लिए, यह स्टेट इज़; इवेंट हैपन एक अच्छा निमोनिक है. नीचे दिए गए डायग्राम से, टाइमलाइन में इवेंट होने पर स्टेट में होने वाले बदलावों को विज़ुअलाइज़ करने में मदद मिलती है. हर इवेंट को, सही स्टेट होल्डर प्रोसेस करता है. इससे स्टेट में बदलाव होता है:
इवेंट, इन सोर्स से आ सकते हैं:
- उपयोगकर्ता: जब वे ऐप्लिकेशन के यूज़र इंटरफ़ेस (यूआई) से इंटरैक्ट करते हैं.
- स्टेट में बदलाव करने वाले अन्य सोर्स: ऐसे एपीआई जो यूज़र इंटरफ़ेस (यूआई), डोमेन या डेटा लेयर से ऐप्लिकेशन का डेटा दिखाते हैं. जैसे, स्नैकबार टाइमआउट इवेंट, इस्तेमाल के उदाहरण या रिपॉज़िटरी.
यूज़र इंटरफ़ेस (यूआई) की स्थिति के प्रोडक्शन का पाइपलाइन
Android ऐप्लिकेशन में स्टेट प्रोडक्शन को एक प्रोसेसिंग पाइपलाइन के तौर पर देखा जा सकता है. इसमें ये शामिल हैं:
- इनपुट: स्टेट में बदलाव करने वाले सोर्स. ये हो सकते हैं:
- यूज़र इंटरफ़ेस (यूआई) लेयर के लिए लोकल: ये उपयोगकर्ता के इवेंट हो सकते हैं. जैसे, टास्क मैनेजमेंट ऐप्लिकेशन में "टू-डू" के लिए कोई
टाइटल डालना. इसके अलावा, ये ऐसे एपीआई हो सकते हैं जो
यूज़र इंटरफ़ेस (यूआई) लॉजिक का ऐक्सेस देते हैं. इससे यूज़र इंटरफ़ेस (यूआई) की स्थिति में बदलाव होता है. उदाहरण के लिए,
Jetpack Compose में
DrawerStateपरopenतरीके को कॉल करना. - यूज़र इंटरफ़ेस (यूआई) लेयर के लिए बाहरी: ये डोमेन या डेटा लेयर के सोर्स होते हैं. इनकी वजह से यूज़र इंटरफ़ेस (यूआई) की स्थिति में बदलाव होता है. उदाहरण के लिए,
NewsRepositoryया अन्य इवेंट से लोड हो चुकी खबरें. - ऊपर दिए गए सोर्स का मिक्सचर.
- यूज़र इंटरफ़ेस (यूआई) लेयर के लिए लोकल: ये उपयोगकर्ता के इवेंट हो सकते हैं. जैसे, टास्क मैनेजमेंट ऐप्लिकेशन में "टू-डू" के लिए कोई
टाइटल डालना. इसके अलावा, ये ऐसे एपीआई हो सकते हैं जो
यूज़र इंटरफ़ेस (यूआई) लॉजिक का ऐक्सेस देते हैं. इससे यूज़र इंटरफ़ेस (यूआई) की स्थिति में बदलाव होता है. उदाहरण के लिए,
Jetpack Compose में
- स्टेट होल्डर: ये ऐसे टाइप होते हैं जो स्टेट में बदलाव करने वाले सोर्स पर, कारोबार के लॉजिक और यूज़र इंटरफ़ेस (यूआई) के लॉजिक को लागू करते हैं. साथ ही, ये यूज़र इंटरफ़ेस (यूआई) की स्थिति को बनाने के लिए, उपयोगकर्ता के इवेंट को प्रोसेस करते हैं.
- आउटपुट: यूज़र इंटरफ़ेस (यूआई) की वह स्थिति जिसे ऐप्लिकेशन रेंडर कर सकता है, ताकि उपयोगकर्ताओं को उनकी ज़रूरत की जानकारी मिल सके.
स्टेट प्रोडक्शन एपीआई
स्टेट प्रोडक्शन में दो मुख्य एपीआई का इस्तेमाल किया जाता है. यह इस बात पर निर्भर करता है कि पाइपलाइन के किस स्टेज पर काम किया जा रहा है:
| पाइपलाइन स्टेज | एपीआई |
|---|---|
| इनपुट | यूज़र इंटरफ़ेस (यूआई) थ्रेड से अलग काम करने के लिए, कोरूटीन और फ़्लो जैसे एसिंक्रोनस एपीआई का इस्तेमाल करें, ताकि यूज़र इंटरफ़ेस (यूआई) में कोई गड़बड़ी न हो. |
| आउटपुट | स्टेट में बदलाव होने पर, यूज़र इंटरफ़ेस (यूआई) को अमान्य करने और फिर से रेंडर करने के लिए, Compose State या StateFlow जैसे ऑब्ज़र्वेबल डेटा होल्डर एपीआई का इस्तेमाल करें. ऑब्ज़र्वेबल डेटा होल्डर यह पक्का करते हैं कि यूज़र इंटरफ़ेस (यूआई) के पास स्क्रीन पर दिखाने के लिए हमेशा यूज़र इंटरफ़ेस (यूआई) की स्थिति मौजूद हो. |
इनपुट के लिए एसिंक्रोनस एपीआई चुनने का, स्टेट प्रोडक्शन पाइपलाइन पर ज़्यादा असर पड़ता है. वहीं, आउटपुट के लिए ऑब्ज़र्वेबल एपीआई चुनने का असर कम पड़ता है. ऐसा इसलिए, क्योंकि इनपुट यह तय करते हैं कि पाइपलाइन पर किस तरह की प्रोसेसिंग लागू की जा सकती है.
स्टेट प्रोडक्शन पाइपलाइन असेंबली
अगले सेक्शन में, अलग-अलग इनपुट के लिए सबसे सही स्टेट प्रोडक्शन के तरीकों और उनसे मेल खाने वाले आउटपुट एपीआई के बारे में बताया गया है. हर स्टेट प्रोडक्शन पाइपलाइन, इनपुट और आउटपुट का कॉम्बिनेशन होती है. साथ ही, यह इनमें से हर शर्त को पूरा करती है:
- लाइफ़साइकल के बारे में जानकारी देने वाली: अगर यूज़र इंटरफ़ेस (यूआई) नहीं दिख रहा है या चालू नहीं है, तो स्टेट प्रोडक्शन पाइपलाइन को कोई भी संसाधन इस्तेमाल नहीं करना चाहिए. हालांकि, अगर इसकी साफ़ तौर पर ज़रूरत हो, तो इस्तेमाल किया जा सकता है.
- इस्तेमाल में आसान: यूज़र इंटरफ़ेस (यूआई) को, बनाए गए यूज़र इंटरफ़ेस (यूआई) की स्थिति को आसानी से रेंडर करना चाहिए. Jetpack Compose में, स्टेट का इस्तेमाल यूज़र इंटरफ़ेस (यूआई) के लिए ज़रूरी है, क्योंकि कंपोज़ेबल, स्टेट में होने वाले बदलावों के आधार पर अपडेट हो सकते हैं.
स्टेट प्रोडक्शन पाइपलाइन में इनपुट
स्टेट प्रोडक्शन पाइपलाइन में इनपुट, स्टेट में बदलाव करने वाले सोर्स की जानकारी इन तरीकों से देते हैं:
- एक बार की जाने वाली कार्रवाइयां, जो सिंक्रोनस या एसिंक्रोनस हो सकती हैं. उदाहरण के लिए,
suspendफ़ंक्शन को कॉल करना. - स्ट्रीम एपीआई. उदाहरण के लिए,
Flows. - ऊपर दिए गए सभी विकल्प.
यहां दिए गए सेक्शन में, ऊपर बताए गए हर इनपुट के लिए, स्टेट प्रोडक्शन पाइपलाइन को असेंबल करने का तरीका बताया गया है.
स्टेट में बदलाव करने वाले सोर्स के तौर पर, एक बार की जाने वाली कार्रवाइयों के लिए एपीआई
ऑब्ज़र्वेबल डेटा होल्डर की मदद से स्टेट मैनेज करें. mutableStateOf एपीआई का इस्तेमाल करें,
खास तौर पर, Compose टेक्स्ट एपीआई के साथ काम करते समय. ज़्यादा जटिल स्टेट
मैनेजमेंट के लिए या अन्य आर्किटेक्चर कॉम्पोनेंट के साथ इंटिग्रेट करते समय, MutableStateFlow एपीआई का इस्तेमाल करें. दोनों एपीआई ऐसे तरीके उपलब्ध कराते हैं जिनकी मदद से, होस्ट की गई वैल्यू को सुरक्षित तरीके से एटॉमिक अपडेट किया जा सकता है. भले ही, अपडेट सिंक्रोनस हों या एसिंक्रोनस.
उदाहरण के लिए, पासे को रोल करने वाले किसी आसान ऐप्लिकेशन में स्टेट अपडेट को देखें. उपयोगकर्ता के पासे को रोल करने पर, सिंक्रोनस Random.nextInt तरीका लागू होता है. साथ ही, नतीजे को यूज़र इंटरफ़ेस (यूआई) की स्थिति में लिखा जाता है.
Compose State
@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
}
}
StateFlow
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,
)
}
}
}
एसिंक्रोनस कॉल से यूज़र इंटरफ़ेस (यूआई) की स्थिति में बदलाव करना
स्टेट में ऐसे बदलाव करने के लिए जिनके लिए एसिंक्रोनस नतीजे की ज़रूरत होती है, सही CoroutineScope में कोरूटीन लॉन्च करें. इससे, CoroutineScope रद्द होने पर ऐप्लिकेशन, काम को रद्द कर सकता है. इसके बाद, स्टेट होल्डर, सस्पेंड किए गए तरीके को कॉल करने के नतीजे को, यूज़र इंटरफ़ेस (यूआई) की स्थिति दिखाने के लिए इस्तेमाल किए जाने वाले ऑब्ज़र्वेबल एपीआई में लिखता है.
उदाहरण के लिए, AddEditTaskViewModel को
आर्किटेक्चर सैंपल में देखें. जब सस्पेंड किया गया saveTask तरीका, किसी टास्क को
एसिंक्रोनस तरीके से सेव करता है, तब update तरीका, MutableStateFlow पर मौजूद स्टेट में हुए
बदलाव को यूज़र इंटरफ़ेस (यूआई) की स्थिति में भेजता है.
Compose State
@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))
}
}
}
}
StateFlow
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))
}
}
}
}
}
बैकग्राउंड थ्रेड से यूज़र इंटरफ़ेस (यूआई) की स्थिति में बदलाव करना
यूज़र इंटरफ़ेस (यूआई) की स्थिति के प्रोडक्शन के लिए, कोरूटीन को मुख्य डिस्पैचर पर लॉन्च करना बेहतर होता है. इसका मतलब है कि नीचे दिए गए कोड स्निपेट में, withContext ब्लॉक के बाहर.
हालांकि, अगर आपको किसी दूसरे बैकग्राउंड कॉन्टेक्स्ट में यूज़र इंटरफ़ेस (यूआई) की स्थिति को अपडेट करना है, तो यह तरीका अपनाएं:
- अलग-अलग
कॉन्करंट कॉन्टेक्स्ट में कोरूटीन चलाने के लिए,
withContextतरीके का इस्तेमाल करें. MutableStateFlowका इस्तेमाल करते समय, हमेशा की तरहupdateतरीके का इस्तेमाल करें.- Compose State का इस्तेमाल करते समय, कॉन्करंट कॉन्टेक्स्ट में स्टेट को एटॉमिक अपडेट करने की गारंटी देने के लिए,
Snapshot.withMutableSnapshotतरीके का इस्तेमाल करें.
उदाहरण के लिए, मान लें कि नीचे दिए गए DiceRollViewModel स्निपेट में, SlowRandom.nextInt कंप्यूटेशन के हिसाब से इंटेंसिव suspend फ़ंक्शन है. इसे सीपीयू-बाउंड कोरूटीन से कॉल किया जाना चाहिए.
Compose State
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
}
}
}
}
}
StateFlow
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,
)
}
}
}
}
}
स्टेट में बदलाव करने वाले सोर्स के तौर पर, स्ट्रीम एपीआई
स्टेट में बदलाव करने वाले ऐसे सोर्स के लिए जो स्ट्रीम में समय के साथ-साथ कई वैल्यू बनाते हैं, सभी सोर्स के आउटपुट को एक साथ जोड़ना, स्टेट प्रोडक्शन का एक आसान तरीका है.
Kotlin Flows का इस्तेमाल करते समय, इसे कंबाइन फ़ंक्शन की मदद से किया जा सकता है.
इसका एक उदाहरण, "Now in Android" सैंपल में
InterestsViewModel में देखा जा सकता है:
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 बनाने के लिए, stateIn ऑपरेटर का इस्तेमाल करने से, यूज़र इंटरफ़ेस (यूआई) को स्टेट प्रोडक्शन पाइपलाइन की गतिविधि पर बेहतर कंट्रोल मिलता है. ऐसा इसलिए, क्योंकि हो सकता है कि इसे सिर्फ़ तब चालू रखने की ज़रूरत हो, जब यूज़र इंटरफ़ेस (यूआई) दिख रहा हो.
- अगर पाइपलाइन को सिर्फ़ तब चालू रखने की ज़रूरत है, जब यूज़र इंटरफ़ेस (यूआई) दिख रहा हो, तो लाइफ़साइकल के बारे में जानकारी देने वाले तरीके से फ़्लो इकट्ठा करते समय,
SharingStarted.WhileSubscribedका इस्तेमाल करें. - अगर पाइपलाइन को तब तक चालू रखने की ज़रूरत है, जब तक उपयोगकर्ता यूज़र इंटरफ़ेस (यूआई) पर वापस आ सकता है, तो
SharingStarted.Lazilyका इस्तेमाल करें. इसका मतलब है कि यूज़र इंटरफ़ेस (यूआई), बैकस्टैक पर है या स्क्रीन से बाहर किसी अन्य टैब में है.
ऐसे मामलों में जहां स्टेट के स्ट्रीम-आधारित सोर्स को एग्रीगेट नहीं किया जा सकता, वहां स्ट्रीम एपीआई, जैसे Kotlin Flows, स्ट्रीम को यूज़र इंटरफ़ेस (यूआई) की स्थिति में प्रोसेस करने में मदद करने के लिए, मर्जिंग, फ़्लैटनिंग वगैरह जैसे कई तरह के ट्रांसफ़ॉर्मेशन उपलब्ध कराते हैं.
स्टेट में बदलाव करने वाले सोर्स के तौर पर, एक बार की जाने वाली कार्रवाइयों के लिए एपीआई और स्ट्रीम एपीआई
अगर स्टेट प्रोडक्शन पाइपलाइन, स्टेट में बदलाव करने वाले सोर्स के तौर पर, एक बार की जाने वाली कार्रवाइयों के लिए एपीआई और स्ट्रीम, दोनों पर निर्भर करती है, तो स्ट्रीम, तय करने वाली कंस्ट्रेंट होती हैं. इसलिए, एक बार की जाने वाली कार्रवाइयों के लिए एपीआई को स्ट्रीम एपीआई में बदलें या उनके आउटपुट को स्ट्रीम में पाइप करें. इसके बाद, ऊपर दिए गए स्ट्रीम सेक्शन में बताए गए तरीके से प्रोसेसिंग जारी रखें.
फ़्लो के साथ, इसका आम तौर पर मतलब है कि स्टेट में होने वाले बदलावों को आगे बढ़ाने के लिए, एक या उससे ज़्यादा प्राइवेट बैकिंग MutableStateFlow इंस्टेंस बनाना. Compose स्टेट से स्नैपशॉट फ़्लो भी बनाए
जा सकते हैं.
आर्किटेक्चर-सैंपल रिपॉज़िटरी से TaskDetailViewModel को देखें. यूज़र इंटरफ़ेस (यूआई) की स्थिति, मौजूदा टास्क (_task) के लिए स्ट्रीम और एक बार की जाने वाली कार्रवाइयों के लिए एपीआई (_isTaskDeleted) पर निर्भर करती है. जब टास्क मिटाया जाता है, तब यह अपडेट होता है. इस फ़्लैग की मदद से, यह पता चलता है कि डेटाबेस में कोई टास्क क्यों नहीं मिला. जैसे, गलत आईडी की वजह से या उपयोगकर्ता ने उसे मिटा दिया है:
Compose State
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, taskAsync ->
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
}
}
StateFlow
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, taskAsync ->
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 }
}
}
स्टेट प्रोडक्शन पाइपलाइन में आउटपुट के टाइप
यूज़र इंटरफ़ेस (यूआई) की स्थिति के लिए आउटपुट एपीआई का चुनाव और उसकी प्रस्तुति का तरीका, इस बात पर निर्भर करता है कि आपका ऐप्लिकेशन, यूज़र इंटरफ़ेस (यूआई) को रेंडर करने के लिए किस एपीआई का इस्तेमाल करता है. जैसे, Compose. Jetpack Compose, नेटिव यूज़र इंटरफ़ेस (यूआई) बनाने के लिए, आधुनिक टूलकिट है. हम इसका इस्तेमाल करने का सुझाव देते हैं. यहां इन बातों का ध्यान रखें:
- लाइफ़साइकल के बारे में जानकारी देने वाले तरीके से स्टेट को पढ़ना.
- स्टेट होल्डर से स्टेट को एक या उससे ज़्यादा फ़ील्ड में दिखाना.
नीचे दी गई टेबल में, Jetpack Compose का इस्तेमाल करते समय, स्टेट प्रोडक्शन पाइपलाइन के लिए इस्तेमाल किए जाने वाले एपीआई के बारे में बताया गया है:
| इनपुट | आउटपुट |
|---|---|
| एक बार की जाने वाली कार्रवाइयों के लिए एपीआई | StateFlow या Compose State |
| स्ट्रीम एपीआई | StateFlow |
| एक बार की जाने वाली कार्रवाइयों के लिए एपीआई और स्ट्रीम एपीआई | StateFlow |
स्टेट प्रोडक्शन पाइपलाइन को शुरू करना
स्टेट प्रोडक्शन पाइपलाइन को शुरू करने के लिए, पाइपलाइन को चलाने के लिए शुरुआती शर्तें सेट करनी होती हैं. इसमें, पाइपलाइन को शुरू करने के लिए ज़रूरी शुरुआती इनपुट वैल्यू देना शामिल हो सकता है. उदाहरण के लिए, किसी न्यूज़ आर्टिकल के ब्यौरे वाले व्यू के लिए id या एसिंक्रोनस लोड शुरू करना.
सिस्टम के संसाधनों को बचाने के लिए, स्टेट प्रोडक्शन पाइपलाइन को लेज़ी तरीके से शुरू करें. आम तौर पर, इसका मतलब है कि आउटपुट के इस्तेमाल होने तक इंतज़ार करना. Flow एपीआई, stateIn तरीके में started आर्ग्युमेंट
की मदद से इसकी अनुमति देते हैं. ऐसे मामलों में जहां यह लागू नहीं होता, वहां स्टेट
प्रोडक्शन पाइपलाइन को साफ़ तौर पर शुरू करने के लिए, आइडमपोटेंट initialize फ़ंक्शन तय करें. इसके लिए, यहां दिया गया स्निपेट देखें:
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
}
}
}
सैंपल
Google के इन सैंपल में, यूज़र इंटरफ़ेस (यूआई) लेयर में स्टेट के प्रोडक्शन के बारे में बताया गया है. इन्हें एक्सप्लोर करके देखें कि इस गाइडेंस को कैसे लागू किया जाता है:
अन्य संसाधन
यूज़र इंटरफ़ेस (यूआई) की स्थिति के बारे में ज़्यादा जानने के लिए, ये संसाधन देखें:
दस्तावेज़
Views का कॉन्टेंट
आपके लिए सुझाव
- ध्यान दें: JavaScript बंद होने पर, लिंक का टेक्स्ट दिखता है
- यूज़र इंटरफ़ेस (यूआई) लेयर
- ऑफ़लाइन-फ़र्स्ट ऐप्लिकेशन बनाना
- स्टेट होल्डर और यूज़र इंटरफ़ेस (यूआई) की स्थिति {:#mad-arch}