前提
AndroidのAACの ViewModelProvider
を使っています。
"Don't keep activities" を有効にしたAndroid Emulator上で画面遷移を行いました。
画面は2つあり、それぞれ Activity1
と Activity2
とします。
Activity1
から Activity2
を startActivity()
で開始させ、戻るボタンを押すことで Activity2
から Activity1
へ画面を遷移させました。
すると、次のことが分かりました。
- "Don't keep activities" が無効なとき:
Activity1
に再び戻ってきたとき、遷移前と同じViewModelが取得されていた。
- "Don't keep activities" が有効なとき:
Activity1
に再び戻ってきたとき、遷移前と異なるViewModelが取得されていた。
これは次の一連の操作を行ったときの Activity1
と Activity1ViewModel
のハッシュコードの値を見ることで分かります。
- スマホを左へ90度回転させる。
- スマホを右へ90度回転させる。
startActivity()
でActivity2
を起動する。- 戻るボタンで
Activity1
へ戻る。
実現したいこと
ここで私が実現したいことは、Activityが破棄された後、ViewModelProvider
が元のViewModelのインスタンスを返すという動作をすることです。
スマホの画面を回転させたときにもActivityは破棄され、再生成されますが、このときに取得されるViewModelのハッシュコードを見る限り、私が予期していた動作をしているようです。
私がここで知りたいのは、Activity2
から戻ったときに取得されるViewModelが元のものであるといった挙動をさせる方法です。
- スマホが回転し、そのアクティビティが破棄される。
- 別のアクティビティを起動中にそのアクティビティが破棄される。
という2つの状況がありますが、取得されるViewModelのインスタンスに違いがあります。
私にはこの2つの状況の、動作に違いを生じさせるような違いはどこにあるのかが分かりません。
その違いが分かり、それに対する良いアプローチをし、ViewModelProvider
に思った通りの挙動をさせることがこの質問のゴールであると考えています。
また、この挙動をさせる代替案として次のことを考えました。
- ViewModelProviderに細工をする;
「型が Activity1
ならば問答無用で Activity1ViewModel
のシングルトンオブジェクトを返し、型が Activity2
ならば問答無用で Activity2ViewModel
のシングルトンオブジェクトを返す」といったような実装をする。
しかし、この実装には問題があります。
- 別物として扱いたい同じ型である2つのActivityを区別できない。
という点です。
よって、この解決方法は賢くないと考えました。
また、Model部にデータを持たせておくという方法も考えましたが、View側に近いようなデータや画面の状態をわざわざ持ち込むことが賢いとは思えません。
ViewとViewModelだけで完結させたいと思ったので、この解決方法は賢くないと考えました。
https://stackoverflow.com/questions/44256250/does-viewmodel-survive-activity-save-and-restore
the ViewModel is destroyed if your process is killed by Android.
システムによってキルされた場合、ViewModelさんも死んでしまうみたいですね。
しかし、できれば onSaveInstanceState()
を使いたくないのですよねぇ。
ProviderをいじるなどのViewModelの管理方法をいじる程度で対処できればいいのですが、それが無理なのであれば、諦めますけど。
コード・出力結果
Kotlin
1package sakana.tadano.androidsampleapp.views 2 3import android.app.Activity 4import android.content.Intent 5import android.os.Bundle 6import android.view.MenuItem 7import androidx.appcompat.app.AppCompatActivity 8import androidx.core.view.GravityCompat 9import androidx.lifecycle.Observer 10import androidx.lifecycle.ViewModelProvider 11import androidx.lifecycle.ViewModelProviders 12import kotlinx.android.synthetic.main.activity_main.* 13import sakana.tadano.androidsampleapp.R 14import sakana.tadano.androidsampleapp.viewmodels.Activity1ViewModel 15 16class Activity1 : AppCompatActivity() { 17 18 init { 19 println("Activity1::init") 20 } 21 22 val viewModel by lazy { 23 ViewModelProviders.of(this@Activity1, ViewModelProvider.AndroidViewModelFactory(this.application)).get(Activity1ViewModel::class.java) 24 } 25 26 override fun onCreate(savedInstanceState: Bundle?) { 27 super.onCreate(savedInstanceState) 28 29 // xmlとコードを紐付ける。 30 this.setContentView(R.layout.activity_main) 31 32 // ボタンを押すと画面遷移をする。 33 this.floatingActionButton.setOnClickListener { this.startActivity2() } 34 35 println("Activity1.onCreate() | Activity1: ${this.hashCode()}, Activity1ViewModel: ${this.viewModel.hashCode()}") 36 } 37 38 override fun onDestroy() { 39 println("Activity1.onDestroy()") 40 super.onDestroy() 41 } 42 43 // Activity2を開始する。 44 private fun startActivity2() { 45 val intent = Intent(this, Activity2::class.java).also { 46 it.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION) 47 it.addFlags(Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) 48 it.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) 49 it.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP) 50 it.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) 51 } 52 this.startActivity(intent) 53 } 54}
// 1. "Don't keep activities" が無効なとき: // 起動時 2019-03-28 23:02:38.493 22693-22693/sakana.tadano.androidsampleapp I/System.out: Activity1.onCreate() | Activity1: 179727580, Activity1ViewModel: 148278770 // 左へ回転時 2019-03-28 23:02:49.964 22693-22693/sakana.tadano.androidsampleapp I/System.out: Activity1.onCreate() | Activity1: 67624917, Activity1ViewModel: 148278770 // 右へ回転時 2019-03-28 23:02:53.718 22693-22693/sakana.tadano.androidsampleapp I/System.out: Activity1.onCreate() | Activity1: 36655929, Activity1ViewModel: 148278770 // Activity2から戻るボタンで戻ったとき // 破棄・再生成は行われなかった。
// 2. "Don't keep activities" が有効なとき: // 起動時 2019-03-28 23:06:58.982 23124-23124/sakana.tadano.androidsampleapp I/System.out: Activity1.onCreate() | Activity1: 179727580, Activity1ViewModel: 223137021 // 左へ回転時 2019-03-28 23:07:06.488 23124-23124/sakana.tadano.androidsampleapp I/System.out: Activity1.onCreate() | Activity1: 66361006, Activity1ViewModel: 223137021 // 右へ回転時 2019-03-28 23:07:09.728 23124-23124/sakana.tadano.androidsampleapp I/System.out: Activity1.onCreate() | Activity1: 260026956, Activity1ViewModel: 223137021 // Activity2から戻るボタンで戻ったとき 2019-03-28 23:07:20.337 23124-23124/sakana.tadano.androidsampleapp I/System.out: Activity1.onCreate() | Activity1: 226287672, Activity1ViewModel: 17244874
ありがとう。
よろしくおねがいします。
回答1件
あなたの回答
tips
プレビュー