【问题标题】:How to update LiveData of a ViewModel from background service and Update UI如何从后台服务更新 ViewModel 的 LiveData 并更新 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
        });
    }
}

我的问题是,我要这样做:

  1. loadUsers() 函数中,我异步获取数据,我将首先检查数据库(房间)中的数据

  2. 如果我没有在那里获取数据,我将调用 API 从 Web 服务器获取数据。

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

推荐的方法是什么?

如果我启动 Service 以从 loadUsers() 方法调用 API,我如何从 Service 更新 MutableLiveData&lt;List&lt;User&gt;&gt; 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获取用户信息

标签: android viewmodel background-service android-room android-architecture-components


【解决方案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)
}

由于usersLiveData,所以Room 数据库负责为用户提供任何插入的数据。

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

【讨论】:

  • 我得到“java.lang.IllegalStateException:无法访问主线程上的数据库,因为它”在调用我的数据库方法时,你能告诉我可能出了什么问题吗?
  • 我正在使用 evernote-job,它在我使用 UI 时在后台更新数据库。但是LiveData 没有更新
  • users.postValue(mUsers); -> 但是,MutableLiveData 的 postValue 方法能接受 LiveData 吗???
  • 我的错误是使用value 而不是postValue。谢谢你的回答。
  • @pcj 您需要在线程中执行房间操作或在主线程上启用操作 - 谷歌以获得更多答案。
【解决方案2】:

您可以在后台线程中使用MutableLiveData&lt;T&gt;.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 ...如何将 LiveData 转换为 MutableLiveData?
  • 我不认为 Room 的 DAO 真的打算处理 MutableLiveData 对象。 DAO 会通知您底层数据库的更改,但是,如果您想更改数据库中的值,您将调用 DAO 的方法。也可能这里的讨论很有用:stackoverflow.com/questions/50943919/…
【解决方案4】:

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

在他们的示例中,他们没有将其放入服务中。看看他们如何使用“存储库”模块和改造来解决它。底部的附录包括更完整的示例,包括通信网络状态、报告错误等。

【讨论】:

    【解决方案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,你可以不使用它

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-01-29
      • 2013-10-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-14
      • 2015-07-20
      相关资源
      最近更新 更多