PS:時間管理は本当に重要です。管理がうまくできればできるほど、自己管理能力が高まります。共に頑張りましょう。
LiveData は、観察可能なデータホルダーのクラスで、通常の observable とは異なり、LiveData はライフサイクルを認識します。LiveData は Android Jetpack コンポーネントの一部でもあり、この記事では以下の点から LiveData を学びます:
- LiveData とは
- LiveData の利点
- LiveData の使用
- カスタム LiveData
- LiveData の変換
LiveData とは#
LiveData は、観察可能なデータホルダーのクラスで、通常の Observable とは異なり、LiveData は Activity、Fragment、Service のライフサイクルを認識し、LiveData はアクティブなライフサイクル状態にあるコンポーネントのオブザーバーのみを更新します。
アプリケーションコンポーネントのオブザーバーが STARTED または RESUMED の状態にある場合、LiveData はそのコンポーネントがアクティブであると見なし、そのコンポーネントは LiveData のデータ更新を受け取ります。他の登録されたコンポーネントオブザーバーは、データ更新を受け取ることはありません。
LiveData の利点#
- UI とデータの一貫性を保つ:LiveData はオブザーバーデザインパターンに従い、ライフサイクルが変化すると、LiveData は対応するアプリケーションコンポーネント(オブザーバー)に通知し、データが変化したときにも UI を更新するよう通知します。
- メモリリークを防ぐ:このオブザーバーは Lifecycle オブジェクトにバインドされており、Lifecycle オブジェクトのライフサイクルが破棄された後、これらのオブザーバーも自動的にクリーンアップされます。
- Activity が非アクティブな状態のときのクラッシュを防ぐ:オブザーバーが非アクティブな状態にある場合、オブザーバーは LiveData イベントを受け取りません。
- ライフサイクルを手動で管理しない:UI コンポーネントは関連データを観察するだけで、観察を停止したり再開したりすることはなく、LiveData は具体的なライフサイクルの変化に応じて自動的に管理します。
- 常に最新のデータを保持:ライフサイクルが非アクティブな状態の場合、非アクティブな状態からアクティブな状態に変わるときに最新のデータを受け取ります。例えば、バックグラウンドからフォアグラウンドに切り替えると自動的に最新のデータを受け取ります。
- 構成変更を正しく処理:Activity または Fragment がデバイスの構成変更により再作成される場合(画面の回転など)、最新のデータを即座に再受信します。
- サービスの共有:LiveData のデータの観察能力を利用して、ライフサイクルの状態に応じてサービスをいつでも接続または切断できます。
LiveData の使用#
特定の ViewModel クラスで LiveData データを定義し、対応する Activity または Fragment で LiveData データの変化を観察します。LiveData の使用により、データを Activity や Fragment に保存するのではなく、LiveData オブジェクトに保存されたデータを ViewModel に保持し、Activity と Fragment の作業負担を軽減します。Activity と Fragment はインターフェースの管理と表示のみを担当し、データを保存することはなく、構成変更時にデータに影響を与えません。
LiveData の使用方法のまとめ:
- ViewModel で具体的な LiveData インスタンスを作成してデータを保存します。以下のように:
public class MViewModel extends ViewModel {
private MutableLiveData<String> data;
public LiveData<String> getData(){
if (data == null){
data = new MutableLiveData<>();
data.postValue(DataUtil.getData());
}
return data;
}
}
- LiveData オブジェクトの observe または observeForever メソッドを使用して、対応する Activity や Fragment などをその LiveData オブジェクトのオブザーバーとして追加します。以下のように:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = DataBindingUtil.setContentView(this,R.layout.activity_main);
MViewModel mViewModel = ViewModelProviders.of(this).get(MViewModel.class);
mViewModel.getData().observe(this, new Observer<String>() {
@Override
public void onChanged(String msg) {
binding.setData(msg);
}
});
}
- LiveData の setValue または postValue を使用してデータを更新し、オブザーバー、つまり Activity または Fragment で更新データを取得します。以下のように:
public void setData(String data) {
mData.setValue(data);
}
では、LifecycleOwner のないオブザーバーを作成するにはどうすればよいですか? LiveData オブジェクトの observeForever メソッドを使用して、LifecycleOwner のないクラスをオブザーバーリストに追加できます。以下のように:
public class NoLifecycleOwner {
public void print(NViewModel viewModel, final TextView tvData){
//observeForeverオブジェクトを使用してLifecycleOwnerのないオブザーバーを作成
viewModel.getData().observeForever(new Observer<String>() {
@Override
public void onChanged(String s) {
tvData.setText("私はLifecycleOwnerのないオブザーバーです:" + s);
}
});
}
}
ただし、observeForever を使用してオブザーバーオブジェクトを取得すると、常にアクティブな状態になります。この場合、手動で removeObserver (Observer) を呼び出してそのオブザーバーを削除する必要があります。
カスタム LiveData#
LiveData オブジェクトはライフサイクルを認識する能力を持ち、外部にサービスを提供することができます。サービスの開始と停止を簡単に制御できます。以下のように:
/**
* カスタムLiveData
* Powered by jzman.
* Created on 2018/12/17 0017.
*/
public class CustomLiveData extends LiveData<String> {
@Override
protected void onActive() {
//アクティブなオブザーバーがこのメソッドを呼び出します
//サービスを開始...
}
@Override
protected void onInactive() {
//アクティブなオブザーバーがいない場合、このメソッドが呼び出されます
//サービスを終了...
}
}
LiveData の変換#
Lifecycle は Transformations というツールクラスを提供し、LiveData のデータ型を変換できます。LiveData がオブザーバーにデータを返す前に、LiveData 内のデータの具体的な型を変更できます。例えば、int 型の数字 1、2 などを中国語の大文字の壱、弐などに変換することができます。では、どのように使用するか、MapViewModel を作成します。以下のように:
/**
* LiveDataの変換
* Powered by jzman.
* Created on 2018/12/17 0017.
*/
public class MapViewModel extends ViewModel {
private MutableLiveData<Integer> mPrice = new MutableLiveData<>();
//Map
private LiveData<String> mMapPrice = Transformations.map(mPrice, new Function<Integer, String>() {
@Override
public String apply(Integer input) {
//Stringを返す
return Util.getNumberCapital(input);
}
});
//SwitchMap
private LiveData<String> mSwitchMapPrice = Transformations.switchMap(mPrice, new Function<Integer, LiveData<String>>() {
@Override
public LiveData<String> apply(Integer input) {
//LiveDataを返す
MutableLiveData<String> data = new MutableLiveData<>();
data.postValue(Util.getNumberCapital(input));
return data;
}
});
public void setPrice(int price) {
mPrice.setValue(price);
}
public LiveData<String> getPrice() {
//Map
return mMapPrice;
//SwitchMap
// return mSwitchMapPrice;
}
}
その後、Activity でデータの変化を観察します。以下のように:
public class MapActivity extends AppCompatActivity {
private MapViewModel mapViewModel;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final ActivityMapBinding binding = DataBindingUtil.setContentView(this,R.layout.activity_map);
mapViewModel = new MapViewModel();
mapViewModel.getPrice().observe(this, new Observer<String>() {
@Override
public void onChanged(String s) {
//数字を中国語の大文字に変換
binding.tvData.setText(s);
}
});
}
public void btnSetPrice1(View view) {
mapViewModel.setPrice(1);
}
public void btnSetPrice2(View view) {
mapViewModel.setPrice(2);
}
}
Transformations ツールクラスの map と switchMap メソッドの唯一の違いは、map が LiveData データを具体的な型に変換することです。上記のコードのように String に、switchMap は LiveData です。上記のコードの getNumberCapital メソッドは単に大文字と小文字を変換するメソッドなので、ここでは貼り付けません。テスト効果は以下の通りです:
map メソッドと switchMap メソッドの内部は、MediatorLiveData が変換に参加しており、MediatorLiveData を使用して map や switch のようなデータ型変換をさらに実現できます。