यूज़र इंटरफ़ेस (यूआई) स्टेट प्रोडक्शन

मॉडर्न यूज़र इंटरफ़ेस (यूआई) में, शायद ही कभी स्टैटिक कॉन्टेंट होता है. जब उपयोगकर्ता यूज़र इंटरफ़ेस (यूआई) से इंटरैक्ट करता है या जब ऐप्लिकेशन को नया डेटा दिखाना होता है, तब यूज़र इंटरफ़ेस (यूआई) की स्थिति बदल जाती है.

इस दस्तावेज़ में, यूज़र इंटरफ़ेस (यूआई) की स्थिति को मैनेज करने और उसे बनाने के बारे में दिशा-निर्देश दिए गए हैं. इसके आखिर में आपको:

  • जानें कि यूज़र इंटरफ़ेस (यूआई) की स्थिति बनाने के लिए, आपको किन एपीआई का इस्तेमाल करना चाहिए. यह इस बात पर निर्भर करता है कि आपके स्टेट होल्डर में, स्टेट में बदलाव करने वाले सोर्स किस तरह के हैं. साथ ही, यह एकतरफ़ा डेटा फ़्लो के सिद्धांतों का पालन करता है.
  • जानें कि आपको यूज़र इंटरफ़ेस (यूआई) की स्थिति को कैसे तय करना चाहिए, ताकि सिस्टम के संसाधनों का सही तरीके से इस्तेमाल किया जा सके.
  • जानें कि यूज़र इंटरफ़ेस (यूआई) के इस्तेमाल के लिए, यूज़र इंटरफ़ेस (यूआई) की स्थिति को कैसे दिखाया जाना चाहिए.

असल में, स्टेट प्रोडक्शन का मतलब है कि यूज़र इंटरफ़ेस (यूआई) की स्थिति में इन बदलावों को धीरे-धीरे लागू करना. स्टेट हमेशा मौजूद रहती है और इवेंट के नतीजे के तौर पर बदलती है. इवेंट और स्थिति के बीच के अंतर के बारे में यहां दी गई टेबल में बताया गया है:

इवेंट राज्य
ये कुछ समय के लिए होते हैं, इनका अनुमान नहीं लगाया जा सकता, और ये एक तय समय के लिए ही मौजूद होते हैं. हमेशा मौजूद रहता है.
राज्य के प्रोडक्शन के इनपुट. राज्य के प्रोडक्शन का आउटपुट.
यूज़र इंटरफ़ेस (यूआई) या अन्य स्रोतों से मिला डेटा. इसका इस्तेमाल यूज़र इंटरफ़ेस (यूआई) करता है.

ऊपर दी गई जानकारी को याद रखने के लिए, यह निमोनिक इस्तेमाल किया जा सकता है: स्टेट बदलती है; इवेंट होते हैं. यहां दिए गए डायग्राम में, टाइमलाइन में इवेंट होने पर स्थिति में होने वाले बदलावों को विज़ुअलाइज़ करने में मदद मिलती है. हर इवेंट को सही स्टेट होल्डर प्रोसेस करता है. इससे स्टेट में बदलाव होता है:

इवेंट बनाम स्थिति
पहली इमेज: इवेंट की वजह से स्थिति में बदलाव होता है

इवेंट यहां से आ सकते हैं:

  • उपयोगकर्ता: ऐप्लिकेशन के यूज़र इंटरफ़ेस (यूआई) से इंटरैक्ट करते समय.
  • स्टेट में बदलाव के अन्य सोर्स: ऐसे एपीआई जो यूज़र इंटरफ़ेस (यूआई), डोमेन या डेटा लेयर से ऐप्लिकेशन का डेटा दिखाते हैं. जैसे, स्नैकबार टाइमआउट इवेंट, इस्तेमाल के उदाहरण या रिपॉज़िटरी.

यूज़र इंटरफ़ेस (यूआई) स्टेट प्रोडक्शन पाइपलाइन

Android ऐप्लिकेशन में स्टेट प्रोडक्शन को प्रोसेसिंग पाइपलाइन के तौर पर देखा जा सकता है. इसमें ये शामिल हैं:

  • इनपुट: ये ऐसे सोर्स होते हैं जिनसे स्थिति में बदलाव होता है. ये इनमें से कोई भी हो सकते हैं:
    • यूज़र इंटरफ़ेस (यूआई) लेयर के लिए स्थानीय: ये उपयोगकर्ता के इवेंट हो सकते हैं. जैसे, टास्क मैनेजमेंट ऐप्लिकेशन में "करने के लिए" टास्क के लिए उपयोगकर्ता का टाइटल डालना. इसके अलावा, ये ऐसे एपीआई भी हो सकते हैं जो यूआई लॉजिक को ऐक्सेस करने की सुविधा देते हैं. इससे यूज़र इंटरफ़ेस (यूआई) की स्थिति में बदलाव होता है. उदाहरण के लिए, Jetpack Compose में DrawerState पर open तरीके को कॉल करना.
    • यूज़र इंटरफ़ेस (यूआई) लेयर के बाहर: ये डोमेन या डेटा लेयर के ऐसे सोर्स होते हैं जिनकी वजह से यूज़र इंटरफ़ेस (यूआई) की स्थिति में बदलाव होता है. उदाहरण के लिए, NewsRepository या अन्य इवेंट से लोड हो चुकी खबरें.
    • ऊपर दिए गए सभी विकल्पों को मिलाकर.
  • स्टेट होल्डर: ये ऐसे टाइप होते हैं जो स्टेट में बदलाव करने वाले सोर्स पर बिज़नेस लॉजिक और/या यूज़र इंटरफ़ेस (यूआई) लॉजिक लागू करते हैं. साथ ही, यूज़र इवेंट को प्रोसेस करके यूज़र इंटरफ़ेस (यूआई) स्टेट बनाते हैं.
  • आउटपुट: यूज़र इंटरफ़ेस (यूआई) की वह स्थिति जिसे ऐप्लिकेशन रेंडर कर सकता है, ताकि उपयोगकर्ताओं को उनकी ज़रूरत की जानकारी मिल सके.
स्टेट प्रोडक्शन पाइपलाइन
दूसरी इमेज: राज्य के प्रोडक्शन की पाइपलाइन

स्टेट प्रोडक्शन एपीआई

स्टेट प्रोडक्शन में दो मुख्य एपीआई इस्तेमाल किए जाते हैं. ये इस बात पर निर्भर करते हैं कि आप पाइपलाइन के किस चरण में हैं:

पाइपलाइन स्टेज एपीआई
इनपुट आपको एसिंक्रोनस एपीआई का इस्तेमाल करना चाहिए, ताकि यूज़र इंटरफ़ेस (यूआई) थ्रेड से बाहर काम किया जा सके. इससे यूज़र इंटरफ़ेस (यूआई) में कोई रुकावट नहीं आएगी. उदाहरण के लिए, Kotlin में Coroutines या Flows और Java प्रोग्रामिंग लैंग्वेज में RxJava या कॉल बैक.
आउटपुट आपको ऑब्ज़र्वेबल डेटा होल्डर एपीआई का इस्तेमाल करना चाहिए, ताकि स्थिति बदलने पर यूज़र इंटरफ़ेस (यूआई) को अमान्य किया जा सके और उसे फिर से रेंडर किया जा सके. उदाहरण के लिए, StateFlow, Compose State या LiveData. Observable डेटा होल्डर यह पक्का करते हैं कि यूज़र इंटरफ़ेस (यूआई) के पास हमेशा स्क्रीन पर दिखाने के लिए यूज़र इंटरफ़ेस (यूआई) की स्थिति हो

इन दोनों में से, इनपुट के लिए एसिंक्रोनस एपीआई चुनने का असर, आउटपुट के लिए ऑब्ज़र्वेबल एपीआई चुनने के मुकाबले, स्टेट प्रोडक्शन पाइपलाइन पर ज़्यादा पड़ता है. ऐसा इसलिए है, क्योंकि इनपुट से यह तय होता है कि पाइपलाइन पर किस तरह की प्रोसेसिंग लागू की जा सकती है.

स्टेट प्रोडक्शन पाइपलाइन असेंबली

अगले सेक्शन में, अलग-अलग इनपुट के लिए सबसे सही स्टेट प्रोडक्शन तकनीक और उनसे मेल खाने वाले आउटपुट एपीआई के बारे में बताया गया है. हर स्टेट प्रोडक्शन पाइपलाइन, इनपुट और आउटपुट का कॉम्बिनेशन होती है. साथ ही, यह इस तरह की होनी चाहिए:

  • लाइफ़साइकल के बारे में जानकारी रखने वाला: अगर यूज़र इंटरफ़ेस (यूआई) नहीं दिख रहा है या चालू नहीं है, तो स्टेट प्रोडक्शन पाइपलाइन को किसी भी संसाधन का इस्तेमाल नहीं करना चाहिए. हालांकि, ऐसा तब किया जा सकता है, जब इसकी साफ़ तौर पर ज़रूरत हो.
  • समझने में आसान: यूज़र इंटरफ़ेस (यूआई) को, जनरेट किए गए यूज़र इंटरफ़ेस (यूआई) की स्थिति को आसानी से रेंडर करना चाहिए. स्टेट प्रोडक्शन पाइपलाइन के आउटपुट के लिए, अलग-अलग View API में अलग-अलग बातों का ध्यान रखना होगा. जैसे, View सिस्टम या Jetpack Compose.

स्टेट प्रोडक्शन पाइपलाइन में इनपुट

स्टेट प्रोडक्शन पाइपलाइन में इनपुट, स्टेट में बदलाव के सोर्स के बारे में इन तरीकों से जानकारी दे सकते हैं:

  • एक बार में होने वाले ऐसे ऑपरेशन जो सिंक्रोनस या एसिंक्रोनस हो सकते हैं. उदाहरण के लिए, suspend फ़ंक्शन को कॉल करना.
  • स्ट्रीम एपीआई, जैसे कि Flows.
  • ऊपर दिए गए सभी विकल्प.

यहां दिए गए सेक्शन में, ऊपर दिए गए हर इनपुट के लिए, स्टेट प्रोडक्शन पाइपलाइन को असेंबल करने का तरीका बताया गया है.

स्टेट में बदलाव के सोर्स के तौर पर वन-शॉट एपीआई

MutableStateFlow एपीआई का इस्तेमाल, स्टेट के ऐसे कंटेनर के तौर पर करें जिसे देखा जा सकता है और जिसमें बदलाव किया जा सकता है. Jetpack Compose ऐप्लिकेशन में, mutableStateOf का इस्तेमाल किया जा सकता है. खास तौर पर, Compose text API के साथ काम करते समय. दोनों एपीआई ऐसे तरीके उपलब्ध कराते हैं जिनसे वे होस्ट की गई वैल्यू को सुरक्षित तरीके से अपडेट कर सकते हैं. ये अपडेट सिंक्रोनस या एसिंक्रोनस हो सकते हैं.

उदाहरण के लिए, पासा फेंकने वाले किसी ऐप्लिकेशन में स्टेट अपडेट करने के बारे में सोचें. जब कोई उपयोगकर्ता पासा फेंकता है, तो Random.nextInt() सिंक्रोनस तरीके से काम करता है. इसके बाद, नतीजे को यूज़र इंटरफ़ेस (यूआई) की स्थिति में लिखा जाता है.

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,
            )
        }
    }
}

लिखने की स्थिति

@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
    }
}

एसिंक्रोनस कॉल से यूज़र इंटरफ़ेस (यूआई) की स्थिति में बदलाव करना

स्टेट में ऐसे बदलावों के लिए एसिंक्रोनस नतीजे की ज़रूरत होती है. इसके लिए, सही CoroutineScope में कोरूटीन लॉन्च करें. इससे CoroutineScope रद्द होने पर, ऐप्लिकेशन को काम बंद करने की अनुमति मिलती है. इसके बाद, स्टेट होल्डर, suspend तरीके के कॉल के नतीजे को उस observable API में लिखता है जिसका इस्तेमाल यूज़र इंटरफ़ेस (यूआई) की स्थिति को दिखाने के लिए किया जाता है.

उदाहरण के लिए, आर्किटेक्चर के सैंपल में मौजूद AddEditTaskViewModel को देखें. जब निलंबित करने वाली saveTask() विधि, किसी टास्क को एसिंक्रोनस तरीके से सेव करती है, तब MutableStateFlow पर मौजूद update विधि, स्थिति में हुए बदलाव को यूज़र इंटरफ़ेस (यूआई) की स्थिति में भेजती है.

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))
                }
            }
        }
    }
}

लिखने की स्थिति

@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))
            }
        }
    }
}

बैकग्राउंड थ्रेड से यूज़र इंटरफ़ेस (यूआई) की स्थिति में बदलाव करना

यूआई की स्थिति को अपडेट करने के लिए, मुख्य डिस्पैचर पर कोरूटीन लॉन्च करना बेहतर होता है. इसका मतलब है कि नीचे दिए गए कोड स्निपेट में, withContext ब्लॉक के बाहर. हालांकि, अगर आपको किसी दूसरे बैकग्राउंड कॉन्टेक्स्ट में यूज़र इंटरफ़ेस (यूआई) की स्थिति अपडेट करनी है, तो इन एपीआई का इस्तेमाल करके ऐसा किया जा सकता है:

  • withContext तरीके का इस्तेमाल करके, अलग-अलग कॉन्करेंट कॉन्टेक्स्ट में कोरूटीन चलाएं.
  • MutableStateFlow का इस्तेमाल करते समय, हमेशा की तरह update तरीके का इस्तेमाल करें.
  • Compose State का इस्तेमाल करते समय, Snapshot.withMutableSnapshot का इस्तेमाल करें, ताकि एक साथ कई अनुरोध मिलने पर भी State को एटॉमिक तरीके से अपडेट किया जा सके.

उदाहरण के लिए, मान लें कि नीचे दिए गए DiceRollViewModel स्निपेट में, SlowRandom.nextInt() एक ऐसा suspend फ़ंक्शन है जिसमें बहुत ज़्यादा कंप्यूटिंग की ज़रूरत होती है. इसे सीपीयू बाउंड कोरूटीन से कॉल किया जाना चाहिए.

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,
                    )
                }
            }
        }
    }
}

लिखने की स्थिति

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
                }
            }
        }
    }
}

स्टेट में बदलाव के सोर्स के तौर पर स्ट्रीम एपीआई

स्टेट में बदलाव करने वाले ऐसे सोर्स जो स्ट्रीम में समय के साथ कई वैल्यू जनरेट करते हैं उनके लिए, सभी सोर्स के आउटपुट को एक साथ इकट्ठा करना, स्टेट जनरेट करने का एक आसान तरीका है.

Kotlin फ़्लो का इस्तेमाल करते समय, combine फ़ंक्शन की मदद से ऐसा किया जा सकता है. इसका एक उदाहरण, InterestsViewModel में "Now in Android" सैंपल में देखा जा सकता है:

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 इंस्टेंस बनाना. कंपोज़ स्टेट से भी स्नैपशॉट फ़्लो बनाए जा सकते हैं.

नीचे दिए गए architecture-samples रिपॉज़िटरी से TaskDetailViewModel देखें:

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, 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 }
    }
}

लिखने की स्थिति

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
    }
}

स्टेट प्रॉडक्शन पाइपलाइन में आउटपुट टाइप

यूज़र इंटरफ़ेस (यूआई) की स्थिति के लिए आउटपुट एपीआई का चुनाव और उसे दिखाने का तरीका, काफ़ी हद तक उस एपीआई पर निर्भर करता है जिसका इस्तेमाल आपका ऐप्लिकेशन यूज़र इंटरफ़ेस (यूआई) को रेंडर करने के लिए करता है. Android ऐप्लिकेशन में, Views या Jetpack Compose का इस्तेमाल किया जा सकता है. इन बातों का ध्यान रखें:

यहां दी गई टेबल में, किसी भी इनपुट और उपभोक्ता के लिए, राज्य के हिसाब से प्रॉडक्शन पाइपलाइन के लिए इस्तेमाल किए जाने वाले एपीआई की खास जानकारी दी गई है:

इनपुट उपभोक्ता आउटपुट
एक बार इस्तेमाल किए जाने वाले एपीआई व्यू StateFlow या LiveData
एक बार इस्तेमाल किए जाने वाले एपीआई लिखें StateFlow या लिखें State
स्ट्रीमिंग एपीआई व्यू StateFlow या LiveData
स्ट्रीमिंग एपीआई लिखें StateFlow
एक बार में डेटा ट्रांसफ़र करने वाले और स्ट्रीम करने वाले एपीआई व्यू StateFlow या LiveData
एक बार में डेटा ट्रांसफ़र करने वाले और स्ट्रीम करने वाले एपीआई लिखें StateFlow

प्रोडक्शन पाइपलाइन के शुरू होने की स्थिति

स्टेट प्रोडक्शन पाइपलाइन शुरू करने के लिए, पाइपलाइन को चलाने की शुरुआती शर्तें सेट करनी होती हैं. इसमें पाइपलाइन शुरू करने के लिए, शुरुआती इनपुट वैल्यू देना शामिल हो सकता है. उदाहरण के लिए, किसी समाचार लेख के ज़्यादा जानकारी वाले व्यू के लिए id या एसिंक्रोनस लोड शुरू करना.

आपको स्टेट प्रोडक्शन पाइपलाइन को ज़रूरत के हिसाब से शुरू करना चाहिए, ताकि सिस्टम के संसाधनों को बचाया जा सके. आम तौर पर, इसका मतलब है कि आउटपुट का इस्तेमाल करने वाले व्यक्ति के उपलब्ध होने तक इंतज़ार करना. Flow एपीआई, stateIn तरीके में started आर्ग्युमेंट का इस्तेमाल करके, ऐसा करने की अनुमति देते हैं. जिन मामलों में यह लागू नहीं होता है उनमें, idempotent 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
        }
    }
}
ViewModel

सैंपल

यहां दिए गए Google के सैंपल से, यूज़र इंटरफ़ेस (यूआई) लेयर में स्टेट बनाने का तरीका पता चलता है. इन दिशा-निर्देशों को लागू करने के तरीके के बारे में जानने के लिए, इन्हें देखें: