質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
MVVM

MVVM(Model View ViewModel)は構築上のデザインパターンで、表現ロジック(ViewModel)によってデータ(Model)からページ(View)を分離させます。

Android

Androidは、Google社が開発したスマートフォンやタブレットなど携帯端末向けのプラットフォームです。 カーネル・ミドルウェア・ユーザーインターフェイス・ウェブブラウザ・電話帳などのアプリケーションやソフトウェアをひとつにまとめて構成。 カーネル・ライブラリ・ランタイムはほとんどがC言語/C++、アプリケーションなどはJavaSEのサブセットとAndroid環境で書かれています。

Kotlin

Kotlinは、ジェットブレインズ社のアンドリー・ブレスラフ、ドミトリー・ジェメロフが開発した、 静的型付けのオブジェクト指向プログラミング言語です。

Q&A

解決済

1回答

3927閲覧

【Android】Activityが破棄された後もViewModelProviderから同じViewModelが提供される方法

tadanoosakana

総合スコア30

MVVM

MVVM(Model View ViewModel)は構築上のデザインパターンで、表現ロジック(ViewModel)によってデータ(Model)からページ(View)を分離させます。

Android

Androidは、Google社が開発したスマートフォンやタブレットなど携帯端末向けのプラットフォームです。 カーネル・ミドルウェア・ユーザーインターフェイス・ウェブブラウザ・電話帳などのアプリケーションやソフトウェアをひとつにまとめて構成。 カーネル・ライブラリ・ランタイムはほとんどがC言語/C++、アプリケーションなどはJavaSEのサブセットとAndroid環境で書かれています。

Kotlin

Kotlinは、ジェットブレインズ社のアンドリー・ブレスラフ、ドミトリー・ジェメロフが開発した、 静的型付けのオブジェクト指向プログラミング言語です。

0グッド

0クリップ

投稿2019/03/28 14:40

編集2019/03/29 02:29

前提

AndroidのAACの ViewModelProvider を使っています。
"Don't keep activities" を有効にしたAndroid Emulator上で画面遷移を行いました。
画面は2つあり、それぞれ Activity1Activity2 とします。
Activity1 から Activity2startActivity() で開始させ、戻るボタンを押すことで Activity2 から Activity1 へ画面を遷移させました。
すると、次のことが分かりました。

  1. "Don't keep activities" が無効なとき:

Activity1 に再び戻ってきたとき、遷移前と同じViewModelが取得されていた。

  1. "Don't keep activities" が有効なとき:

Activity1 に再び戻ってきたとき、遷移前と異なるViewModelが取得されていた。

これは次の一連の操作を行ったときの Activity1Activity1ViewModel のハッシュコードの値を見ることで分かります。

  • スマホを左へ90度回転させる。
  • スマホを右へ90度回転させる。
  • startActivity()Activity2 を起動する。
  • 戻るボタンで Activity1 へ戻る。

実現したいこと

ここで私が実現したいことは、Activityが破棄された後、ViewModelProvider が元のViewModelのインスタンスを返すという動作をすることです。
スマホの画面を回転させたときにもActivityは破棄され、再生成されますが、このときに取得されるViewModelのハッシュコードを見る限り、私が予期していた動作をしているようです。
私がここで知りたいのは、Activity2 から戻ったときに取得されるViewModelが元のものであるといった挙動をさせる方法です。

  1. スマホが回転し、そのアクティビティが破棄される。
  2. 別のアクティビティを起動中にそのアクティビティが破棄される。

という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

ありがとう。
よろしくおねがいします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

jimbe

2019/03/29 00:58

コードの一部では無く, 再現出来るような全体のご提示を頂けますか?
guest

回答1

0

ベストアンサー

同一アプリ内でのアクティビティによる画面遷移や回転による再生成のみならず, システムによる破棄まで通して同一となりますと, データは永続化してもオブジェクトは再生成されたら「異なるモノ」ですから, ありえないように思います.

投稿2019/03/29 11:21

jimbe

総合スコア12646

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

jimbe

2019/03/29 11:22

ご期待に沿える回答を出せず申し訳ないです.
tadanoosakana

2019/03/29 12:14

ありがとうございます。 心の何処かでもしかしたらできるのでは、と思っていたのですが、その言葉で踏ん切りが付きました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問