ViewModel の概要 Android Jetpack の一部
ViewModel
は、ライフサイクルを意識した方法で UI 関連のデータを保存し管理するためのクラスです。ViewModel
クラスを使用すると、画面の回転などの構成の変更後にデータを引き継ぐことができます。
アクティビティやフラグメントなどの UI コントローラのライフサイクルは Android フレームワークによって管理されます。フレームワークでは、デベロッパーの制御下にない特定のユーザー操作やデバイス イベントが発生した際の対応として、UI コントローラの破棄や再作成を決定する場合があります。
システムによって UI コントローラが破棄または再作成されると、UI コントローラに保存されている一時的な UI 関連のデータはすべて失われます。たとえば、アプリがいずれかのアクティビティに、ユーザーのリストを含める場合があります。構成の変更によってアクティビティが再作成されると、その新しいアクティビティはユーザーのリストを再取得する必要があります。単純なデータの場合、アクティビティは onSaveInstanceState()
メソッドを使用して onCreate()
のバンドルからデータを復元できますが、このアプローチが適しているのは、シリアル化の後に逆シリアル化を行える少量のデータの場合だけです。ユーザーやビットマップのリストのように大量のデータが見込まれる場合には適していません。
もう 1 つの問題は、UI コントローラでは実行に時間がかかる非同期呼び出しを頻繁に行う必要があることです。UI コントローラは非同期呼び出しを管理し、破棄された後にシステムが呼び出しのクリーンアップを行ってメモリリークが発生しないようにする必要があります。この管理ではメンテナンスを何度も実施する必要があり、構成の変更によってオブジェクトが再作成された場合には、すでに行った呼び出しを再度行わなければならないこともあるため、リソースが無駄になります。
アクティビティやフラグメントなどの UI コントローラの主な目的は、UI データの表示、ユーザー操作への対応、オペレーティング システムとのやり取り(権限リクエストなど)を行うことです。UI コントローラに対してデータベースやネットワークからのデータ読み込みも行うよう要求すると、クラスが肥大化することになります。 UI コントローラに過度の役割を割り当てると、アプリの作業を他のクラスに任せずに 1 つのクラスですべて処理しようとすることになり、テストも極めて困難になります。
ビューデータの所有権を UI コントローラのロジックから切り離すことで、複雑さが軽減され、効率性が高まります。
ViewModel を実装する
アーキテクチャ コンポーネントには、UI コントローラ向けに UI のデータを準備する ViewModel
ヘルパークラスが用意されています。ViewModel
オブジェクトは構成の変更時に自動的に保持されるため、このオブジェクトが保持しているデータは後続のアクティビティやフラグメントのインスタンスがすぐに利用できます。たとえば、アプリでユーザーのリストを表示する必要がある場合は、次のサンプルコードに示すように、ユーザーのリストを取得して保持する役割をアクティビティやフラグメントではなく ViewModel
に割り当てます。
View
class MyViewModel : ViewModel() { private val users: MutableLiveData<List<User>> by lazy { MutableLiveData<List<User>>().also { loadUsers() } } fun getUsers(): LiveData<List<User>> { return users } private fun loadUsers() { // Do an asynchronous operation to fetch users. } }
View
public class MyViewModel extends ViewModel { private MutableLiveData<List<User>> users; public LiveData<List<User>> getUsers() { if (users == null) { users = new MutableLiveData<List<User>>(); loadUsers(); } return users; } private void loadUsers() { // Do an asynchronous operation to fetch users. } }
次のように、リストにはアクティビティからアクセスできます。
View
class MyActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { // Create a ViewModel the first time the system calls an activity's onCreate() method. // Re-created activities receive the same MyViewModel instance created by the first activity. // Use the 'by viewModels()' Kotlin property delegate // from the activity-ktx artifact val model: MyViewModel by viewModels() model.getUsers().observe(this, Observer<List<User>>{ users -> // update UI }) } }
View
public class MyActivity extends AppCompatActivity { public void onCreate(Bundle savedInstanceState) { // Create a ViewModel the first time the system calls an activity's onCreate() method. // Re-created activities receive the same MyViewModel instance created by the first activity. MyViewModel model = new ViewModelProvider(this).get(MyViewModel.class); model.getUsers().observe(this, users -> { // update UI }); } }
再作成されたアクティビティが受け取る MyViewModel
インスタンスは、最初のアクティビティで作成されたものと同じです。オーナーのアクティビティが終了すると、フレームワークは、リソースをクリーンアップできるように ViewModel
オブジェクトの onCleared()
メソッドを呼び出します。
ViewModel
オブジェクトは、ビューや LifecycleOwners
の特定のインスタンスよりも生存期間が長くなるように設計されています。この設計では、ViewModel
を対象とするテストを簡単に作成することもできます。このオブジェクトでは、ビュー オブジェクトと Lifecycle
オブジェクトが認識されないためです。ViewModel
オブジェクトには LifecycleObservers
(LiveData
オブジェクトなど)を含めることができます。ただし、ViewModel
オブジェクトでは、ライフサイクル対応の監視可能オブジェクト(LiveData
オブジェクトなど)への変更を監視しないでください。ViewModel
で Application
のコンテキストが必要な場合は(システム サービスを検出する場合など)、AndroidViewModel
クラスを拡張して、Application
を受け取るコンストラクタを作成できます(Application
クラスは Context
を拡張したものであるため)。
依存関係を使用して ViewModel を作成する
依存関係注入のベスト プラクティスに基づいて、ViewModel はコンストラクタのパラメータとして依存関係を取得できます。ほとんどの場合、これらはドメインレイヤまたはデータレイヤのタイプです。フレームワークは ViewModel を提供しているため、ViewModel のインスタンスを作成するには特別なメカニズムが必要です。そのメカニズムとは、ViewModelProvider.Factory
インターフェースです。このインターフェースの実装のみが、適切なスコープで ViewModel をインスタンス化することができます。
ViewModel クラスがコンストラクタ内で依存関係を受け取る場合は、ViewModelProvider.Factory
インターフェースを実装するファクトリを提供してください。create(Class<T>, CreationExtras)
関数をオーバーライドして、ViewModel の新しいインスタンスを提供します。
CreationExtras
を使用すると、ViewModel のインスタンス化に役立つ関連情報にアクセスできます。エクストラからアクセスできるキーのリストは次のとおりです。
キー | 機能 |
---|---|
ViewModelProvider.NewInstanceFactory.VIEW_MODEL_KEY |
この Key により、ViewModelProvider.get() に渡したカスタムキーにアクセスできます。 |
ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY |
Application クラスのインスタンスにアクセスできます。 |
SavedStateHandleSupport.DEFAULT_ARGS_KEY |
SavedStateHandle の作成に使用する引数のバンドルにアクセスできます。 |
SavedStateHandleSupport.SAVED_STATE_REGISTRY_OWNER_KEY |
ViewModel の作成に使用されている SavedStateRegistryOwner にアクセスできます。 |
SavedStateHandleSupport.VIEW_MODEL_STORE_OWNER_KEY |
ViewModel の作成に使用されている ViewModelStoreOwner にアクセスできます。 |
SavedStateHandle
の新しいインスタンスを作成するには、CreationExtras.createSavedStateHandle()
.createSavedStateHandle() 関数を使用して ViewModel に渡します。
次の例は、Application
クラスをスコープとするリポジトリと SavedStateHandle
を依存関係として取得する ViewModel のインスタンスを提供する方法を示しています。
View
import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.Companion.APPLICATION_KEY import androidx.lifecycle.createSavedStateHandle import androidx.lifecycle.viewmodel.CreationExtras class MyViewModel( private val myRepository: MyRepository, private val savedStateHandle: SavedStateHandle ) : ViewModel() { // ViewModel logic // ... // Define ViewModel factory in a companion object companion object { val Factory: ViewModelProvider.Factory = object : ViewModelProvider.Factory { @Suppress("UNCHECKED_CAST") override fun <T : ViewModel> create( modelClass: Class<T>, extras: CreationExtras ): T { // Get the Application object from extras val application = checkNotNull(extras[APPLICATION_KEY]) // Create a SavedStateHandle for this ViewModel from extras val savedStateHandle = extras.createSavedStateHandle() return MyViewModel( (application as MyApplication).myRepository, savedStateHandle ) as T } } } }
View
import static androidx.lifecycle.SavedStateHandleSupport.createSavedStateHandle; import static androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.APPLICATION_KEY; import androidx.lifecycle.SavedStateHandle; import androidx.lifecycle.ViewModel; import androidx.lifecycle.viewmodel.ViewModelInitializer; public class MyViewModel extends ViewModel { public MyViewModel( MyRepository myRepository, SavedStateHandle savedStateHandle ) { /* Init ViewModel here */ } static final ViewModelInitializer<MyViewModel> initializer = new ViewModelInitializer<>( MyViewModel.class, creationExtras -> { MyApplication app = (MyApplication) creationExtras.get(APPLICATION_KEY); assert app != null; SavedStateHandle savedStateHandle = createSavedStateHandle(creationExtras); return new MyViewModel(app.getMyRepository(), savedStateHandle); } ); }
次に、ViewModel のインスタンスを取得するときに、このファクトリを使用できます。
View
import androidx.activity.viewModels class MyActivity : AppCompatActivity() { private val viewModel: MyViewModel by viewModels { MyViewModel.Factory } // Rest of Activity code }
View
import androidx.appcompat.app.AppCompatActivity; import androidx.lifecycle.ViewModelProvider; public class MyActivity extends AppCompatActivity { MyViewModel myViewModel = new ViewModelProvider( this, ViewModelProvider.Factory.from(MyViewModel.initializer) ).get(MyViewModel.class); // Rest of Activity code }
Compose
import androidx.lifecycle.viewmodel.compose.viewModel @Composable fun MyScreen( modifier: Modifier = Modifier, viewModel: MyViewModel = viewModel(factory = MyViewModel.Factory) ) { // ... }
または、ViewModel ファクトリ DSL を使用し、より慣用的な Kotlin API を使用してファイルを作成します。
View
import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.Companion.APPLICATION_KEY import androidx.lifecycle.createSavedStateHandle import androidx.lifecycle.viewmodel.initializer import androidx.lifecycle.viewmodel.viewModelFactory class MyViewModel( private val myRepository: MyRepository, private val savedStateHandle: SavedStateHandle ) : ViewModel() { // ViewModel logic // Define ViewModel factory in a companion object companion object { val Factory: ViewModelProvider.Factory = viewModelFactory { initializer { val savedStateHandle = createSavedStateHandle() val myRepository = (this[APPLICATION_KEY] as MyApplication).myRepository MyViewModel( myRepository = myRepository, savedStateHandle = savedStateHandle ) } } } }
2.5.0 より前のバージョンの ViewModel のファクトリ
2.5.0 より前のバージョンの ViewModel を使用している場合は、ViewModelProvider.Factory
を拡張するクラスのサブセットのファクトリを提供し、create(Class<T>)
関数を実装する必要があります。ViewModel で必要となる依存関係に応じて、異なるクラスを拡張する必要があります。
AndroidViewModelFactory
:Application
クラスが必要な場合。AbstractSavedStateViewModelFactory
:SavedStateHandle
を依存関係として渡す必要がある場合。
Application
または SavedStateHandle
が不要な場合は、ViewModelProvider.Factory
から拡張するだけです。
次の例では、リポジトリと SavedStateHandle
タイプを依存関係として取得する ViewModel に対して AbstractSavedStateViewModelFactory
を使用します。
View
class MyViewModel( private val myRepository: MyRepository, private val savedStateHandle: SavedStateHandle ) : ViewModel() { // ViewModel logic ... // Define ViewModel factory in a companion object companion object { fun provideFactory( myRepository: MyRepository, owner: SavedStateRegistryOwner, defaultArgs: Bundle? = null, ): AbstractSavedStateViewModelFactory = object : AbstractSavedStateViewModelFactory(owner, defaultArgs) { @Suppress("UNCHECKED_CAST") override fun <T : ViewModel> create( key: String, modelClass: Class<T>, handle: SavedStateHandle ): T { return MyViewModel(myRepository, handle) as T } } } }
View
import androidx.annotation.NonNull; import androidx.lifecycle.AbstractSavedStateViewModelFactory; import androidx.lifecycle.SavedStateHandle; import androidx.lifecycle.ViewModel; public class MyViewModel extends ViewModel { public MyViewModel( MyRepository myRepository, SavedStateHandle savedStateHandle ) { /* Init ViewModel here */ } } public class MyViewModelFactory extends AbstractSavedStateViewModelFactory { private final MyRepository myRepository; public MyViewModelFactory( MyRepository myRepository ) { this.myRepository = myRepository; } @SuppressWarnings("unchecked") @NonNull @Override protected <T extends ViewModel> T create( @NonNull String key, @NonNull Class<T> modelClass, @NonNull SavedStateHandle handle ) { return (T) new MyViewModel(myRepository, handle); } }
次に、ファクトリを使用して ViewModel を取得します。
View
import androidx.activity.viewModels class MyActivity : AppCompatActivity() { private val viewModel: MyViewModel by viewModels { MyViewModel.provideFactory((application as MyApplication).myRepository, this) } // Rest of Activity code }
View
public class MyActivity extends AppCompatActivity { MyViewModel myViewModel = new ViewModelProvider( this, new MyViewModelFactory(((MyApplication) getApplication()).getMyRepository()) ).get(MyViewModel.class); // Rest of Activity code }
Compose
import androidx.lifecycle.viewmodel.compose.viewModel @Composable fun MyScreen( modifier: Modifier = Modifier, viewModel: MyViewModel = viewModel( factory = MyViewModel.provideFactory( (LocalContext.current.applicationContext as MyApplication).myRepository, owner = LocalSavedStateRegistryOwner.current ) ) ) { // ... }
ViewModel のライフサイクル
ViewModel
オブジェクトのスコープは、ViewModel
を取得する際に、ViewModelProvider
に渡される ViewModelStoreOwner
の Lifecycle
に設定されます。ViewModel
は、スコープの ViewModelStoreOwner
が完全に消えるまでメモリ内に残ります。
- アクティビティの場合は終了時。
- フラグメントの場合はデタッチ時。
- Navigation エントリの場合はバックスタックからの削除時。
これにより、ViewModel は、構成の変更後に引き継ぐデータを保存するための優れたソリューションとなっています。
図 1 に、回転を経て終了するまでのアクティビティのさまざまなライフサイクルの状態を示します。この図には、関連するアクティビティのライフサイクルの横に ViewModel
のライフタイムも示されています。この図はアクティビティの状態を示していますが、フラグメントのライフサイクルの状態も基本的には同じです。
通常は、アクティビティ オブジェクトの onCreate()
メソッドが最初に呼び出されたときに、ViewModel
をリクエストします。onCreate()
は、デバイスの画面が回転されたときなど、アクティビティの生存期間全体を通して複数回呼び出されることがあります。ViewModel
は、最初に ViewModel
をリクエストしてからアクティビティが終了して破棄されるまで存在します。
ViewModel API
ViewModelProvider.get()
メソッドを使用すると、任意の ViewModelStoreOwner
をスコープとする ViewModel のインスタンスを取得できます。Kotlin ユーザーには、最も一般的なユースケースで使用できるさまざまな拡張関数があります。すべての Kotlin 拡張関数の実装では、内部で ViewModelProvider API が使用されます。
最も近い ViewModelStoreOwner をスコープとする ViewModel
ViewModel をアクティビティ、フラグメント、またはナビゲーション グラフのデスティネーションにスコープ設定できます。アクティビティ、フラグメント、Navigation ライブラリによって提供される viewModels()
拡張関数と、Compose の viewModel()
関数を使用すると、最も近い ViewModelStoreOwner
をスコープとする ViewModel のインスタンスを取得できます。
View
class MyActivity : AppCompatActivity() { // ViewModel API available in activity.activity-ktx // The ViewModel is scoped to `this` Activity val viewModel: MyViewModel by viewModels() } class MyFragment : Fragment() { // ViewModel API available in fragment.fragment-ktx // The ViewModel is scoped to `this` Fragment val viewModel: MyViewModel by viewModels() }
View
public class MyActivity extends AppCompatActivity { // The ViewModel is scoped to `this` Activity MyViewModel viewModel = new ViewModelProvider(this).get(MyViewModel.class); } public class MyFragment extends Fragment { // The ViewModel is scoped to `this` Fragment MyViewModel viewModel = new ViewModelProvider(this).get(MyViewModel.class); }
Compose
@Composable fun MyScreen( modifier: Modifier = Modifier, // ViewModel API available in lifecycle.lifecycle-viewmodel-compose // The ViewModel is scoped to the closest ViewModelStoreOwner provided // via the LocalViewModelStoreOwner CompositionLocal. This could be the // host Activity or Fragment, or destination of the Navigation graph. viewModel: MyViewModel = viewModel() ) { /* ... */ }
任意の ViewModelStoreOwner をスコープとする ViewModel
View システムの ComponentActivity.viewModels()
関数と Fragment.viewModels()
関数、Compose の viewModel()
関数は、ViewModel のインスタンスがどの ViewModelStoreOwner
にスコープ設定されるかを指定するオプションの ownerProducer
パラメータを取ります。次のサンプルは、親フラグメントをスコープとする ViewModel のインスタンスを取得する方法を示しています。
View
class MyFragment : Fragment() { // ViewModel API available in fragment.fragment-ktx // The ViewModel is scoped to the parent of `this` Fragment val viewModel: SharedViewModel by viewModels( ownerProducer = { requireParentFragment() } ) }
View
public class MyFragment extends Fragment { SharedViewModel viewModel; @Override public void onViewCreated(View view, Bundle savedInstanceState) { // The ViewModel is scoped to the parent of `this` Fragment viewModel = new ViewModelProvider(requireParentFragment()) .get(SharedViewModel.class); } }
Compose
@Composable fun MyScreen( context: Context = LocalContext.current, // ViewModel API available in lifecycle.lifecycle-viewmodel-compose // The ViewModel is scoped to the parent of the host Fragment // where this composable function is called viewModel: SharedViewModel = viewModel( viewModelStoreOwner = (context as Fragment).requireParentFragment() ) ) { /* ... */ }
フラグメントからアクティビティをスコープとする ViewModel を取得することは、アプリ内での一般的なユースケースであるため、activityViewModels()
View 拡張関数を利用できます。View と Kotlin を使用しない場合は、上記と同じ API を使用して適切なオーナーを渡すことができます。
View
class MyFragment : Fragment() { // ViewModel API available in fragment.fragment-ktx // The ViewModel is scoped to the host Activity val viewModel: SharedViewModel by activityViewModels() }
View
public class MyFragment extends Fragment { SharedViewModel viewModel; @Override public void onViewCreated(View view, Bundle savedInstanceState) { // The ViewModel is scoped to the host Activity viewModel = new ViewModelProvider(requireActivity()) .get(SharedViewModel.class); } }
Compose
@Composable fun MyScreen( context: Context = LocalContext.current, // ViewModel API available in lifecycle.lifecycle-viewmodel-compose // The ViewModel is scoped to the Activity of the host Fragment // where this composable function is called viewModel: SharedViewModel = viewModel( viewModelStoreOwner = (context as Fragment).requireActivity() ) ) { /* ... */ }
ナビゲーション グラフをスコープとする ViewModel
ナビゲーション グラフも ViewModel のストアオーナーです。Navigation Fragment または Navigation Compose を使用している場合は、navGraphViewModels(graphId)
View 拡張関数を使用してナビゲーション グラフをスコープとする ViewModel のインスタンスを取得できます。
View
class MyFragment : Fragment() { // ViewModel API available in navigation.navigation-fragment // The ViewModel is scoped to the `nav_graph` Navigation graph val viewModel: SharedViewModel by navGraphViewModels(R.id.nav_graph) // Equivalent navGraphViewModels code using the viewModels API val viewModel: SharedViewModel by viewModels( { findNavController().getBackStackEntry(R.id.nav_graph) } ) }
View
public class MyFragment extends Fragment { SharedViewModel viewModel; @Override public void onViewCreated(View view, Bundle savedInstanceState) { NavController navController = NavHostFragment.findNavController(this); NavBackStackEntry backStackEntry = navController.getBackStackEntry(R.id.my_graph); // The ViewModel is scoped to the `nav_graph` Navigation graph viewModel = new ViewModelProvider(backStackEntry).get(SharedViewModel.class); } }
Compose
@Composable fun MyAppNavHost() { // ... composable("myScreen") { backStackEntry -> val parentEntry = remember(backStackEntry) { navController.getBackStackEntry("parentNavigationRoute") } // The ViewModel is scoped to the `parentNavigationRoute` Navigation graph val parentViewModel: SharedViewModel = viewModel(parentEntry) // ... } }
Jetpack Navigation に加えて Hilt を使用している場合は、次のように hiltNavGraphViewModels(graphId)
API を使用できます。
View
class MyFragment : Fragment() { // ViewModel API available in hilt.hilt-navigation-fragment // The ViewModel is scoped to the `nav_graph` Navigation graph // and is provided using the Hilt-generated ViewModel factory val viewModel: SharedViewModel by hiltNavGraphViewModels(R.id.nav_graph) }
View
public class MyFragment extends Fragment { SharedViewModel viewModel; @Override public void onViewCreated(View view, Bundle savedInstanceState) { NavController navController = NavHostFragment.findNavController(this); NavBackStackEntry backStackEntry = navController.getBackStackEntry(R.id.my_graph); // The ViewModel is scoped to the `nav_graph` Navigation graph // and is provided using the Hilt-generated ViewModel factory viewModel = new ViewModelProvider( backStackEntry, HiltViewModelFactory.create(getContext(), backStackEntry) ).get(SharedViewModel.class); } }
Compose
@Composable fun MyAppNavHost() { // ... composable("myScreen") { backStackEntry -> val parentEntry = remember(backStackEntry) { navController.getBackStackEntry("parentNavigationRoute") } // ViewModel API available in hilt.hilt-navigation-compose // The ViewModel is scoped to the `parentNavigationRoute` Navigation graph // and is provided using the Hilt-generated ViewModel factory val parentViewModel: SharedViewModel = hiltViewModel(parentEntry) // ... } }
フラグメント間でデータを共有する
よくあるのは、アクティビティ内の複数のフラグメントが互いにやり取りする必要があるケースです。一方のフラグメントでリスト内のアイテムを選択し、もう一方のフラグメントで選択したアイテムのコンテンツを表示する、一般的な分割ビュー(list-detail
)のフラグメントのケースについて考えてみます。このケースは決して簡単ではありません。両方のフラグメントがインターフェースの記述を定義する必要があり、オーナーのアクティビティは、この 2 つのフラグメントを互いにバインドする必要があるためです。また、どちらのフラグメントも、もう一方のフラグメントがまだ作成されていないシナリオ、または参照できないシナリオに対処する必要があります。
この一般的な問題に対処するには、ViewModel
オブジェクトを使用します。次のサンプルコードに示すように、これらのフラグメントではアクティビティ スコープを使って ViewModel
を共有し、このやり取りを処理できます。
View
class SharedViewModel : ViewModel() { val selected = MutableLiveData- () fun select(item: Item) { selected.value = item } } class ListFragment : Fragment() { private lateinit var itemSelector: Selector // Use the 'by activityViewModels()' Kotlin property delegate // from the fragment-ktx artifact private val model: SharedViewModel by activityViewModels() override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) itemSelector.setOnClickListener { item -> // Update the UI } } } class DetailFragment : Fragment() { // Use the 'by activityViewModels()' Kotlin property delegate // from the fragment-ktx artifact private val model: SharedViewModel by activityViewModels() override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) model.selected.observe(viewLifecycleOwner, Observer
- { item -> // Update the UI }) } }
View
public class SharedViewModel extends ViewModel { private final MutableLiveData<Item> selected = new MutableLiveData<Item>(); public void select(Item item) { selected.setValue(item); } public LiveData- getSelected() { return selected; } } public class ListFragment extends Fragment { private SharedViewModel model; public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); model = new ViewModelProvider(requireActivity()).get(SharedViewModel.class); itemSelector.setOnClickListener(item -> { model.select(item); }); } } public class DetailFragment extends Fragment { public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); SharedViewModel model = new ViewModelProvider(requireActivity()).get(SharedViewModel.class); model.getSelected().observe(getViewLifecycleOwner(), item -> { // Update the UI. }); } }
両方のフラグメントが、それらを含むアクティビティを取得することに注意してください。このように、それぞれのフラグメントが ViewModelProvider
を取得するとき、スコープがアクティビティに設定された同じ SharedViewModel
インスタンスを受け取ります。
この方法には次のようなメリットがあります。
- アクティビティは何もする必要がなく、フラグメント間のやり取りについて何も認識する必要がありません。
- フラグメントは、
SharedViewModel
コントラクト以外は互いについて認識する必要がありません。フラグメントの一方が存在しなくなっても、もう一方は通常どおり処理を継続します。 - フラグメントはそれぞれ独自のライフサイクルを持ち、もう一方のフラグメントのライフサイクルによる影響を受けません。2 つのフラグメントが入れ替わっても、UI は何の問題もなく処理を継続します。
ローダを ViewModel に置き換える
CursorLoader
などのローダクラスを頻繁に使用すると、アプリの UI のデータとデータベースの同期を維持できます。ViewModel
を他のいくつかのクラスと組み合わせて使用することで、ローダの代わりにできます。ViewModel
を使用すると、UI コントローラをデータ読み込み処理から分離できます。これにより、クラス間の強い参照を減らすことができます。
ローダを使用する一般的なアプローチの 1 つとして、アプリで CursorLoader
を使用してデータベースのコンテンツを監視することがあります。データベース内の 1 つの値が変更されると、ローダがデータの再読み込みを自動的にトリガーして UI を更新します。

ViewModel
を Room と LiveData と組み合わせて使用することで、ローダの代わりにできます。ViewModel
を使用すると、デバイスの構成の変更後にもデータが引き継がれるようになります。Room は、データベースが変更されたときに LiveData
に通知します。LiveData はそれを受けて、変更されたデータを使用して UI を更新します。

ViewModel でコルーチンを使用する
ViewModel
には、Kotlin コルーチンのサポートが含まれています。詳しくは、Android アーキテクチャ コンポーネントで Kotlin コルーチンを使用するをご覧ください。
追加情報
データの複雑さが増すと、データの読み込みのためだけに別のクラスを使用することがあります。ViewModel
の目的は、UI コントローラのデータをカプセル化して、構成の変更後にもデータが引き継がれるようにすることです。構成の変更の前後におけるデータの読み込み、永続化、管理の方法については、UI の状態の保存をご覧ください。
Android アプリのアーキテクチャ ガイドでは、これらの機能を処理するためにリポジトリ クラスを作成することが推奨されています。
参考情報
ViewModel
クラスについて詳しくは、以下のリソースをご覧ください。
サンプル
- Android アーキテクチャ コンポーネントの基本的なサンプル
- Sunflower(Android Jetpack を使用した Android 開発のおすすめの方法を示すガーデニング アプリ)
Codelab
- Android Room とビュー(Java)、(Kotlin)
- Android ライフサイクル対応コンポーネントの Codelab
ブログ
- ViewModel: 簡単な例
- ViewModel: 永続性、onSaveInstanceState()、UI の状態の復元、ローダ
- ViewModel と LiveData: パターンとアンチパターン
- Kotlin の詳細: 簡単なラムダ式の構文を理解する
- Kotlin の詳細: スコープ関数
- Kotlin の詳細: カスタム アクセサーを使用するタイミング
- アーキテクチャ コンポーネントを使用したライフサイクル対応データの読み込み