2017-10-13 13 views
10

मैंने हाल ही में Google द्वारा जारी किए गए नए एंड्रॉइड आर्किटेक्चर घटकों पर नज़र डालने का फैसला किया है, विशेष रूप से उनके व्यूमोडेल लाइफसाइक्ल-जागरूक वर्ग का उपयोग एमवीवीएम आर्किटेक्चर और लाइवडाटा में कर रहा है।एमवीवीएम पैटर्न और स्टार्ट एक्टिविटी

जब तक मैं एक ही गतिविधि, या एक टुकड़ा से निपट रहा हूं, सबकुछ ठीक है।

हालांकि, मुझे गतिविधि स्विचिंग को संभालने के लिए एक अच्छा समाधान नहीं मिल रहा है। कहें, एक संक्षिप्त उदाहरण के लिए, गतिविधि ए को गतिविधि बी

लॉन्च करने के लिए एक बटन है प्रारंभिकता() को कहां संभाला जाएगा?

एमवीवीएम पैटर्न के बाद, क्लिक लिस्टनर का तर्क व्यूमोडेल में होना चाहिए। हालांकि, हम वहां गतिविधि के संदर्भ होने से बचना चाहते हैं। तो ViewModel को संदर्भ पास करना एक विकल्प नहीं है।

मैंने "ओके" लगने वाले कुछ विकल्पों को संकुचित कर दिया, लेकिन "यहां यह कैसे करना है" का कोई उचित उत्तर नहीं मिला।

विकल्प 1: संभावित रूटिंग (ACTIVITY_B, ACTIVITY_C) पर मूल्य मैपिंग के साथ व्यूमोडेल में एक enum है। इसे लाइवडाटा के साथ जोड़ो। गतिविधि इस लाइवडेटा का निरीक्षण करेगी, और जब व्यूमोडेल निर्णय लेता है कि ACTIVITY_C लॉन्च किया जाना चाहिए, तो यह केवल Value (ACTIVITY_C) पोस्ट करेगा। गतिविधि सामान्य रूप से startActivity() को कॉल कर सकती है।

विकल्प 2: नियमित इंटरफ़ेस पैटर्न। विकल्प 1 के समान सिद्धांत, लेकिन गतिविधि इंटरफ़ेस को कार्यान्वित करेगी। हालांकि मैं इसके साथ थोड़ा और अधिक जोड़ रहा हूँ।

विकल्प 3: संदेश विकल्प, जैसे ओटो या इसी तरह के। ViewModel एक प्रसारण भेजता है, गतिविधि इसे उठाती है और इसे लॉन्च करता है। इस समाधान के साथ केवल समस्या यह है कि, डिफ़ॉल्ट रूप से, आपको ViewModel के अंदर उस प्रसारण का पंजीकरण/पंजीकरण रद्द करना चाहिए। तो मदद नहीं करता है।

विकल्प 4: कहीं भी एक सिंगलटन या समान के रूप में, एक बड़ी रूटिंग कक्षा होने के कारण, किसी भी गतिविधि के लिए प्रासंगिक रूटिंग प्रेषित करने के लिए कहा जा सकता है। अंततः इंटरफ़ेस के माध्यम से? इसलिए हर गतिविधि (या एक BaseActivity) लागू करेगा

IRouting { void requestLaunchActivity(ACTIVITY_B); } 

इस विधि बस मुझे थोड़ा चिंता अपने ऐप के टुकड़े/गतिविधियों का एक बहुत (क्योंकि रूटिंग वर्ग humongous बन जाएगा)

होने ताकि है शुरू होता है यह। यह मेरा सवाल है। आप लोग इसे कैसे संभालेंगे? क्या आप एक ऐसे विकल्प के साथ जाते हैं जिस पर मैंने नहीं सोचा था? आप सबसे प्रासंगिक क्या विकल्प चुनते हैं और क्यों? अनुशंसित Google दृष्टिकोण क्या है?

पुनश्च: लिंक है कि मुझे नहीं मिला कहीं भी 1 - Android ViewModel call Activity methods 2 - How to start an activity from a plain non-activity java class?

+1

धन्यवाद। आपकी मदद करने में खुशी हुई :-) –

उत्तर

5

NSimon, अपने महान है कि आप एएसी का उपयोग शुरू।

मैंने इससे पहले एएक्स-गीथब में issue लिखा था।

ऐसा करने के कई तरीके हैं।

एक समाधान एक navigationController जो गतिविधि के संदर्भ रखती है करने के लिए एक

WeakReference का उपयोग कर किया जाएगा। यह व्यूमोडेल के अंदर संदर्भ-बाध्य सामग्री को संभालने के लिए एक आम प्रयोग किया गया पैटर्न है।

मैं कई कारणों से इसे बहुत कम करता हूं। पहला: आमतौर पर इसका मतलब है कि आपको अपने नेविगेशन नियंत्रक का संदर्भ रखना है जो संदर्भ रिसाव को हल करता है, लेकिन आर्किटेक्चर को बिल्कुल हल नहीं करता है।

सबसे अच्छा तरीका (मेरे oppinion में) LiveData का उपयोग कर रहा है जो जीवन चक्र जागरूक है और सभी वांछित सामान कर सकता है।

उदाहरण:

class YourVm : ViewModel() { 

    val uiEventLiveData = SingleLiveData<Pair<YourModel, Int>>() 
    fun onClick(item: YourModel) { 
     uiEventLiveData.value = item to 3 // can be predefined values 
    } 
} 

उसके बाद आप परिवर्तन के लिए अपने दृष्टिकोण को अंदर सुन सकते हैं। बाकी क्योंकि यह हमेशा नई पर्यवेक्षकों जो बुरा व्यवहार की ओर जाता है के लिए नवीनतम परिणाम उत्सर्जित करेगा

class YourFragmentOrActivity { 
    //assign your vm whatever 
    override fun onActivityCreated(savedInstanceState: Bundle?) { 
     var context = this 
     yourVm.uiEventLiveData.observe(this, Observer { 
      when (it?.second) { 
       1 -> { context.startActivity(...) } 
       2 -> { .. } 
      } 

     }) 
    } 
} 

, देखभाल कि ive इस्तेमाल किया एक संशोधित MutableLiveData लो। उदाहरण के लिए यदि आप गतिविधि बदलते हैं और वापस जाते हैं तो यह एक लूप में समाप्त हो जाएगा।

class SingleLiveData<T> : MutableLiveData<T>() { 

    private val mPending = AtomicBoolean(false) 

    @MainThread 
    override fun observe(owner: LifecycleOwner, observer: Observer<T>) { 

     if (hasActiveObservers()) { 
      Log.w(TAG, "Multiple observers registered but only one will be notified of changes.") 
     } 

     // Observe the internal MutableLiveData 
     super.observe(owner, Observer { t -> 
      if (mPending.compareAndSet(true, false)) { 
       observer.onChanged(t) 
      } 
     }) 
    } 

    @MainThread 
    override fun setValue(t: T?) { 
     mPending.set(true) 
     super.setValue(t) 
    } 

    /** 
    * Used for cases where T is Void, to make calls cleaner. 
    */ 
    @MainThread 
    fun call() { 
     value = null 
    } 

    companion object { 
     private val TAG = "SingleLiveData" 
    } 
} 

क्यों कि प्रयास बेहतर WeakReferences, इंटरफेस, या किसी अन्य समाधान का उपयोग तो नहीं है?

क्योंकि यह घटना व्यावसायिक तर्क के साथ यूआई तर्क विभाजित करती है। कई पर्यवेक्षकों के लिए भी संभव है। यह जीवन चक्र के बारे में परवाह करता है। यह कुछ भी रिसाव नहीं करता है।

आप PublishSubject का उपयोग कर लाइवडेटा के बजाय RxJava का उपयोग करके इसे हल भी कर सकते हैं। (addToRxKotlin की आवश्यकता है)

इसे ऑनस्टॉप() में जारी करके सदस्यता लीक करने के बारे में परवाह करें।

class YourVm : ViewModel() { 
    var subject : PublishSubject<YourItem> = PublishSubject.create(); 
} 

class YourFragmentOrActivityOrWhatever { 
    var composite = CompositeDisposable() 
    onStart() { 
     YourVm.subject 
      .subscribe({ Log.d("...", "Event emitted $it") }, { error("Error occured $it") }) 
       .addTo(compositeDisposable)   
     } 
     onStop() { 
     compositeDisposable.clear() 
     } 
    } 

यह भी ध्यान रखें कि व्यूमोडेल एक गतिविधि या एक टुकड़ा से जुड़ा हुआ है। आप कई क्रियाकलापों के बीच व्यूमोडेल साझा नहीं कर सकते क्योंकि यह "लाइवसाइकिल-जागरूकता" को तोड़ देगा।

यदि आपको room जैसे डेटाबेस का उपयोग करके अपने डेटा को जारी रखने की आवश्यकता है या पार्सल का उपयोग कर डेटा साझा करें।

+0

बहुत विस्तृत उत्तर के लिए धन्यवाद। मैं लाइवडाटा दृष्टिकोण की तरफ झुका रहा था, हालांकि आपके लाइवडाटा ट्वीक्स के बारे में नहीं सोचा था। कुल मिलाकर, यह सब बहुत "हैकी" लगता है, और ऐसा करने में लगभग बुरा लगता है। कोई बात नहीं धन्यवाद ! (संपादित करें: केवल 20h में बकाया को मान्य कर सकते हैं) – NSimon

+1

नहीं, यह हैकी नहीं है। यह बात करता है कि पर्यवेक्षक पैटर्न कैसे काम करता है। आपको एक क्लिक मिलता है, इसे अपने व्यूमोडेल पर दबाएं और यदि कोई दृश्य है (यह गारंटी है) तो यह डेटा को संसाधित करता है। यह भी एक पैच नहीं है :) यह कैसे काम करता है। बस एक नमूना क्लिक करें, यह हर "डेटा इनपुट" हो सकता है। –

संबंधित मुद्दे