web-dev-qa-db-ja.com

Android画面回転時にViewModelが再作成されました

アーキテクチャコンポーネントViewModelが保持されないケースを発見しました-簡単に言えば、次のようになります:

  1. アクティビティが開始され、ViewModelインスタンスが作成されます
  2. アクティビティはバックグラウンドに置かれます
  3. デバイス画面が回転している
  4. アクティビティはフォアグラウンドに戻されます
  5. ViewModelのonClearedメソッドが呼び出され、新しいオブジェクトが作成されます

この場合、Androidの正常な動作で、私のViewModelインスタンスが破壊されますか?そうであれば、その状態を維持するための推奨される解決策はありますか?

onClearedが呼び出されたときにそれを保存することは、私が考えることができる1つの方法ですが、アクティビティが実際に終了するときにも状態を永続化します。もう1つの方法は、onRestoreInstanceStateを使用することですが、(アプリがバックグラウンドにある場合だけでなく)画面が回転するたびに発生します。

そのようなケースを処理するための銀の弾丸はありますか?

8
tomwyr

はい@tomwyr、これはAndroidフレームワークからのバグでした。 バグの詳細

修正は28.0.0-alpha3およびAndroidX 1.0.0-alpha3で利用可能です

しかし、上記のバージョンに今すぐ更新したくない場合は、次のように解決できます(これは悪い解決策であることはわかっていますが、他の良い方法は見ていません

アクティビティでonDestroy methodをオーバーライドして、すべての必須フィールドをローカル変数に保存しますsuper.onDestroyを呼び出す前に。次に、super.onDestroyを呼び出してからViewModelを再度初期化し、必要なフィールドをViewModelの新しいインスタンスに割り当てます。

isFinishing

以下のコードはKotlinにあります

override fun onDestroy() {
     val oldViewModel = obtainViewModel()

     if (!isFinishing) { //isFinishing will be false in case of orientation change

          val requiredFieldValue = oldViewModel.getRequiredFieldValue()

          super.onDestroy

         val newViewModel = obtainViewModel()

         if (newViewModel != oldViewModel) { //View Model has been destroyed
              newViewModel.setRequiredFieldValue(requiredFieldValue)
          }
      } else {
         super.onDestroy
      }
 }

private fun obtainViewModel(): SampleViewModel {
      return ViewModelProviders.of(this).get(SampleViewModel::class.Java)
}
7

申し訳ありませんが、ViewModelの唯一の目的は、データの所有者がさまざまなライフサイクルイベントを経験する間、データを存続させて保持することです(つまり、「状態を保存する」)。したがって、自分で「状態を保存」する必要はありません。

このことから、「正常な動作ではない」ことがわかります。 onCleared()は、アクティビティが終了した後にのみ呼び出されます(再度作成されることはありません)。

ViewModelを使用してViewModelProviderを作成していますか、それともコンストラクタを使用してインスタンスを作成していますか?

あなたの活動では、次のようなものが必要です:

// in onCreate() - for example - of your activity
model = ViewModelProviders.of(this).get(MyViewModel.class);
// then use it anywhere in the activity like so
model.someAsyncMethod().observe(this, arg -> {
    // do sth...
});

これにより、期待どおりの効果が得られます。

0
Ace

サポートライブラリ/ compileSDK/targetSDKを28に変更します。

マルチウィンドウでも同様の問題がありました。分割画面に切り替えると、viewModelが再作成されます。サポートライブラリ28で問題が解決しました。 (私のライフサイクルのバージョンは1.1.1です)

0
Feng Shi