新型コロナの影響で自宅待機になってしまい、その間勉強するものとしてSunflower
リポジトリを勧めてもらいました。
JetPackのライブラリのうち、今回はLiveData
編です
DataBinding編からの続きです。
尚、引用しているソースは明記しているところ以外は、基本的には全てSunflower
のリポジトリのものです。
環境
- 確認時は
Android Studio
のバージョンは3.6.2
を使用しました -
JetPack
はAndroidX
ライブラリを利用するのでCompile SDK
を28
以上にする必要があります
そもそもLiveDataってなに?
公式の説明によると
-
LiveData
は監視可能なデータホルダー クラス - 通常の監視と異なるのはライフサイクルに応じた監視が可能
- ライフサイクルがアクティブなオブザーバーのみを更新する
- ライフサイクルの状態が
STARTED
またはRESUMED
の場合 = アクティブ -
LiveData
は更新に関する情報をアクティブなオブザーバーにのみ通知 -
非アクティブ
なオブザーバーには、変更に関する通知は行われない
- ライフサイクルの状態が
-
オブザーバー
はLifecycleOwner
インターフェースを実装するオブジェクトとペアで登録できる- ペアリング → 対応する
Lifecycle
オブジェクトの状態がDESTROYED
→オブザーバー
を削除可能
- ペアリング → 対応する
-
アクティビティ
とフラグメント
で利用すると、LiveData
オブジェクトを安全に監視可能- ライフサイクルが破棄されるとすぐに登録が解除される
だそうで、ざっくりいうと
LifecycleOwner
が破棄されれば登録が解除される通知機能を持つクラス
という感じでしょうか??(^^;
メリット
公式にはこんなに書いてありました!
- UI をデータの状態と一致させることができる
- メモリリークが発生しない
- 停止されたアクティビティに起因するクラッシュが発生しない
- 手動によるライフサイクル処理が行われない
- データが常に最新
- 適切な設定の変更
- リソースの共有
たくさんあります!!
購読処理の実施(LiveDataとの連携)
DataBinding編
で見ていた処理を確認してみましょう
使用箇所
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = FragmentGardenBinding.inflate(inflater, container, false)
:
:
subscribeUi(adapter, binding)
return binding.root
}
private fun subscribeUi(adapter: GardenPlantingAdapter, binding: FragmentGardenBinding) {
viewModel.plantAndGardenPlantings.observe(viewLifecycleOwner) { result ->
binding.hasPlantings = !result.isNullOrEmpty()
adapter.submitList(result)
}
- 名前の通り
subscribeUi()
メソッドで購読処理を実施しており、plantAndGardenPlantings
をobserve
しています。
こうすることで、plantAndGardenPlantings
の値が変更された時にresultが更新されて、そこから算出された
hasPlantings
でRecyclerView
を更新します。
購読を実施するタイミングについて
この購読処理はGardenFragment#onCreateView
で実施されています。
その理由は下記によるものです。
- 殆どの場合画面が
作られた時
にのみ実施されるのが適しているためです。- 例えば、
Activity
のonResume()
などの場合は、繰り返し呼ばれる場合があるので不適切
- 例えば、
- アクティビティやフラグメントが
アクティブ
になり次第、そのデータを表示できるため(その前に準備しておく必要がある!)
LiveDataクラスを見てみる
次にplantAndGardenPlantings
がどうなっているかをみてみましょう!
LiveDataクラスの定義
class GardenPlantingListViewModel internal constructor(
gardenPlantingRepository: GardenPlantingRepository
) : ViewModel() {
val plantAndGardenPlantings: LiveData<List<PlantAndGardenPlantings>> =
gardenPlantingRepository.getPlantedGardens()
}
なにやらカッコが多くて見にくいですが
plantAndGardenPlantings
の型は LiveData<List<PlantAndGardenPlantings>>
となっています
これはLiveDataクラス
で、PlantAndGardenPlantingsクラスのリストの型
をしているよ! という定義です。
class GardenPlantingRepository private constructor(
private val gardenPlantingDao: GardenPlantingDao
) {
:
:
fun getPlantedGardens() = gardenPlantingDao.getPlantedGardens()
:
:
}
@Dao
interface GardenPlantingDao {
:
:
@Transaction
@Query("SELECT * FROM plants WHERE id IN (SELECT DISTINCT(plant_id) FROM garden_plantings)")
fun getPlantedGardens(): LiveData<List<PlantAndGardenPlantings>>
:
:
}
GardenPlantingRepository#getPlantedGardens()
→ GardenPlantingDao#getPlantedGardens()
と順に呼び出していき、
最終的にはDBからデータを取得していますね。
Observe
の定義とonChanged()
@MainThread inline fun <T> LiveData<T>.observe(
owner: LifecycleOwner,
crossinline onChanged: (T) -> Unit
): Observer<T> {
val wrappedObserver = Observer<T> { t -> onChanged.invoke(t) }
observe(owner, wrappedObserver)
return wrappedObserver
}
そして、LiveDataの定義をみてみます。。。
とても複雑ですが、ポイントは
-
observe()
はメインスレッドで実施する - 第二引数のラムダ式
onChange()
も、メインスレッドで実施する
改めて最初のobserve
メソッドを実施している部分をみてみると・・・
private fun subscribeUi(adapter: GardenPlantingAdapter, binding: FragmentGardenBinding) {
viewModel.plantAndGardenPlantings.observe(viewLifecycleOwner) { result ->
binding.hasPlantings = !result.isNullOrEmpty()
adapter.submitList(result)
}
-
observe()
メソッドのラムダ式で書いてある第二引数のonChange()
はメインスレッド
で実行する。 - resultに値がセットされた場合、通知がくる
- result には
list<PlantAndGardenPlantings>
が返ってくる
注意点
- 実際に動作確認を実施したところ、
値が変更した時
のみに動作するのかと思っていたのですが、
そうではなくLiveDataに値がセットされる度
に、onChanged()
が実行されていました。
(onChanged()
じゃない気がしますが。。。)
まとめ
-
LiveData
とは、LifecycleOwner
が破棄されれば登録が解除される通知機能を持つクラスです
*LiveData<データ型>
で定義します - LiveDataの購読処理はonCreate() (Fragmentの場合はonCreateView)で実施するのが殆どの場合適している。
- 購読された
LiveData
のデータが更新されると、その度に通知が来る。- ただし、更新とは言っても
変更する度
ではなく、値がセットされる度
に通知がくる。
- ただし、更新とは言っても
以上です!
参考サイト
- Sunflowerリポジトリ
- LiveData: 公式ドキュメント
- DataBinding: 公式ドキュメント