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

最近我正在研究 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()函数中,我异步获取数据,首先检查数据库(Room)中的数据

  2. 如果我没有得到那里的数据,我会做一个 API 调用,以获取数据从网络服务器。

  3. 我将插入获取的数据到数据库(房间) ,并根据数据更新用户界面。

这样做的推荐方法是什么?

如果我启动一个 ServiceloadUsers()方法调用 API,我如何更新该 ServiceMutableLiveData<List<User>> users变量?

88870 次浏览

I am assuming that you are using android architecture components. Actually it doesn't matter wherever you are calling service, asynctask or handler to update the data. You can insert the data from the service or from the asynctask using postValue(..) method. Your class would look like this:

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

As the users is LiveData, Room database is responsible for providing users data wherever it is inserted.

Note: In MVVM like architecture, the repository is mostly responsible for checking and pulling local data and remote data.

Take a look at the Android architecture guide that accompanies the new architecture modules like LiveData and ViewModel. They discuss this exact issue in depth.

In their examples they don't put it in a service. Take a look at how they solve it using a "repository" module and Retrofit. The addendums at the bottom include more complete examples including communicating network state, reporting errors, etc.

... 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?

If the app is fetching user data on a background thread, postValue (rather than setValue) will be useful.

In the loadData method there is a reference to the MutableLiveData "users" object. The loadData method also fetches some fresh user data from somewhere (for example, a repository).

Now, if execution is on a background thread, MutableLiveData.postValue() updates outside observers of the MutableLiveData object.

Maybe something like this:

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


}

You can use MutableLiveData<T>.postValue(T value) method from background thread.

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

If you are calling your api in Repository then,

In Repository:

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

In Activity/Fragment

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

Note : Using JAVA_1.8 lambda here, you can use without it