Bir ViewModel'in LiveData'sını arka plan hizmetinden güncelleme ve UI'yi Güncelleme


97

Son zamanlarda google tarafından yeni tanıtılan Android Mimarisini keşfediyorum. Gönderen Belgeler ben bu bulduk:

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

etkinlik bu listeye aşağıdaki şekilde erişebilir:

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

Sorum şu, şunu yapacağım:

  1. içinde loadUsers()işlevin Ben öncelikle bu veriler için veritabanı (Oda) kontrol edeceği uyumsuz verilerin alınmaya am

  2. Verileri orada alamazsam, verileri web sunucusundan almak için bir API çağrısı yapacağım.

  3. Getirilen verileri veritabanına (Oda) ekleyeceğim ve kullanıcı arayüzünü verilere göre güncelleyeceğim.

Bunu yapmak için önerilen yaklaşım nedir?

Yöntemden ServiceAPI'yi çağırmaya başlarsam loadUsers(), MutableLiveData<List<User>> usersdeğişkeni bundan nasıl güncelleyebilirim Service?


9
Her şeyden önce, bir Depo eksiksiniz. ViewModel'iniz herhangi bir veri yükleme görevi yapmamalıdır. Bunun dışında, Oda'yı kullandığınız için, Hizmetinizin Canlı Verileri doğrudan ViewModel'de güncellemesi gerekmez. Hizmet yalnızca Oda'ya veri ekleyebilirken, ViewModel Verileriniz yalnızca Odaya eklenmeli ve Odadan güncellemeler almalıdır (Hizmet verileri ekledikten sonra). Ancak mutlak en iyi mimari için, bu sayfanın altından NetworkBoundResource sınıf uygulamasına bakın: developer.android.com/topic/libraries/architecture/guide.html
Marko Gajić

öneri için teşekkürler :)
CodeCameo

1
Repositor sınıfından ROOM veya android mimari bileşenlerini açıklayan sosyal dokümantasyonda bahsedilmiyor
Jonathan

2
Depo, kod ayırma ve mimari için önerilen bir en iyi uygulamadır; şu örneğe bakın: codelabs.developers.google.com/codelabs/…
glisu

1
İşlev loadUsers()temelde kullanıcı bilgilerini almak için
depoyu arayacak

Yanıtlar:


97

Android mimari bileşenlerini kullandığınızı varsayıyorum . Aslında service, asynctask or handler, verileri güncellemek için nerede ararsanız arayın önemli değil . Yöntemi kullanarak hizmetten veya eşzamansız görevden verileri ekleyebilirsiniz postValue(..). Sınıfınız şöyle görünecektir:

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

As usersise LiveData, Roomveritabanı takılı olduğundan her yerde kullanıcıların verilerini sağlamaktan sorumludur.

Note: MVVM benzeri mimaride, depo çoğunlukla yerel verileri ve uzak verileri kontrol etmekten ve çekmekten sorumludur.


3
Yukarıdaki gibi db yöntemlerimi çağırırken "java.lang.IllegalStateException: Ana iş parçacığındaki veritabanına erişilemiyor" mesajı alıyorum, neyin yanlış olabileceğini söyleyebilir misiniz?
Prashant

Kullanıcı arayüzündeyken veritabanını arka planda güncelleyen evernote-job kullanıyorum. Ancak LiveDatagüncellenmiyor
Akshay Chordiya

users.postValue(mUsers);-> Ama MutableLiveData'nın postValue yöntemi LiveData'yı kabul edebilir mi ???
Cheok Yan Cheng

2
Benim hatam valueyerine kullanmaktı postValue. Cevabınız için teşekkürler.
Hesam

1
@pcj ya oda işlemini bir iş parçacığında yapmanız ya da ana iş parçacığında işlemleri etkinleştirmeniz gerekir - daha fazla yanıt için google.
kilokahn

46

MutableLiveData<T>.postValue(T value)Arka plan iş parçacığından yöntemi kullanabilirsiniz .

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

19
PostValue (), LiveData'da korunuyor ancak MutableLiveData'da herkese açık
Long Ranger

Bu, arka plandaki bir görevde ve .setValue()izin verilmeyen durumlarda bile çalışır
davejoem

18

... loadUsers () işlevinde verileri eşzamansız olarak alıyorum ... API'yi loadUsers () yönteminden çağırmak için bir Hizmeti başlatırsam, MutableLiveData> users değişkenini bu Hizmetten nasıl güncelleyebilirim?

Uygulama bir arka plan ileti dizisindeki kullanıcı verilerini alıyorsa , postValue ( setValue yerine ) yararlı olacaktır.

LoadData yönteminde MutableLiveData "users" nesnesine bir referans vardır. LoadData yöntemi ayrıca bir yerden (örneğin bir havuzdan) bazı yeni kullanıcı verilerini alır.

Şimdi, yürütme bir arka plan iş parçacığı üzerindeyse, MutableLiveData.postValue (), MutableLiveData nesnesinin gözlemcileri dışında güncellenir.

Belki bunun gibi bir şey:

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

}

Deponun getUsers()zaman uyumsuz olduğu veriler için bir api diyebileceğimiz yöntem 's return ifadesinden Listelerinden dönebilirsiniz nasıl bu durumda (bunun için bir hizmet veya asynctask başlayabilir)?
CodeCameo

Belki LiveData nesnesini bir argüman olarak alabilir. (Repository.getUsers (kullanıcılar) gibi bir şey). Daha sonra depo yöntemi users.postValue'nun kendisini çağıracaktır. Ve bu durumda, loadUsers yönteminin bir arka plan iş parçacığına bile ihtiyacı olmayacaktır.
albert c braun

1
Cevap için teşekkürler .... ancak, depolama için bir Oda DB kullanıyorum ve DAO bir LiveData <Nesne> döndürüyor ... LiveData'yı MutableLiveData <Object> 'e nasıl dönüştürebilirim?
kilokahn

Room'un DAO'sunun gerçekten MutableLiveData nesneleriyle uğraşmak için tasarlandığını sanmıyorum. DAO, temeldeki veri tabanındaki bir değişikliği size bildiriyor, ancak, DB'deki değeri değiştirmek isterseniz, DAO'nun yöntemlerini çağırırsınız. Ayrıca buradaki tartışma yararlı olabilir: stackoverflow.com/questions/50943919/…
albert c braun

3

Ve gibi yeni mimari modüllere eşlik eden Android mimari kılavuzuna bir göz atın . Bu konuyu derinlemesine tartışıyorlar.LiveDataViewModel

Örneklerinde bunu bir hizmete koymazlar. Bir "depo" modülü ve Retrofit kullanarak bunu nasıl çözdüklerine bir göz atın. Alttaki ekler, iletişim ağı durumu, raporlama hataları vb. Dahil olmak üzere daha eksiksiz örnekler içerir.


2

API'nizi Depoda arıyorsanız, o zaman,

In depoyu :

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

In ViewModel

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

Etkinlik / Parça İçi

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

Not: JAVA_1.8 lambda'yı burada kullanarak, onsuz kullanabilirsiniz

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.