バックグラウンドサービスと更新UIからViewModelのLiveDataを更新する方法


95

最近、Googleによって最近導入されたAndroidアーキテクチャを調査しています。ドキュメントから私はこれを見つけました:

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. そこでデータを取得できない場合は、WebサーバーからデータをフェッチするためのAPI呼び出しを行います。

  3. 取得したデータをデータベース(Room)に挿入し、データに応じてUIを更新します。

これを行うための推奨アプローチは何ですか?

メソッドServiceからAPIを呼び出すを開始した場合、そこから変数loadUsers()を更新するにはどうすればよいですか?MutableLiveData<List<User>> usersService


8
まず、リポジトリがありません。ViewModelがデータ読み込みタスクを実行してはいけません。それ以外は、Roomを使用しているため、サービスでViewModelのLiveDataを直接更新する必要はありません。サービスはデータをRoomに挿入するだけですが、ViewModelDataはRoomにのみアタッチし、Roomから更新を取得する必要があります(Serviceがデータを挿入した後)。しかし、絶対的な最高のアーキテクチャは、このページの下からNetworkBoundResourceクラスの実装を見用:developer.android.com/topic/libraries/architecture/guide.html
マーコ・ガジック

提案ありがとうございます:)
CodeCameo 2017年

1
リポジトリクラスは、ROOMまたはAndroidアーキテクチャコンポーネントを説明する公式ドキュメントでは言及されていません
ジョナサン

2
:リポジトリは、コードの分離やアーキテクチャの推奨ベストプラクティス、この例を見ているcodelabs.developers.google.com/codelabs/...
glisu

1
関数はloadUsers()基本的にユーザー情報を取得するためにリポジトリを呼び出します
CodeCameo

回答:


96

私はあなたがアンドロイドアーキテクチャコンポーネントを使用していると仮定しています。実際service, asynctask or handler、データを更新するためにどこを呼び出しているかは問題ではありません。サービスまたはasynctask using postValue(..)メソッドを使用してデータを挿入できます。クラスは次のようになります。

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

現状でusersLiveDataRoomデータベースは挿入された場所にユーザーデータを提供する責任があります。

Note: MVVMのようなアーキテクチャーでは、リポジトリは主にローカルデータとリモートデータのチェックとプルを担当します。


3
上記のように私のdbメソッドを呼び出すと、「java.lang.IllegalStateException:メインスレッド上のデータベースにアクセスできません」というメッセージが表示されます。何が問題なのかわかりますか?
pcj 2017

UIを使用している間、バックグラウンドでデータベースを更新するevernote-jobを使用しています。しかしLiveData更新されていません
Akshay Chordiya 2018

users.postValue(mUsers);->しかし、MutableLiveDataのpostValueメソッドはLiveDataを受け入れることができますか?
Cheok Yan Cheng

2
私の間違いはのvalue代わりにを使っていましたpostValue。ご回答有難うございます。
Hesam

1
@pcjあなたはスレッドで部屋の操作をするか、メインスレッドで操作を有効にする必要があります-より多くの答えのためにグーグル。
キロカーン2018

46

MutableLiveData<T>.postValue(T value)バックグラウンドスレッドからメソッドを使用できます。

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

19
念のため、postValue()はLiveDataでは保護されていますが、MutableLiveDataでは公開されています
Long Ranger

この作業は、バックグラウンドタスクからでも、.setValue()許可されない状況でも
davejoem

17

... loadUsers()関数でデータを非同期でフェッチしています... loadUsers()メソッドからAPIを呼び出すサービスを開始した場合、そのサービスからMutableLiveData>ユーザー変数を更新するにはどうすればよいですか?

アプリがバックグラウンドスレッドでユーザーデータをフェッチしている場合は、(setValueではなく)postValueが役立ちます。

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

}

リポジトリのgetUsers()メソッドは、非同期のデータ(このためにサービスまたはasynctaskを開始する可能性があります)のAPIを呼び出す場合があります。その場合、returnステートメントからリストを返すにはどうすればよいですか。
CodeCameo 2017年

おそらく、それは引数としてLiveDataオブジェクトをとることができます。(repository.getUsers(users)のようなもの)。次に、repositoryメソッドはusers.postValue自体を呼び出します。そしてその場合、loadUsersメソッドはバックグラウンドスレッドを必要としません。
albert cブラウン2017年

1
回答ありがとうございます....しかし、Room DBをストレージに使用していて、DAOがLiveData <Object>を返します... LiveDataをMutableLiveData <Object>に変換するにはどうすればよいですか?
キロカーン2018

RoomのDAOがMutableLiveDataオブジェクトを処理することを本当に意図されているとは思いません。DAOは基になるdbの変更を通知しますが、DBの値を変更する場合は、DAOのメソッドを呼び出します。また、多分ここでの議論は有用です:stackoverflow.com/questions/50943919/...
アルバート・CはBRAUN

3

やなどの新しいアーキテクチャモジュールに付属するAndroidアーキテクチャガイドご覧ください。彼らはこの正確な問題を詳細に議論します。LiveDataViewModel

彼らの例では、彼らはそれをサービスに入れていません。「リポジトリ」モジュールとレトロフィットを使用してどのように解決するかを見てください。下部の補遺には、ネットワーク状態の通信、エラーの報告など、より完全な例が含まれています。


2

リポジトリで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ラムダを使用すると、それなしで使用できます

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.