0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

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

Last updated at Posted at 2020-07-26

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

JetPackのライブラリのうち、今回はLiveData編です
DataBinding編からの続きです。

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

環境

  • 確認時はAndroid Studioのバージョンは 3.6.2を使用しました
  • JetPackAndroidXライブラリを利用するのでCompile SDK28以上にする必要があります

そもそもLiveDataってなに?

公式の説明によると

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

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

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

メリット

公式にはこんなに書いてありました!

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

たくさんあります!!

購読処理の実施(LiveDataとの連携)

DataBinding編で見ていた処理を確認してみましょう

使用箇所

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()メソッドで購読処理を実施しており、plantAndGardenPlantingsobserveしています。
    こうすることで、plantAndGardenPlantingsの値が変更された時にresultが更新されて、そこから算出された
    hasPlantingsRecyclerViewを更新します。

購読を実施するタイミングについて

この購読処理はGardenFragment#onCreateViewで実施されています。
その理由は下記によるものです。

  • 殆どの場合画面が作られた時にのみ実施されるのが適しているためです。
    • 例えば、ActivityonResume()などの場合は、繰り返し呼ばれる場合があるので不適切
  • アクティビティやフラグメントがアクティブになり次第、そのデータを表示できるため(その前に準備しておく必要がある!)

LiveDataクラスを見てみる

次にplantAndGardenPlantingsがどうなっているかをみてみましょう!

LiveDataクラスの定義

GardenPlantingListViewModel.kt
class GardenPlantingListViewModel internal constructor(
    gardenPlantingRepository: GardenPlantingRepository
) : ViewModel() {
    val plantAndGardenPlantings: LiveData<List<PlantAndGardenPlantings>> =
            gardenPlantingRepository.getPlantedGardens()
}

なにやらカッコが多くて見にくいですが
plantAndGardenPlantingsの型は LiveData<List<PlantAndGardenPlantings>> となっています
これはLiveDataクラスで、PlantAndGardenPlantingsクラスのリストの型をしているよ! という定義です。

GardenPlantingRepository.kt
class GardenPlantingRepository private constructor(
    private val gardenPlantingDao: GardenPlantingDao
) {
            :
            :
    fun getPlantedGardens() = gardenPlantingDao.getPlantedGardens()
            :
            :
}
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()

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メソッドを実施している部分をみてみると・・・

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のデータが更新されると、その度に通知が来る。
    • ただし、更新とは言っても変更する度ではなく、値がセットされる度に通知がくる。

以上です!

参考サイト

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?