Как обновить LiveData из ViewModel из фоновой службы и обновить интерфейс

Недавно я изучаю архитектуру Android, которая недавно была представлена 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() я асинхронно loadUsers() данные, где сначала проверяю базу данных (Room) на эти данные

  2. Если я не получу данные там, я сделаю вызов API для получения данных с веб-сервера.

  3. Я вставлю извлеченные данные в базу данных (Комната) и обновлю интерфейс в соответствии с данными.

Каков рекомендуемый подход для этого?

Если я запускаю Service для вызова API из loadUsers(), как я могу обновить переменную MutableLiveData<List<User>> users из этой Service?

Ответы

Ответ 1

Я предполагаю, что вы используете компоненты архитектуры Android. На самом деле это не имеет значения, где бы вы ни service, asynctask or handler для обновления данных. Вы можете вставить данные из сервиса или из асинхронной задачи, используя postValue(..). Ваш класс будет выглядеть так:

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

Поскольку users являются LiveData, база данных Room отвечает за предоставление данных пользователям, где бы они ни были вставлены.

Note: В архитектуре, аналогичной MVVM, хранилище в основном отвечает за проверку и извлечение локальных и удаленных данных.

Ответ 2

Пожалуйста, используйте текущие данные метод postValue (..) из фонового потока.

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

Ответ 3

... в функции loadUsers() Я получаю данные асинхронно... Если я запускаю службу для вызова API из метода loadUsers(), как Могу ли я обновить переменную MutableLiveData > users Сервис

Если приложение извлекает данные пользователя в фоновом потоке, postValue (а не setValue).

В методе loadData имеется ссылка на объект "пользователи" MutableLiveData. Метод loadData также извлекает некоторые свежие пользовательские данные откуда-то (например, репозиторий).

Теперь, если выполнение выполняется в фоновом потоке, 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);        
        }
    });

}

Ответ 4

Взгляните на руководство по архитектуре Android, которое сопровождает новые модули архитектуры, такие как LiveData и ViewModel. Они подробно обсуждают этот точный вопрос.

В своих примерах они не помещают его в службу. Посмотрите, как они решаются с помощью модуля "Репозиторий" и "Дооснастка". В добавлениях внизу представлены более полные примеры, включая состояние сети, сообщения об ошибках и т.д.

Ответ 5

https://developer.android.com/topic/libraries/architecture/guide.html

Имеет очень хорошие ответы на вашу проблему. Прокрутите весь путь до конца, чтобы увидеть, как вы можете сохранить ответ в вашей базе данных и обновить пользовательский интерфейс с помощью LiveData.

enter image description here

Ответ 6

Если вы называете свой API в репозитории, то

В репозитории:

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, вы можете использовать ее без