2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

[Java]Android Fragment間のデータ受け渡し Bundle vs ViewModel

Last updated at Posted at 2021-12-01

目的

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などでデータ名を指定し値取得ができます。

FirstFlagment.java

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()
    }
    ...
}
SecondFlagment.java

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の属性としてデータを定義するため、データ名(変数名)と型をシンプルに管理できます。これで、データ名と型はこのクラスを見れば間違いなく分かります。

MyViewModel.java

public class MyViewModel extends ViewModel {
    public int apple;
    public String orange;
    public int lemon;
    ...

データの設定/取得は、ViewModelProviderでViewModelを取得して実行できます。

FirstFlagment.java

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()
    }
    ...
}
SecondFlagment.java

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は読み取り専用と決めて使ったため、このデメリットは問題になりませんでした。

ViewModelProvider経由でどこからでもデータ取得可能
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

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?