【Android Architecture Components 】PagingLibraryをなぜ使うのか

コンバンハ、saikiです。

こちらはAndroid Advent Calendar 2018、12日目の記事となります!メリクリ!間に合ってよかった。本当に。

さて、AACのPagingLibrary、皆さん使ってますか?

これからページングを実装するなら当然PagingLibrary使うでしょ!っていう雰囲気があると思うんですが、使おうと思うと

DataSource,DataSourceFactory,PagedList,PagedListAdapter,BoundaryCallback

となかなかに登場人物も多いので、ただ単にページングしたいだけなら自力で実装する方が単純っちゃあ単純ですよね。

と言うことで本当にPagingLibraryを使うべきなのか、なぜ使うのかを今更感はありますが調べてみました。

調べてて思ったんですが、Paging、他のAACと比べて言及されてる記事が少ない上に名前が一般的過ぎて情報集め辛くないですか…?

まあそれはさておき、幸いなことにCodelabがめちゃくちゃ良かったので、そちらをベースに進めていきます。

公式:Codelab

本記事ではCodelabの通り、APIからデータを取得しキャッシュとしてDB(Room)に保存する前提で話をしますのでよろしくお願いします。この前提は大事です。

実装方法を知りたいんじゃ!と言う方は直接CodeLabをやるのが一番いいのですが、英語でとっつきにくいし途中のコードがないのも寂しかったので、私がフォークして進めて要点だけ適当に和訳したリポジトリを上げておきましたので、もし良かったらみてみてください。

非公式:codelabやってみたやつ

PagingLibraryとは?

Android Architecture Componentsの一つで、その名の通りページングを実現するためのライブラリです。

ページングを比較的簡単にいい感じに実装することが可能です。どのようにいい感じかはこの記事で説明します。

なぜPagingLibraryが必要か

さて、いままでは自力で実装されていたページング、わざわざその名を冠した専用のライブラリが作られ、使われるようになったのには当然理由がありそうです。

Codelabの「Githubリポジトリを検索してリスト表示するアプリ」を参考に、使わない実装の問題点と使うことでどう解決されるかを見ていきましょう。

時間がない人用に結論だけ言うと下記三つの問題点が解決されます。

  • 自力で追加ロードの実装をしなければならない。
  • データベースから全てのデータが一度にロードされてしまう(表示する分だけロードしたい)
  • データベースから読み込んだ全てのデータのリストがメモリにのってしまう。

1個目はまあ当然なので大事なのは下二つです。

詳しく見ていきましょう。

PagingLibraryを使わない実装

CodelabのmasterブランチがPagingLibraryを使わない実装になっています。

APIとDB(Room)を併用しているので少し複雑ですが、処理の流れをざっくり説明すると、

検索した場合

検索実行

DBからデータを 全て 引いてきてリスト表示

同時にAPIリクエスト

返ってきたらDBにinsert

Roomを使っているのでDBに更新があれば自動でDBからデータが 全て 引かれて来てリスト表示

スクロールした場合

スクロール

リスナーで検知

持っているデータの末端まで行ったらAPIリクエスト
(末端まで行ったかどうかは自力でチェック)

返ってきたらDBにinsert

Roomを使っているのでDBに更新があれば自動でDBからデータが 全て 引かれて来てリスト表示

となっています。

Roomを使っていることにより、DBへ更新が来るのと同時にLiveDataに通知が来てリストが自動で更新されるのですが、Daoに記述されているデータ取得のクエリは以下ですから

当然、DBに該当のデータが10000件あれば10000件取って来ることになります。

そして、その結果はRepoのListに変換されLiveDataにセットされるので、メモリに10000件のデータを持つことになります。

これが前述した

  • データベースから全てのデータが一度にロードされてしまう。
  • データベースから読み込んだ全てのデータのリストがメモリにのってしまう。

ですね。

自力で解決?

自力で解決するにはどうすればいいんだろうかと考えてみたんですが、私にはRoomの仕組みにのったまま解決できる気がしませんでした。

とにかくしんどそう。

もしできる方がいれば是非教えていただけると嬉しいです。

ということでPagingの登場です。

Pagingを使った実装

CodelabのsolutionブランチがPagingを使った実装となっています。

具体的な使い方や詳細な説明はCodelabに書いてあるので割愛しますが、データ取得のメソッドの返り値がLiveDataからDataSource.Factoryに変わり、LivePagedListBuilderなどを使ってごにょごにょすることで、最終的にViewがObserveするLiveDataのもつListがPagedListに変わります。

これらの変更を加えると、必要な分しかDBから読み込まずメモリにものらないよううまいことやってくれるわけです。

かしこい!

試しにActivityに流れて来るPagedListの中身を見て見ました。

PagedListの中身を見てみた

このように、Sizeは全データ分ですが、中身は一部分のみしか入っておらず、必要のないところはnullになっているようです。

一度にどれくらい読み込むか、どれくらいPagedListに持たせておくかなどはLivePagedListBuilderにPagedList.Configを渡すことで色々と指定できそうです。(どの値がなんなのかまだ理解しきれていませんが)

あとページングのライブラリなので当然っちゃ当然なのですが自力での追加読み込みの実装をしなくて済みます。

Activityでこんな感じリスナーをセットしてviewModelで閾値チェック&リクエストしてたのをまるっと消せます。

その代わりにBoundaryCallbackを継承したクラスを作り中に、DBが空だった時の動作と、最後までスクロールした時の動作を書いてやれば良いわけです。

シンプルですね。

ちなみに、PagingLibraryはRoomを使わず、APIからのデータのみでも実装可能です。

が、その場合表示されているアイテムのデータを一つだけ更新するようなことができないうえに、おそらくDBで持っていたキャッシュをどうしてもメモリに持つことになりそうですよね。

ちょっと不便な上にPagingLibraryの恩恵を受けきれないので、基本的にはRoomを使う形で考えるのが良さそうです。

まとめ

と言うことで、AndroidArchitectureComponentのPagingLibralyは自力でDB(Room)+APIを利用したページングを実装した時に起こる

  • 自力で追加ロードの実装をしなければならない。
  • データベースから全てのデータが一度にロードされてしまう。
  • データベースから読み込んだ全てのデータのリストがメモリにのってしまう。

という問題を簡単に解決するぞ!という気持ちで使うと良さそうでした。

内部の仕組みはまだまだ雰囲気しかわかっていませんが、少なくともなんのために使うのかがわかってよかったです。

実装方法に関してはCodeLabを進めればわかるので是非みなさんやって見てください。

次はListAdapterについて書きたいと思っています。

ではまた。

自己紹介
Android開発エンジニアです。
kotlinがお気に入り。

 

おすすめ

Udemyのビデオ講座は本当に捗るので学習にとてもおすすめです。まじで。


スポンサーリンク

コメントをどうぞ

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください