web-dev-qa-db-ja.com

アクティビティまたはフラグメントの外部でViewModelインスタンスを取得する正しい方法

MainActivityのRoomデータベースから背景の場所を表示する場所アプリを構築しています。呼び出すことでViewModelを取得できます

_locationViewModel = ViewModelProviders.of(this).get(LocationViewModel.class);
locationViewModel.getLocations().observe(this, this);
_

BroadCastReceiverを介して位置情報の更新を受信したときに、定期的な背景の位置をRoomデータベースに保存する必要があります。 locationViewModel.getLocations().setValue()を呼び出して保存する必要があります

_public class LocationUpdatesBroadcastReceiver extends BroadcastReceiver {

    static final String ACTION_PROCESS_UPDATES =
            "com.google.Android.gms.location.sample.backgroundlocationupdates.action" +
                    ".PROCESS_UPDATES";

    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent != null) {
            final String action = intent.getAction();
            if (ACTION_PROCESS_UPDATES.equals(action)) {
                LocationResult result = LocationResult.extractResult(intent);
                if (result != null) {
                    List<Location> locations = result.getLocations();
                    List<SavedLocation> locationsToSave = covertToSavedLocations(locations)
                    //Need an instance of LocationViewModel to call locationViewModel.getLocations().setValue(locationsToSave)
                }
            }
        }
    }
}
_

このBroadcastReceiverのような非アクティビティクラスでLocationViewModelインスタンスを取得するにはどうすればよいですか。 BroadcastReceiverのlocationViewModel = ViewModelProviders.of(context).get(LocationViewModel.class)から受け取ったコンテキストであるonReceive (Context context, Intent intent)を呼び出すことは正しいですか?

ViewModelを取得した後、BroadcastReceiverはLifecycleOwnerではないため、 LiveData.observeForever および LiveData.removeObserver を使用する必要がありますか?

10
PrashanD

このBroadcastReceiverのような非アクティビティクラスでLocationViewModelインスタンスを取得するにはどうすればよいですか。

あなたはそれをするべきではありません。その悪い設計慣行。

LocationViewModel = ViewModelProviders.of(context).get(LocationViewModel.class)を呼び出すのは正しいですか。ここで、contextはBroadcastReceiverのonReceive(Context context、Intent intent)から受け取るコンテキストです。

いいえ、役に立たない

次のようにして、希望する結果を得ることができます:

別のシングルトンクラスで、Room DB操作をViewModelから分離します。 ViewModelおよびその他の必要な場所で使用します。ブロードキャストを受信したら、ViewModelではなく、このシングルトンクラスを介してデータをDBに書き込みます。

フラグメントのLiveDataを監視している場合は、ビューも更新されます。

7
Sagar

これは、ViewModelインスタンスを渡すアンチパターンです。

理想的には、ViewModelは入力ストリーム(BroasdcastReceiverを介した位置の更新、ユーザーのアクションなどの更新を可能にする)と出力ストリーム(Activity/Fragmentがユーザーに観察および表示するため)で一度設定されます。

大まかな依存関係の作成は次のようにする必要があります。できればDaggerのようなDIフレームワークを使用してください。

MainActivity

protected void onCreate(Bundle savedBundleState) {
    PublishSubject<List<SavedLocation>> currentLocationSubject = new PublishSubject();// here I'm using RxJava but you can use alternatives
    MyBroastcastReceiver broadcastReceiver = new MyBroadcastReceiver(currentLocationSubject);
    locationViewModel = ViewModelProviders.of(this).get(LocationViewModel.class);
    locationViewModel.setLocationInputStream(currentLocationSubject);
    locationViewModel.init();
}

MutableLiveDataは、ここでの入力ストリームに最適なRxJavaのサブジェクトに非常に似ています。

MyBroastcastReceiver

static final String ACTION_PROCESS_UPDATES =
            "com.google.Android.gms.location.sample.backgroundlocationupdates.action" +
                    ".PROCESS_UPDATES";

    private PublishSubject<List<SavedLocation>> currentLocationSubject = new PublishSubject();

    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent != null) {
            final String action = intent.getAction();
            if (ACTION_PROCESS_UPDATES.equals(action)) {
                LocationResult result = LocationResult.extractResult(intent);
                if (result != null) {
                    List<Location> locations = result.getLocations();
                    List<SavedLocation> locationsToSave = covertToSavedLocations(locations)
                    currentLocationSubject.onNext(locationsToSave);// add input to the input stream
                }
            }
        }
    }

LocationViewModel

Observable<List<SavedLocation>> inputLocationStream;
PublishSubject<List<SavedLocation>> outputLocationStream = new PublishSubject();

void setLocationInputStream(Observable<List<SavedLocation>> inputLocationStream) {
    this.inputLocationStream = inputLocationStream;
}

void init() {
    inputLocationStream.subscribe {
        saveLocationList -> {
            outputLocationStream.onNext(saveLocationList);
        }
    }
}

これで、RxJavaのObservableの代わりにLiveDataストリームにすることができる出力ストリームを観察できます。

この方法でデータは実際に一方向に流れ、MVVMアーキテクチャーに準拠

1
ericn