1. ホーム
  2. アンドロイド

AndroidアーキテクチャのコンポーネントであるViewModelとLiveDataの紹介と使い方

2022-02-24 22:36:20
<パス

ViewModelについて

ViewModel クラスは、ライフサイクルに焦点を当てた方法で UI 関連データを保存および管理するために設計されています。
例えば、Activityの構成が変わった(画面が回転した)場合、Activityを再作成し、再度onCreate()メソッドを呼び出すことになります。onSaveInstanceState()メソッドでデータを保存し、Bundle経由でonCreate()メソッドから復元することもできますが、この方法はそれ用にシリアライズできる少量のデータに対してのみ有効で、大量になる可能性のあるデータに対しては有効ではありません。ViewModel を使用する場合、ViewModel は自動的に以前のデータを保持し、新しい Activity または Fragment で使用できるようにします。Framework は現在の Activity がシステムによって破棄されるまで ViewModel の onCleared() メソッドを呼び出すので、onCleared() メソッドの中でリソースのクリーンアップを行うことができます。

LiveDataについて

LiveDataは、観測可能なデータホルダークラスです。一般的なオブザーバーとは異なり、LiveDataはライフサイクルを意識しています。つまり、アクティビティ、フラグメント、サービスなどの他のアプリケーション コンポーネントのライフサイクルを尊重し、この認識により、LiveDataはライフサイクル状態にあるアプリケーション コンポーネントのみを更新するようになります。

liveData はオブザーバークラスによって表されるオブザーバを、そのライフサイクルが STARTED または RESUMED 状態である場合にアクティブとして扱い、そのデータの変更を通知します。

LiveDataのメリット。

  • UIインターフェースでのデータ状態の確保

    LiveDataはObserverパターンに従っています。liveDataはライフサイクルの状態が変化するとObserverオブジェクトに通知し、それらのObserverオブジェクトのUIを更新します。Observerは変更が発生するたびにではなく、アプリケーションデータが変わるたびにUIを更新することが可能です。

  • メモリーリークなし

    オブザーバーは対応するライフサイクルにバインドされた後、ページが破棄される際に自動的に削除され、メモリオーバーを引き起こすことはありません。

  • アクティビティが見えないため、クラッシュしない

    アクティビティが表示されていない場合、LiveDataはデータの変更があってもオブザーバーに通知しません。これは、オブザーバーのLifeCyeleがこの時点ではSTARTEDまたはRESUMEDの状態でないためです。

  • コンフィギュレーション変更

    <ブロッククオート

    現在のActivityの設定変更(画面の向きなど)により、onCreateから再歩行し、その時点でオブザーバーは設定変更前の最新データを即座に受け取ります。

  • 共有リソース

    システムサービスに1回接続したLocationLivaDataが1つあれば、すべてのオブザーバーをサポートすることができます。

  • ヒューマンライフサイクルプロセッシングを廃止

    ActivityやFragmentは、データを見る必要があるときにデータを見るだけでよく、ライフサイクルの変更に悩まされることはもうないのです。すべてLiveDataに任せて、自動で管理してもらいましょう。

ViewModel と LiveData ライブラリの依存関係の追加

まず、GoogleのMavenリポジトリ(プロジェクトの一番外側のbuild.gradleファイル)に以下を追加します。

allprojects {
    repositories {
        jcenter()
        google()
    }
}

次に、app/build.gradle ファイルに依存関係を追加します。

dependencies {
    compile fileTree(include: ['*.jar'], dir: 'libs')
    // ViewModel and LiveData dependencies
    implementation "android.arch.lifecycle:extensions:1.1.0"
    annotationProcessor "android.arch.lifecycle:compiler:1.1.0"
    ......
}

ViewModelの定義とLiveDataの作成

public class AccountModel extends AndroidViewModel{

    // Create LiveData
    private MutableLiveData<AccountBean> mAccount = new MutableLiveData<>();

    public AccountModel(@NonNull Application application) {
        super(application);
    }

    public void setAccount(String name, String phone, String blog){
        mAccount.setValue(new AccountBean(name, phone, blog));
    }

    public MutableLiveData<AccountBean> getAccount(){
        return mAccount;
    }

    // When MyActivity is destroyed, Framework will call onCleared() of ViewModel
    @Override
    protected void onCleared() {
        Log.e("AccountModel", "==========onCleared()==========");
        super.onCleared();
    }
}

を使用しています。

public class MainActivity extends AppCompatActivity {

    private AccountModel mModel;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        final TextView mText = findViewById(R.id.textView);
        mModel = ViewModelProviders.of(this).get(AccountModel.class);
        findViewById(R.id.main_set_button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // Set a data
                // mModel.setAccount("QinchuanJunior", "182*****008", "http://blog.csdn.net/mjb00000");
                // post a piece of data
                mModel.getAccount().postValue(new AccountBean("QinchuanJunior", "182*****008", "http://blog.csdn.net/mjb00000"));
            }
        });
        mModel.getAccount().observe(this, new Observer<AccountBean>() {
            @Override
            public void onChanged(@Nullable AccountBean accountBean) {
                mText.setText(AccountModel.getFormatContent(accountBean.getName(), accountBean.getPhone(), accountBean.getBlog())));
            }
        });
    }
}

同一Activity内でのデータ共有

同じActivityに複数のFragmentがある場合、データの受け渡しは通常、Acticityが連携するインターフェースを定義することで行われます。ActivityとFragmentの間の通信は、ViewModelを使うことで簡単に解決できます。

  • Activityはこれら2つのFragment間の通信を邪魔するようなことをする必要はない。
  • フラグメントは互いのことを知る必要がなく、片方が見えなくなったとしても、もう片方は問題なく動作する。
  • Fragmentは独自のライフサイクルを持ち、互いに干渉せず、BをFragmentCに置き換えても、FragmentAは問題なく動作する。
ActivityとFragment、FragmentとFragmentの間のデータ共有
public class MainActivity extends AppCompatActivity {

    private AccountModel mModel;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        final TextView mText = findViewById(R.id.textView);
        // Add the Fragment trying to
        getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container_1, new TopFragment()).commit();
        getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container_2, new BottomFragment()).commit();
        // Initialize the ViewModel
        mModel = ViewModelProviders.of(this).get(AccountModel.class);
        findViewById(R.id.main_set_button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // Set a data
                // mModel.setAccount("QinchuanJunior", "182*****008", "http://blog.csdn.net/mjb00000");
                // post a piece of data
                mModel.getAccount().postValue(new AccountBean("QinchuanJunior", "182*****008", "http://blog.csdn.net/mjb00000"));
            }
        });
        mModel.getAccount().observe(this, new Observer<AccountBean>() {
            @Override
            public void onChanged(@Nullable AccountBean accountBean) {
                mText.setText(AccountModel.getFormatContent(accountBean.getName(), accountBean.getPhone(), accountBean.getBlog())));
            }
        });
    }
}

フラグメント1

public class TopFragment extends Fragment {

    private AccountModel mModel;
    private TextView mText;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_top, container, false);
    }

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        mText = view.findViewById(R.id.fragment_text_view);
        mModel = ViewModelProviders.of(getActivity()).get(AccountModel.class);
        view.findViewById(R.id.fragment_set_button).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                mModel.getAccount().postValue(new AccountBean("QinchuanJunior", "182*****008", "This data was Post from Fragment"));
            }
        });
        mModel.getAccount().observe(this, new Observer<AccountBean>() {
            @Override
            public void onChanged(@Nullable AccountBean accountBean) {
                mText.setText(AccountModel.getFormatContent(accountBean.getName(), accountBean.getPhone(), accountBean.getBlog())));
            }
        });
    }
}

フラグメント2

public class BottomFragment extends Fragment {

    private AccountModel mModel;
    private TextView mText;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_bottom, container, false);
    }

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        mText = view.findViewById(R.id.fragment_text_view);
        mModel = ViewModelProviders.of(getActivity()).get(AccountModel.class);
        mModel.getAccount().observe(this, new Observer<AccountBean>() {
            @Override
            public void onChanged(@Nullable AccountBean accountBean) {
                mText.setText(AccountModel.getFormatContent(accountBean.getName(), accountBean.getPhone(), accountBean.getBlog())));
            }
        });
    }
}

画像例

アクティビティからデータを投稿する

フラグメントからデータをポストアウトする

ケースソースを見る