AndroidアーキテクチャのコンポーネントであるViewModelとLiveDataの紹介と使い方
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())));
}
});
}
}
画像例
アクティビティからデータを投稿する
フラグメントからデータをポストアウトする
関連
-
AndroidStudioは、新しいプロジェクト:エミュレータを作成した後、エラーを報告します。ERROR: x86 emulation currently requires hardware acceleration!
-
アプリがGoogle検索でインデックスされない
-
SolutionMethod がスーパークラスのメソッドをオーバーライドしない。
-
エラーを解決する SSLピアが正しくシャットダウンされない
-
Androidです。AAPT2 エラーの正しい対処法:ファイルリソースのリンクに失敗した
-
adb push 権限拒否の解決策
-
Android ListViewでaddHeaderを使用する
-
Androidレイアウトにおけるmargin,padding,alignの使い分けと違いについて
-
adb remount の解決に失敗しました。
-
Androidアプリケーションのウィンドウ(Activity)のビューオブジェクト(View)の生成過程の解析
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
アンドロイド アルメアビ アルメアビ-v7a
-
RecyclerView がアダプタが接続されていませんと表示され、レイアウトソリューションをスキップする
-
障害発生 [INSTALL_FAILED_OLDER_SDK] 解決方法
-
解決 仮想メソッド '...' を呼び出そうとした。ListAdapter' を null オブジェクトの参照で呼び出そうとした。
-
Android ProgressBarの詳しい解説とカスタマイズ方法
-
Android Studioのヒント - これを読めば、すべてのヒントが役に立つ
-
appの実行エラー:ターゲットデバイスが見つかりませんでした。
-
android.view.inflateexception バイナリ xml ファイル行例外の解決方法
-
com.android.supportのバージョンの競合に対する解決策
-
Android ボタンにボーダーと背景色を追加する