如何从后台服务更新 ViewModel 的 LiveData 和 Update UI

How to update LiveData of a ViewModel from background service and Update UI

最近我正在探索 Android 架构,该架构最近由 google 介绍。从 Documentation 我发现了这个:

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

activity 可以按如下方式访问此列表:

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. 如果我没有在那里获取数据,我将进行 API 调用以从 Web 服务器获取数据。

  3. 我会将获取的数据插入数据库(房间)并根据数据更新UI。

执行此操作的推荐方法是什么?

如果我启动一个 Service 以从 loadUsers() 方法调用 API,我如何从 Service 更新 MutableLiveData<List<User>> users 变量?

我假设您正在使用 android architecture components。实际上,无论您在哪里调用 service, asynctask or handler 来更新数据都没有关系。您可以使用 postValue(..) 方法从服务或异步任务插入数据。您的 class 将如下所示:

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

由于 usersLiveDataRoom 数据库负责在插入的任何位置提供用户数据。

Note: 在类似MVVM的架构中,repository主要负责检查和拉取本地数据和远程数据。

看看 Android architecture guide 伴随新架构模块的 LiveDataViewModel。他们深入讨论了这个确切的问题。

在他们的示例中,他们没有将其放入服务中。看看他们是如何使用 "repository" 模块和 Retrofit 解决这个问题的。底部的附录包括更完整的示例,包括通信网络状态、报告错误等。

... in the loadUsers() function I am fetching the data asynchronously ... If I start a Service to call the API from the loadUsers() method, how can I update the MutableLiveData> users variable from that Service?

如果应用在后台线程上获取用户数据,postValue (rather than setValue) 将很有用。

在 loadData 方法中有一个对 MutableLiveData "users" 对象的引用。 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);        
        }
    });

}

您可以从后台线程使用 MutableLiveData<T>.postValue(T value) 方法。

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

如果您在存储库中调用 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;
}

Activity/Fragment

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

注意:这里使用JAVA_1.8 lambda,不用它也可以使用