目的
Android, Javaで開発するにあたり、Fragment間のデータ受け渡しにBundleとViewModelどちらを使うべきか指標を明示する。
※ただしデータの値は変更しない前提です。
結論
Bundle:Fragment間で受け渡すデータ数が少ない場合
ViewModel:Fragment間で受け渡すデータ数が多い場合
のように使い分けるのが良いと思います。
はじめに
初めてAndroid開発をしたときに、Fragmentから別Fragmentにデータを受け渡すとき、メジャーな方法としてBundleとViewModelがあると知りました。
両方使ってみて、それぞれの利点・欠点が少し分かったので、まとめてみます。
ちなみに筆者は最初はBundleを使いまくっていたのですが、以下に記載する欠点により、ViewModelをメインで使うようになりました。
Bundle vs ViewModel
BundleとViewModelの利点・欠点をまとめます。
Bundle | ViewModel | |
---|---|---|
利点 | データの定義が不要 データのスコープが狭い |
データ名/型の管理がシンプル Fragmentへの渡し不要 |
欠点 | データ名/型の管理が煩雑 Fragmentへの渡し必要 |
データの定義が必要 データのスコープが広い |
以下、それぞれ説明していきます。
Bundle
利点
データの定義が不要
BundleはputStringやputIntなどでデータ名を指定し値設定、getStringやgetIntなどでデータ名を指定し値取得ができます。
public class FirstFlagment extends Fragment {
...
private void moveFragment() {
Bundle bundle = getArguents();
bundle.putInt("Apple", 100);// ★値設定
SecondFragment secondFragment = new SecondFragment();
SecondFragment.setArguments(bundle);
// フラグメント遷移
getChildFragmentManager()
.beginTransaction()
.add(R.id.first_layout, secondFragment)
.commit()
}
...
}
public class SecondFlagment extends Fragment {
...
@Override
public void onViewCreated(View view, Bundle bundle) {
Bundle bundle = getArguents();
int value = bundle.getInt("Apple");// ★値取得
...
}
...
}
上記のように、データ名を指定して簡単に値を設定し渡すことができます。
データのスコープが狭い
上記の例のように、Bundleを渡したフラグメントのみ、該当データを取得できるため、データのスコープは狭く制限できていると言えます。
欠点
データ名/型の管理が煩雑
データの種類が増えてくると、当然データ名が複数必要になります。
また、各データの型が違う場合は、どのデータがどの型か、の管理も必要になります。
要は、データの種類が増えると、取り出すときにどんなデータ名と型で登録したっけ?と分かりづらくなっちゃうってことです。
以下は、データの設定と取得でデータ名/型が一致していないため、NGとなるケースです。
bundle.putInt("Apple", 30);
bundle.putString("Orange", "30");
bundle.putInt("Lemon", 60);
int apple = bundle.getInt("Apple");// ← OK
int orange = bundle.getInt("Orange");// ← NG: getStringで取得していない
int lemon = bundle.getInt("Lemo");// ← NG: データ名誤り
Fragmentへの渡し必要
FragmentのsetArgumentsでBundleを渡す必要があります。
これを忘れると、次のFragmentでBundleを使えません。
Bundle bundle = getArguents();
bundle.putInt("Apple", 100);
SecondFragment secondFragment = new SecondFragment();
SecondFragment.setArguments(bundle);// ← これを忘れるとデータを渡せない
ViewModel
利点
データ名/型の管理がシンプル
ViewModelの属性としてデータを定義するため、データ名(変数名)と型をシンプルに管理できます。これで、データ名と型はこのクラスを見れば間違いなく分かります。
public class MyViewModel extends ViewModel {
public int apple;
public String orange;
public int lemon;
...
データの設定/取得は、ViewModelProviderでViewModelを取得して実行できます。
public class FirstFlagment extends Fragment {
...
private void moveFragment() {
MyViewModel viewModel = new ViewModelProvider(this).get(MyViewModel.class);
viewModel.apple = 100;// ★値設定
SecondFragment secondFragment = new SecondFragment();
// フラグメント遷移
getChildFragmentManager()
.beginTransaction()
.add(R.id.first_layout, secondFragment)
.commit()
}
...
}
public class SecondFlagment extends Fragment {
...
@Override
public void onViewCreated(View view, Bundle bundle) {
MyViewModel viewModel = new ViewModelProvider(this).get(MyViewModel.class);
int value = viewModel.apple;// ★値取得
...
}
...
}
Fragmentへの渡し不要
上記コードの通り、ViewModelのデータ取得/設定はViewModelProvider経由で行うので、bundleのように次フラグメントへの渡し忘れは起きません。
欠点
データの定義が必要
上記のコード例の通り、ViewModel継承したクラスを定義し、受け渡すデータをすべて属性として定義する必要があります。
そのためBundleと比べると、ひと手間が必要で、1,2個だけデータを受け渡したいときはわざわざViewModelを作るのは面倒に感じます。
データのスコープが広い
ViewModelは、ViewModelProviderを用いてどのフラグメントからも取得可能です。
よって、データのスコープが広くなっていると言えます。
チームで開発をしている場合、ViewModelの値がどこかで書き換えられているかもしれませんので、注意して使う必要があります。
私の場合、アプリ全体を一人で開発したため、ViewModelは読み取り専用と決めて使ったため、このデメリットは問題になりませんでした。
MyViewModel viewModel = new ViewModelProvider(this).get(MyViewModel.class);
まとめ
-
Bundle
データの定義が不要でスコープも限定できるが、
データ名/型の管理が煩雑でFragmentへの渡し必要。
⇒Fragment間で受け渡すデータ数が少ない場合 に使い易い。 -
ViewModel
データ名/型の管理がシンプルでFragmentへの渡し不要だが、
データの定義が必要でデータのスコープが広い。
⇒Fragment間で受け渡すデータ数が多い場合 に使い易い。
と結論づけました。
最後に
- 筆者はAndroid開発に携わって1年ほどです。
- まだまだ知見不足の点があるかと思いますので、お気づきの点はご指摘くださると助かります。
参考
https://developer.android.com/topic/libraries/architecture/viewmodel?hl=JA#sharing
https://qiita.com/m-coder/items/3a8e66d49f2830b09bf4