मैं Flows का उपयोग करने के लिए नया हूं। मेरे पास एक ऐसी स्थिति है जहां मुझे readProfileFromFirestore() को कॉल करने से पहले userLoginStatusChangedFlow पर कॉल करने के लिए इंतजार करना पड़ता है (जो एक Flow भी एकत्र करता है)। पहला यह जांचता है कि उपयोगकर्ता Firebase Auth में लॉग इन है, जबकि दूसरा उपयोगकर्ता की प्रोफ़ाइल जानकारी को Firestore से डाउनलोड करता है। मेरे पास कोड काम करता है, लेकिन मुझे यकीन नहीं है कि मैं इसे इच्छित तरीके से कर रहा हूं।

प्रश्न: क्या इस तरह "चेन" Flows का मानक अभ्यास है? क्या आप इसे अलग तरह से करेंगे?

    init {
        viewModelScope.launch {
            repository.userLoginStatusChangedFlow.collect { userLoggedIn: Boolean? ->
                if (userLoggedIn == true) {
                    launch {
                        readProfileFromFirestore()
                    }
                } else {
                    navigateToLoginFragment()
                }
            }
        }
    }

readProfileFromFirestore() विधि जिसे ऊपर कहा जाता है:

    // Download profile from Firestore and update the repository's cached profile.
    private suspend fun readProfileFromFirestore() {
        repository.readProfileFromFirestoreFlow().collect { state ->
            when (state) {
                is State.Success -> {
                    val profile: Models.Profile? = state.data
                    if (profile != null && repository.isProfileComplete(profile)) {
                        repository.updateCachedProfile(profile)
                        navigateToExploreFragment()
                    } else {
                        navigateToAuthenticationFragment()
                    }
                }
                is State.Failed -> {
                    // Error occurred while getting profile, so just inform user and go to LoginFragment.
                    displayErrorToast(state.throwable.message ?: "Failed to get profile")
                    navigateToAuthenticationFragment()
                }
                is State.Loading -> return@collect
            }
        }
    }
0
Gavin Wright 21 अगस्त 2021, 15:40

2 जवाब

सबसे बढ़िया उत्तर

आपके कोड के बारे में कुछ असामान्य बातें हैं।

सबसे पहले, यह असामान्य है कि आपके पास एकल मान का प्रवाह है। एक प्रवाह कई मानों के लिए होता है जो समय की अवधि में आते हैं। यदि पुनर्प्राप्त करने के लिए केवल एक आइटम है तो सस्पेंड फ़ंक्शन अधिक समझदार है। ऐसा लगता है कि आपके दोनों प्रवाहों में एक ही अजीब व्यवहार है।

दूसरा यह है कि आप कोरआउटिन के अंदर से एक कोरआउट को लॉन्च कर रहे हैं, जब यह आखिरी चीज है जो आपके कोरआउट को करने की आवश्यकता है। इस स्थिति के लिए अतिरिक्त जटिलता पेश करने का कोई कारण नहीं है। एक coroutine के अंदर launch को कॉल करना समझ में आता है यदि आपको उन अन्य घटनाओं की प्रतीक्षा किए बिना वर्तमान coroutine में और अधिक करने के लिए क्रियाओं की कुछ साइड चेन शुरू करने की आवश्यकता है। यहां ऐसा नहीं है।

कभी-कभी, वस्तुओं की एक सीमित संख्या के साथ प्रवाह की आवश्यकता होती है, लेकिन प्रवाह के लिए सैद्धांतिक रूप से अनंत होना अधिक सामान्य है, क्योंकि वे कुछ चल रहे राज्य को मतदान कर रहे हैं। इन स्थितियों में, इकट्ठा करने के बाद कोरआउटिन में कुछ भी करने की कोशिश करना अजीब होगा, क्योंकि आप नहीं जानते कि यह कब होगा या क्या होगा।

इसलिए, आपके कोड को देखते हुए, मैं कहूंगा कि आपका कोई भी प्रवाह पहले स्थान पर मौजूद नहीं होना चाहिए और आपको उन्हें निलंबित कार्यों से बदलना चाहिए।

लेकिन अगर किसी कारण से आप ऐसा नहीं कर सकते हैं (जैसे कि शायद यह राज्य परिवर्तन का प्रवाह है और आप केवल यह सुनना चाहते हैं कि किसी चीज़ की नवीनतम स्थिति क्या है, उस स्थिति पर प्रतिक्रिया करें और भविष्य के राज्य परिवर्तनों को अनदेखा करें), तो आप इसका उपयोग कर सकते हैं अपने कोरआउटिन को सरल बनाने के लिए collect() के बजाय first(), जैसे:

init {
    viewModelScope.launch {
        val userLoggedIn = repository.userLoginStatusChangedFlow.first()
        if (userLoggedIn == true) {
            readProfileFromFirestore()
        } else {
            navigateToLoginFragment()
        }
    }
}

private suspend fun readProfileFromFirestore() {
    val state = repository.readProfileFromFirestoreFlow().first()
    when (state) {
        //...
    }
}

ध्यान दें कि यदि आप अपने फ़्लो को सस्पेंड फ़ंक्शंस से बदल देते हैं तो यह कोड बहुत समान है। first() मूल रूप से फ़्लो को सस्पेंड फ़ंक्शन में परिवर्तित करता है।

1
Tenfour04 22 अगस्त 2021, 00:40

सबसे पहले, जैसा कि @ Tenfour04 ने बताया, first ऑपरेटर के साथ सस्पेंड फंक्शन आपके मामले के लिए पर्याप्त होगा,

यदि आप प्रवाह को नियोजित करने पर जोर देते हैं (जो मामलों के लिए उचित हो सकता है जैसे कि यदि आप उपयोगकर्ता-संवादात्मक पुनर्प्रयास प्रदान करते हैं), तो आप दो प्रवाहों को ऑपरेटर flatMap-क्लस्टर (flatMapMerge/flatMapConcat/flatMapLatest) के साथ जोड़ सकते हैं और एक का उपयोग कर सकते हैं collect मूल्य का उपभोग करने के लिए

val  credentials = flow {  emit(true) }
val  profiles = flow { emit(State.Loading) }

credentials.flatMapMerge { value -> 
   if (!value) flow { emit(State.NotLogin) } else profiles
}.collect { state ->
   // mapping your state here
}
1
Minami 2 सितंबर 2021, 12:05