Sunflowerリポジトリで学ぶJetPack〜LiveData編


新型コロナの影響で自宅待機になってしまい、その間勉強するものとして`Sunflower`リポジトリを勧めてもらいました。

JetPackのライブラリのうち、今回は`LiveData`編です
[DataBinding編](https://qiita.com/apimama/items/87da362855a9ca74e167)からの続きです。

尚、引用しているソースは明記しているところ以外は、基本的には全て`Sunflower`のリポジトリのものです。　

# 環境
* 確認時は`Android Studio`のバージョンは `3.6.2`を使用しました
* `JetPack`は`AndroidX`ライブラリを利用するので`Compile SDK`を`２８`以上にする必要があります

# そもそもLiveDataってなに？
公式の説明によると

* `LiveData`は監視可能なデータホルダー クラス
* 通常の監視と異なるのは**ライフサイクル**に応じた監視が可能
* ライフサイクルが**アクティブ**なオブザーバーのみを更新する  
    * ライフサイクルの状態が`STARTED` または `RESUMED`の場合 ＝ **アクティブ**
    * `LiveData`は更新に関する情報を**アクティブ**なオブザーバーにのみ通知
    * `非アクティブ`なオブザーバーには、変更に関する通知は`行われない`
* `オブザーバー`は`LifecycleOwner`インターフェースを実装するオブジェクトとペアで登録できる
    * ペアリング　→　対応する`Lifecycle`オブジェクトの状態が`DESTROYED` →　`オブザーバー`を削除可能
* `アクティビティ`と`フラグメント`で利用すると、`LiveData`オブジェクトを安全に監視可能
    * ライフサイクルが破棄されるとすぐに登録が解除される

だそうで、ざっくりいうと

**`LifecycleOwner`が破棄されれば登録が解除される通知機能を持つクラス**
という感じでしょうか？？（^^;

# メリット
公式にはこんなに書いてありました！

* UI をデータの状態と一致させることができる
* メモリリークが発生しない
* 停止されたアクティビティに起因するクラッシュが発生しない
* 手動によるライフサイクル処理が行われない
* データが常に最新
* 適切な設定の変更
* リソースの共有

たくさんあります！！

# 購読処理の実施(LiveDataとの連携)
`DataBinding編`で見ていた処理を確認してみましょう

## 使用箇所
```kotlin:GardenFragment.kt
    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クラスの定義
```kotlin:GardenPlantingListViewModel.kt
class GardenPlantingListViewModel internal constructor(
    gardenPlantingRepository: GardenPlantingRepository
) : ViewModel() {
    val plantAndGardenPlantings: LiveData<List<PlantAndGardenPlantings>> =
            gardenPlantingRepository.getPlantedGardens()
}
```
なにやらカッコが多くて見にくいですが
`plantAndGardenPlantings`の型は `LiveData<List<PlantAndGardenPlantings>>` となっています
これは`LiveDataクラス`で、`PlantAndGardenPlantingsクラスのリストの型`をしているよ！　という定義です。

```kotlin:GardenPlantingRepository.kt
class GardenPlantingRepository private constructor(
    private val gardenPlantingDao: GardenPlantingDao
) {
            :
            :
    fun getPlantedGardens() = gardenPlantingDao.getPlantedGardens()
            :
            :
}
```
```kotlin:GardenPlantingDao.kt
@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()`
```kotlin:LiveData.kt
@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`メソッドを実施している部分をみてみると・・・

```kotlin:GardenFragment.kt
    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リポジトリ](https://github.com/android/sunflower)
* [LiveData](https://developer.android.com/topic/libraries/architecture/livedata): 公式ドキュメント
* [DataBinding](https://developer.android.com/topic/libraries/data-binding): 公式ドキュメント