RxJava to Coroutines: एंड-टू-एंड फीचर माइग्रेशन

छवि

(मूल रूप से मध्यम पर प्रकाशित)

Kotlin coroutines केवल हल्के धागों से बहुत अधिक हैं - वे एक नए प्रतिमान हैं जो डेवलपर्स को एक संरचित और मुहावरेदार तरीके से संगामिति से निपटने में मदद करता है।

एंड्रॉइड ऐप को विकसित करते समय किसी को कई अलग-अलग चीजों पर विचार करना चाहिए: यूआई थ्रेड से लंबे समय तक चलने वाले संचालन को लेना, जीवनचक्र की घटनाओं को संभालना, सदस्यता रद्द करना, उपयोगकर्ता इंटरफ़ेस को अपडेट करने के लिए यूआई थ्रेड पर वापस स्विच करना। पिछले कुछ वर्षों में RxJava समस्याओं के इस सेट को हल करने के लिए सबसे अधिक उपयोग किए जाने वाले ढाँचों में से एक बन गया। इस लेख में मैं आपको RxJava से कोरआउट्स के लिए एंड-टू-एंड फीचर माइग्रेशन के माध्यम से मार्गदर्शन करने जा रहा हूं।

फ़ीचर


हम जिस सुविधा को कोरटाइन में बदलने जा रहे हैं, वह काफी सरल है: जब उपयोगकर्ता किसी देश को सबमिट करता है, तो हम यह जांचने के लिए एपीआई कॉल करते हैं कि क्या कंपनी हाउस जैसे किसी प्रदाता के माध्यम से व्यापार विवरण देखने के लिए पात्र है। यदि कॉल सफल था, तो हम प्रतिक्रिया दिखाते हैं, यदि नहीं - त्रुटि संदेश।

प्रवास



हम अपने कोड को रिट्रोफिट सेवा से शुरू करते हुए एक अप-अप दृष्टिकोण में स्थानांतरित करने जा रहे हैं, एक रिपॉजिटरी लेयर तक, फिर एक इंटरेक्टर लेयर तक और अंत में एक ViewModel पर जा रहे हैं।

वर्तमान में सिंगल लौटाने वाले फ़ंक्शंस सस्पेंड होने वाले फ़ंक्शंस और फ़ंक्शंस होने चाहिए, जो ऑब्ज़र्वेबल को लौटाते हैं, फ़्लो लौटना चाहिए। इस विशेष उदाहरण में हम फ्लो के साथ कुछ भी नहीं करने जा रहे हैं।

रेट्रोफिट सेवा


चलिए सीधे कोड में कूदते हैं और BusinessLookupService को BusinessLookupService में coroutines में रिफलैक्टर करते हैं। यह अब कैसा दिखता है।

interface BusinessLookupService { @GET("v1/eligibility") fun businessLookupEligibility( @Query("countryCode") countryCode: String ): Single<NetworkResponse<BusinessLookupEligibilityResponse, ErrorResponse>> } 

रिफैक्टरिंग चरण:

  1. संस्करण के साथ शुरू 2.6.0 रेट्रोफ़िट सस्पेंड संशोधक का समर्थन करता है। चलिए BusinessLookupEligibility विधि को एक निलंबित फ़ंक्शन में बदलते हैं।
  2. सिंगल रैपर को वापसी प्रकार से निकालें।

 interface BusinessLookupService { @GET("v1/eligibility") suspend fun businessLookupEligibility( @Query("countryCode") countryCode: String ): NetworkResponse<BusinessLookupEligibilityResponse, ErrorResponse> } 

NetworkResponse एक सीलबंद वर्ग है जो BusinessLookupEligibilityResponse या ErrorResponse का प्रतिनिधित्व करता है। NetworkResponse का निर्माण एक कस्टम रेट्रोफिट कॉल अडैप्टर में किया जाता है। इस तरह हम डेटा प्रवाह को केवल दो संभावित मामलों तक ही सीमित रखते हैं - सफलता या त्रुटि, इसलिए BusinessLookupService के उपभोक्ताओं को अपवाद से निपटने की चिंता करने की आवश्यकता नहीं है।

कोष


चलो आगे बढ़ते हैं और देखते हैं कि हमारे पास BusinessLookupRepository में क्या है। BusinessLookupEligibility method बॉडी में हम businessLookupService.businessLookupEligibility (जिसे हमने अभी रिफलेक्ट किया है) कहते हैं और RxJava के मैप ऑपरेटर का उपयोग करके NetworkResponse को एक परिणाम के रूप में बदलने के लिए और डोमेन मॉडल के लिए प्रतिक्रिया मॉडल बनाते हैं। परिणाम एक अन्य सील वर्ग है जो Result.Success का प्रतिनिधित्व करता है और इसमें नेटवर्क नेटवर्क कॉल सफल होने पर TheBusinessLookupEligibility ऑब्जेक्ट शामिल है। यदि नेटवर्क कॉल में कोई त्रुटि थी, डिसेरिएलाइज़ेशन अपवाद या कुछ और गलत हो गया तो हम Result.Failure का निर्माण एक सार्थक त्रुटि संदेश के साथ करते हैं (ErrorMessage Stringealias for String)।

 class BusinessLookupRepository @Inject constructor( private val businessLookupService: BusinessLookupService, private val businessLookupApiToDomainMapper: BusinessLookupApiToDomainMapper, private val responseToString: Mapper, private val schedulerProvider: SchedulerProvider ) { fun businessLookupEligibility(countryCode: String): Single<Result<BusinessLookupEligibility, ErrorMessage>> { return businessLookupService.businessLookupEligibility(countryCode) .map { response -> return@map when (response) { is NetworkResponse.Success -> { val businessLookupEligibility = businessLookupApiToDomainMapper.map(response.body) Result.Success<BusinessLookupEligibility, ErrorMessage>(businessLookupEligibility) } is NetworkResponse.Error -> Result.Failure<BusinessLookupEligibility, ErrorMessage>( responseToString.transform(response) ) } }.subscribeOn(schedulerProvider.io()) } } 

रिफैक्टरिंग चरण:

  1. businessLookupEligibility एक निलंबित कार्य बन जाता है।
  2. सिंगल रैपर को वापसी प्रकार से निकालें।
  3. रिपॉजिटरी में विधियां आमतौर पर नेटवर्क कॉल या डीबी क्वेरी जैसे लंबे समय तक चलने वाले कार्य कर रही हैं। यह निर्दिष्ट करने के लिए कि यह काम किस थ्रेड पर किया जाना चाहिए, रिपॉजिटरी की जिम्मेदारी है। SubscribeOn (शेड्यूलरप्रोवाइडर.आईओ ()) द्वारा हम RxJava को बता रहे हैं कि io थ्रेड पर काम किया जाना चाहिए। उसी को कोरटाइन के साथ कैसे प्राप्त किया जा सकता है? हम ब्लॉक के निष्पादन को अलग थ्रेड में स्थानांतरित करने के लिए एक विशिष्ट डिस्पैचर के साथ कॉनटेक्स्ट का उपयोग करने जा रहे हैं और जब निष्पादन पूरा हो जाता है तो मूल डिस्पैचर में वापस। यह सुनिश्चित करने के लिए एक अच्छा अभ्यास है कि withContext का उपयोग करके एक फ़ंक्शन मुख्य-सुरक्षित है। BusinessLookupRepository के उपभोक्ताओं को यह नहीं सोचना चाहिए कि BusinessLookupEligibility विधि को निष्पादित करने के लिए उन्हें किस धागे का उपयोग करना चाहिए, इसे मुख्य धागे से कॉल करना सुरक्षित होना चाहिए।
  4. हमें मानचित्र ऑपरेटर की अब आवश्यकता नहीं है क्योंकि हम एक निलंबित कार्य के एक शरीर में businessLookupService.businessLookupEligibility के परिणाम का उपयोग कर सकते हैं।

 class BusinessLookupRepository @Inject constructor( private val businessLookupService: BusinessLookupService, private val businessLookupApiToDomainMapper: BusinessLookupApiToDomainMapper, private val responseToString: Mapper, private val dispatcherProvider: DispatcherProvider ) { suspend fun businessLookupEligibility(countryCode: String): Result<BusinessLookupEligibility, ErrorMessage> = withContext(dispatcherProvider.io) { when (val response = businessLookupService.businessLookupEligibility(countryCode)) { is NetworkResponse.Success -> { val businessLookupEligibility = businessLookupApiToDomainMapper.map(response.body) Result.Success<BusinessLookupEligibility, ErrorMessage>(businessLookupEligibility) } is NetworkResponse.Error -> Result.Failure<BusinessLookupEligibility, ErrorMessage>( responseToString.transform(response) ) } } } 

Interactor


इस विशिष्ट उदाहरण में BusinessLookupEligibilityInteractor में कोई अतिरिक्त तर्क नहीं है और BusinessLookupRepository के प्रॉक्सी के रूप में कार्य करता है। हम इनवोक ऑपरेटर को ओवरलोडिंग के लिए उपयोग करते हैं, ताकि इंटरेक्टर को एक फ़ंक्शन के रूप में लागू किया जा सके।

 class BusinessLookupEligibilityInteractor @Inject constructor( private val businessLookupRepository: BusinessLookupRepository ) { operator fun invoke(countryCode: String): Single<Result<BusinessLookupEligibility, ErrorMessage>> = businessLookupRepository.businessLookupEligibility(countryCode) } 

रिफैक्टरिंग चरण:

  1. ऑपरेटर मज़ा चालान निलंबित ऑपरेटर मज़ा चालान बन जाता है।
  2. सिंगल रैपर को वापसी प्रकार से निकालें।

 class BusinessLookupEligibilityInteractor @Inject constructor( private val businessLookupRepository: BusinessLookupRepository ) { suspend operator fun invoke(countryCode: String): Result<BusinessLookupEligibility, ErrorMessage> = businessLookupRepository.businessLookupEligibility(countryCode) } 

ViewModel


BusinessProfileViewModel में हम BusinessLookupEligibilityInteractor कहते हैं जो सिंगल देता है। हम स्ट्रीम को सब्सक्राइब करते हैं और UI शेड्यूल को निर्दिष्ट करके UI थ्रेड पर इसका निरीक्षण करते हैं। सफलता के मामले में, हम एक डोमेन मॉडल से एक बिजनेस व्यूस्टेट लाइवडेट पर मान प्रदान करते हैं। विफलता के मामले में हम एक त्रुटि संदेश देते हैं।

हम प्रत्येक सदस्यता को एक कंपोजिटडिजाइन के साथ जोड़ते हैं और उन्हें एक ViewModel के जीवनचक्र की ऑनक्लेयर () विधि में डिस्पोज करते हैं।

 class BusinessProfileViewModel @Inject constructor( private val businessLookupEligibilityInteractor: BusinessLookupEligibilityInteractor, private val schedulerProvider: SchedulerProvider ) : ViewModel() { private val disposables = CompositeDisposable() internal val businessViewState: MutableLiveData<ViewState> = LiveDataFactory.createDefault("Loading...") fun onCountrySubmit(country: Country) { disposables.add(businessLookupEligibilityInteractor(country.countryCode) .observeOn(schedulerProvider.ui()) .subscribe { state -> return@subscribe when (state) { is Result.Success -> businessViewState.value = state.entity.provider is Result.Failure -> businessViewState.value = state.failure } }) } @Override protected void onCleared() { super.onCleared(); disposables.clear(); } } 

रिफैक्टरिंग चरण:

  1. लेख की शुरुआत में मैंने कोरआउट के मुख्य लाभों में से एक का उल्लेख किया है - संरचित संगामिति। और यह वह जगह है जहाँ यह खेल में आता है। हर कोरटाइन में एक स्कोप होता है। कार्यक्षेत्र का नियंत्रण अपनी नौकरी के माध्यम से होता है। यदि कोई नौकरी रद्द हो जाती है, तो संबंधित दायरे में आने वाले सभी कोरआउट को भी रद्द कर दिया जाएगा। आप अपने स्वयं के स्कोप बनाने के लिए स्वतंत्र हैं, लेकिन इस मामले में हम theViewModel के जीवन-चक्र-जागरूक दृश्यModelScope का लाभ उठा रहे हैं। हम viewModelScope.launch का उपयोग करके एक viewModelScope में एक नया coroutine शुरू करेंगे। Coroutine को मुख्य थ्रेड में लॉन्च किया जाएगा क्योंकि viewModelScope में एक डिफ़ॉल्ट डिस्पैचर है - Dispatchers.Main। डिस्पैचर्स पर एक कॉरआउट शुरू हुआ। निलंबित करते समय मुख्य थ्रेड को ब्लॉक नहीं किया जाएगा। जैसा कि हमने अभी एक coroutine लॉन्च किया है, हम businessLookupEligibilityInteractor को निलंबित करने वाले ऑपरेटर को आमंत्रित कर सकते हैं और परिणाम प्राप्त कर सकते हैं। BusinessLookupEligibilityInteractor BusinessLookupRepository.businessLookupEligibility डिस्पैचरर्स.आईओ को वापस भेजती है और डिस्पैचर्स को वापस भेज देती है। मेन। जैसा कि हम UI थ्रेड में हैं, हम एक मान बताकर businessViewState LiveData को अपडेट कर सकते हैं।
  2. हम डिस्पोज़ेबल्स से छुटकारा पा सकते हैं क्योंकि viewModelScope एक ViewModel जीवनचक्र के लिए बाध्य है। इस दायरे में लॉन्च किया गया कोई भी कॉउंटइन स्वचालित रूप से रद्द हो जाता है अगर ViewModel क्लियर हो जाता है।

 class BusinessProfileViewModel @Inject constructor( private val businessLookupEligibilityInteractor: BusinessLookupEligibilityInteractor ) : ViewModel() { internal val businessViewState: MutableLiveData<ViewState> = LiveDataFactory.createDefault("Loading...") fun onCountrySubmit(country: Country) { viewModelScope.launch { when (val state = businessLookupEligibilityInteractor(country.countryCode)) { is Result.Success -> businessViewState.value = state.entity.provider is Result.Failure -> businessViewState.value = state.failure } } } } 

मुख्य टेकअवे


कोरटाइन के साथ लिखे गए कोड को पढ़ना और समझना काफी आसान है, फिर भी यह एक ऐसा बदलाव है जिसे सीखने के लिए कुछ प्रयास करने की आवश्यकता होती है।

इस लेख में मैंने परीक्षण को कवर नहीं किया। मैंने मॉकिट लाइब्रेरी का उपयोग किया क्योंकि मेरे पास मॉकिटो का उपयोग करके कोराउटीन के परीक्षण के मुद्दे थे।

आरएक्स जावा के साथ मैंने जो कुछ भी लिखा है मुझे कोरटाइन, फ्लो और चैनल के साथ लागू करना काफी आसान लगा। कोराउटीन के लाभों में से एक यह है कि वे एक कोटलिन भाषा की विशेषता हैं और भाषा के साथ मिलकर विकसित हो रहे हैं।

Source: https://habr.com/ru/post/hi483832/


All Articles