पृष्ठभूमि सेवा और अद्यतन UI से एक ViewModel के LiveData को कैसे अपडेट करें


95

हाल ही में मैं एंड्रॉइड आर्किटेक्चर की खोज कर रहा हूं, जिसे हाल ही में Google द्वारा पेश किया गया है। से प्रलेखन मैं इस पाया है:

public class MyViewModel extends ViewModel {
    private MutableLiveData<List<User>> users;
    public LiveData<List<User>> getUsers() {
        if (users == null) {
            users = new MutableLiveData<List<Users>>();
            loadUsers();
        }
        return users;
    }

    private void loadUsers() {
        // do async operation to fetch users
    }
}

गतिविधि इस सूची तक पहुँच सकती है:

public class MyActivity extends AppCompatActivity {
    public void onCreate(Bundle savedInstanceState) {
        MyViewModel model = ViewModelProviders.of(this).get(MyViewModel.class);
        model.getUsers().observe(this, users -> {
            // update UI
        });
    }
}

मेरा सवाल है, मैं यह करने जा रहा हूं:

  1. में loadUsers()समारोह मैं कहाँ मैं पहली बार है कि डेटा के लिए डेटाबेस (कक्ष) की जाँच करेगा एसिंक्रोनस रूप से डेटा प्राप्त करने में कर रहा हूँ

  2. अगर मुझे वहां डेटा नहीं मिलता है तो मैं वेब सर्वर से डेटा लाने के लिए एक एपीआई कॉल करूंगा।

  3. मैं प्राप्त किए गए डेटा को डेटाबेस (कक्ष) में सम्मिलित करूंगा और डेटा के अनुसार UI अपडेट करूंगा।

ऐसा करने के लिए अनुशंसित दृष्टिकोण क्या है?

यदि मैं विधि Serviceसे एपीआई को कॉल करने के लिए शुरू करता हूं, तो मैं उस से चर को loadUsers()कैसे अपडेट कर सकता हूं ?MutableLiveData<List<User>> usersService


8
सबसे पहले, आप एक रिपॉजिटरी याद कर रहे हैं। आपका ViewModel कोई डेटा लोडिंग कार्य नहीं करना चाहिए। इसके अलावा, आपके कमरे का उपयोग करने के बाद से, आपकी सेवा को सीधे ViewModel में LiveData को अपडेट करने की आवश्यकता नहीं है। सेवा केवल कक्ष में डेटा सम्मिलित कर सकती है, जबकि आपका ViewModelData केवल कक्ष से जुड़ा होना चाहिए, और कक्ष से अपडेट प्राप्त करना चाहिए (सेवा आवेषण डेटा के बाद)। लेकिन पूर्ण सर्वश्रेष्ठ वास्तुकला के लिए, इस पृष्ठ के निचले भाग से NetworkBoundResource वर्ग कार्यान्वयन देखें: developer.android.com/topic/lbooks/altecture/guide.html
मार्को गाज़िक

सुझाव के लिए धन्यवाद :)
कोडकेमियो

1
आरओओएम या एंड्रॉइड आर्किटेक्चर घटकों का वर्णन करने वाले अपमानजनक डॉक्स में रिपॉजिटर वर्ग का उल्लेख नहीं किया गया है
जोनाथन

2
रिपोजिटरी कोड अलगाव और वास्तुकला के लिए एक सुझाया गया सबसे अच्छा अभ्यास है, इस उदाहरण को देखें: codelabs.developers.google.com/codelabs/…
glisu

1
फ़ंक्शन loadUsers()मूल रूप से उपयोगकर्ता जानकारी प्राप्त करने के लिए रेपो को
बुलाएगा

जवाबों:


96

मैं मान रहा हूं कि आप एंड्रॉइड आर्किटेक्चर घटकों का उपयोग कर रहे हैं । वास्तव में यह कोई फर्क नहीं पड़ता कि आप service, asynctask or handlerडेटा को अपडेट करने के लिए कहां बुला रहे हैं । आप डेटा को सेवा से या asynctask से postValue(..)विधि का उपयोग करके सम्मिलित कर सकते हैं । आपकी कक्षा इस तरह दिखेगी:

private void loadUsers() {
    // do async operation to fetch users and use postValue() method
    users.postValue(listOfData)
}

के रूप में usersहै LiveData, Roomडेटाबेस जहाँ भी यह डाला जाता है उन डेटा उपलब्ध कराने के लिए जिम्मेदार है।

Note: आर्किटेक्चर की तरह MVVM में, रिपॉजिटरी ज्यादातर स्थानीय डेटा और रिमोट डेटा की जाँच और खींचने के लिए जिम्मेदार है।


3
मुझे "java.lang.IllegalStateException मिल रही है: ऊपर की तरह मेरे db तरीकों को कॉल करने पर" इसके बाद से मुख्य थ्रेड पर डेटाबेस तक नहीं पहुंच सकता, क्या आप बता सकते हैं कि क्या गलत हो सकता है?
pcj

मैं एवरनोट-जॉब का उपयोग कर रहा हूं जो कि यूआई में होने के दौरान पृष्ठभूमि में डेटाबेस को अपडेट कर रहा है। लेकिन LiveDataअपडेट नहीं कर रहा है
अक्षय चोरिया

users.postValue(mUsers);-> लेकिन, क्या MutableLiveData की पोस्टवैल्यू विधि LiveData को स्वीकार करने में सक्षम है ???
चोक यान चेंग

2
मेरी गलती के valueबजाय उपयोग कर रहा था postValue। आपके उत्तर के लिए धन्यवाद।
हेसाम

1
@ pcj आपको या तो कमरे के संचालन को थ्रेड में करने की आवश्यकता है या अधिक उत्तर के लिए मुख्य थ्रेड - google पर संचालन को सक्षम करने की आवश्यकता है।
किलोकाहन

46

आप MutableLiveData<T>.postValue(T value)पृष्ठभूमि थ्रेड से विधि का उपयोग कर सकते हैं ।

private void loadUsers() {
    // do async operation to fetch users and use postValue() method
   users.postValue(listOfData)
}

19
याद दिलाने के लिए, PostValue () को LiveData में संरक्षित किया गया है लेकिन MutableLiveData में सार्वजनिक
लॉन्ग रेंजर

यह कार्य एक पृष्ठभूमि कार्य से भी है और ऐसी स्थितियों में जहां .setValue()अनुमति नहीं होगी
davejoem

17

... लोडयूजर () फ़ंक्शन में मैं डेटा को अतुल्य रूप से प्राप्त कर रहा हूं ... अगर मैं लोडयूजर () विधि से एपीआई को कॉल करने के लिए एक सेवा शुरू करता हूं, तो मैं उस सेवा से MutableLiveData> उपयोगकर्ता चर कैसे अपडेट कर सकता हूं?

यदि ऐप उपयोगकर्ता डेटा को बैकग्राउंड थ्रेड, पोस्टवैल्यू ( सेटवैल्यू के बजाय) पर ला रहा है ) उपयोगी होगा।

लोडडेटा विधि में MutableLiveData "उपयोगकर्ताओं" ऑब्जेक्ट का संदर्भ है। लोडडेटा विधि भी कहीं से कुछ ताजा उपयोगकर्ता डेटा लाती है (उदाहरण के लिए, एक भंडार)।

अब, यदि निष्पादन एक पृष्ठभूमि थ्रेड पर है, MutableLiveData.postValue () MutableLiveData ऑब्जेक्ट के पर्यवेक्षकों के बाहर अद्यतन करता है।

शायद कुछ इस तरह:

private MutableLiveData<List<User>> users;

.
.
.

private void loadUsers() {
    // do async operation to fetch users
    ExecutorService service =  Executors.newSingleThreadExecutor();
    service.submit(new Runnable() {
        @Override
        public void run() {
            // on background thread, obtain a fresh list of users
            List<String> freshUserList = aRepositorySomewhere.getUsers();

            // now that you have the fresh user data in freshUserList, 
            // make it available to outside observers of the "users" 
            // MutableLiveData object
            users.postValue(freshUserList);        
        }
    });

}

रिपॉजिटरी की getUsers()विधि उस डेटा के लिए एक एपि कह सकती है जो कि एसक्यूएन है (इसके लिए कोई सेवा या एसिंक्टस्क शुरू कर सकते हैं) उस स्थिति में यह विवरण वापसी से सूची कैसे लौटा सकता है?
कोडकेमो

शायद यह LiveData ऑब्जेक्ट को एक तर्क के रूप में ले सकता है। (रिपॉजिटरी.गेटयूजर्स (यूजर्स) जैसा कुछ)। इसके बाद रिपॉजिटरी मेथड में यूजर्स को कॉल किया जाएगा। और, उस स्थिति में, लोडयूजर विधि को पृष्ठभूमि थ्रेड की भी आवश्यकता नहीं होगी।
अल्बर्ट सी ब्रौन

1
उत्तर के लिए धन्यवाद .... हालांकि, मैं भंडारण के लिए एक कमरे के DB का उपयोग कर रहा हूं और DAO एक LiveData <Object> लौटाता है ... मैं LiveData को MutableLiveData <ऑब्जेक्ट> में कैसे परिवर्तित करूं?
किलोकाहन

मुझे नहीं लगता कि रूम का DAO वास्तव में MutableLiveData ऑब्जेक्ट्स से निपटने का इरादा है। DAO आपको अंतर्निहित db में बदलाव के बारे में सूचित कर रहा है, लेकिन, यदि आप DB में मान बदलना चाहते हैं, तो आप DAO के तरीकों को कॉल करेंगे। इसके अलावा शायद यहाँ चर्चा उपयोगी है: stackoverflow.com/questions/50943919/…
अल्बर्ट सी ब्रौन

3

एंड्रॉइड आर्किटेक्चर गाइड पर एक नज़र डालें जो नए आर्किटेक्चर मॉड्यूल को पसंद करता है जैसे LiveDataऔर ViewModel। वे इस सटीक मुद्दे पर गहराई से चर्चा करते हैं।

अपने उदाहरणों में वे इसे एक सेवा में नहीं रखते हैं। एक नज़र डालें कि वे इसे "रिपॉजिटरी" मॉड्यूल और रेट्रोफिट का उपयोग करके कैसे हल करते हैं। नीचे दिए गए परिशिष्टों में अधिक पूर्ण उदाहरण शामिल हैं जिसमें संचार स्थिति, रिपोर्टिंग त्रुटियां, आदि शामिल हैं।


2

यदि आप रिपॉजिटरी में अपने एपी को बुला रहे हैं तो,

में भंडार :

public MutableLiveData<LoginResponseModel> checkLogin(LoginRequestModel loginRequestModel) {
    final MutableLiveData<LoginResponseModel> data = new MutableLiveData<>();
    apiService.checkLogin(loginRequestModel)
            .enqueue(new Callback<LoginResponseModel>() {
                @Override
                public void onResponse(@NonNull Call<LoginResponseModel> call, @Nullable Response<LoginResponseModel> response) {
                    if (response != null && response.isSuccessful()) {
                        data.postValue(response.body());
                        Log.i("Response ", response.body().getMessage());
                    }
                }

                @Override
                public void onFailure(@NonNull Call<LoginResponseModel> call, Throwable t) {
                    data.postValue(null);
                }
            });
    return data;
}

में ViewModel

public LiveData<LoginResponseModel> getUser() {
    loginResponseModelMutableLiveData = repository.checkLogin(loginRequestModel);
    return loginResponseModelMutableLiveData;
}

में गतिविधि / टुकड़ा

loginViewModel.getUser().observe(LoginActivity.this, loginResponseModel -> {
        if (loginResponseModel != null) {
            Toast.makeText(LoginActivity.this, loginResponseModel.getUser().getType(), Toast.LENGTH_SHORT).show();
        }
    });

नोट: यहां JAVA_1.8 लंबो का उपयोग करके, आप इसके बिना उपयोग कर सकते हैं

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.