如何从后台服务更新 ViewModel 的 LiveData 并更新 UI
Posted
技术标签:
【中文标题】如何从后台服务更新 ViewModel 的 LiveData 并更新 UI【英文标题】:How to update LiveData of a ViewModel from background service and Update UI 【发布时间】:2017-10-27 13:09:56 【问题描述】:最近我正在探索谷歌最近推出的 android 架构。从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
活动可以按如下方式访问此列表:
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
);
我的问题是,我要这样做:
在 loadUsers()
函数中,我异步获取数据,我将首先检查数据库(房间)中的数据
如果我没有在那里获取数据,我将调用 API 从 Web 服务器获取数据。
我会将获取的数据插入数据库(房间)并根据数据更新 UI。
推荐的方法是什么?
如果我启动 Service
以从 loadUsers()
方法调用 API,我如何从 Service
更新 MutableLiveData<List<User>> users
变量?
【问题讨论】:
首先,您缺少一个存储库。您的 ViewModel 不应执行任何数据加载任务。除此之外,由于您使用 Room,您的服务不必直接更新 ViewModel 中的 LiveData。 Service 只能将数据插入 Room,而您的 ViewModelData 应该只附加到 Room,并从 Room 获取更新(在 Service 插入数据之后)。但是对于绝对最佳的架构,请查看本页底部的 NetworkBoundResource 类实现:developer.android.com/topic/libraries/architecture/guide.html 谢谢你的建议:) Repositor 类在描述 ROOM 或 android 架构组件的官方文档中未提及 Repository 是代码分离和架构的建议最佳实践,看这个例子:codelabs.developers.google.com/codelabs/… 函数loadUsers()
基本上会调用repo获取用户信息
【参考方案1】:
我假设您使用的是android architecture components。实际上,无论您在哪里调用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 的架构中,repository 主要负责检查和拉取本地数据和远程数据。
【讨论】:
我得到“java.lang.IllegalStateException:无法访问主线程上的数据库,因为它”在调用我的数据库方法时,你能告诉我可能出了什么问题吗? 我正在使用 evernote-job,它在我使用 UI 时在后台更新数据库。但是LiveData
没有更新
users.postValue(mUsers);
-> 但是,MutableLiveData 的 postValue 方法能接受 LiveData 吗???
我的错误是使用value
而不是postValue
。谢谢你的回答。
@pcj 您需要在线程中执行房间操作或在主线程上启用操作 - 谷歌以获得更多答案。【参考方案2】:
您可以在后台线程中使用MutableLiveData<T>.postValue(T value)
方法。
private void loadUsers()
// do async operation to fetch users and use postValue() method
users.postValue(listOfData)
【讨论】:
提醒一下,postValue() 在 LiveData 中是受保护的,但在 MutableLiveData 中是公开的 即使在后台任务中以及在不允许.setValue()
的情况下也能工作【参考方案3】:
... 在 loadUsers() 函数中,我正在异步获取数据... 如果我启动服务以从 loadUsers() 方法调用 API,如何 我可以从中更新 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);
);
【讨论】:
Repository 的getUsers()
方法可能会为异步数据调用 api(可能为此启动服务或异步任务),在这种情况下,它如何从它的 return 语句中返回 List?
也许它可以将 LiveData 对象作为参数。 (类似于 repository.getUsers(users))。然后存储库方法将调用 users.postValue 本身。而且,在这种情况下,loadUsers 方法甚至不需要后台线程。
感谢您的回答 .... 但是,我使用 Room DB 进行存储,DAO 返回 LiveData
我不认为 Room 的 DAO 真的打算处理 MutableLiveData 对象。 DAO 会通知您底层数据库的更改,但是,如果您想更改数据库中的值,您将调用 DAO 的方法。也可能这里的讨论很有用:***.com/questions/50943919/…【参考方案4】:
看看Android architecture guide 与LiveData
和ViewModel
等新架构模块相伴的。他们深入讨论了这个确切的问题。
在他们的示例中,他们没有将其放入服务中。看看他们如何使用“存储库”模块和改造来解决它。底部的附录包括更完整的示例,包括通信网络状态、报告错误等。
【讨论】:
【参考方案5】:如果你在 Repository 中调用你的 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 lambda,你可以不使用它
【讨论】:
以上是关于如何从后台服务更新 ViewModel 的 LiveData 并更新 UI的主要内容,如果未能解决你的问题,请参考以下文章