dely Tech Blog

クラシル・TRILLを運営するdely株式会社の開発ブログです

理想のページングを実装する 後編

こんにちは、クラシルAndroidエンジニアの@MeilCliです。前回、クラシル内のレシピ保存機能の開発に際してページングに関して考慮した理想のUXについての考え方について紹介しました

tech.dely.jp

今回はそれの後編にあたり、Android側の実際の実装に関して深ぼって紹介しようと思います

設計

前回の記事において、サーバー側は時刻ベースのCursorを用いたページングAPIの実装、クライアント側は要素の追加・移動などのユーザー操作を記録し、その差分反映をリストに対して行うという実装方針を紹介しました これをクラシルAndroidの設計に基づいた構造に落とし込んでいきます

クラシルAndroidの設計については先日別記事にて紹介したのでよかったらそちらも読んでいただければと思います

tech.dely.jp

保存する・保存解除する・保存状態を確認する

まず保存に関する基本的なCreate/Read/Deleteに関する操作ですが、クラシルAndroidの設計を図示すると画像のような感じになります
※保存は内部的にはBookmarkと呼ばれています

BookmarkRecipeUseCaseはおおよそ以下のようなinterfaceをしています

interface BookmarkRecipeUseCase {

    val bookmarkingRecipeIds: Flowable<Collection<String>>

    fun requestBookmarkStatus(targetRecipeIds: List<String>)

    fun bookmarkRecipe(recipeId: String)

    fun unBookmarkRecipe(recipeId: String)
}

保存する・保存解除するの動作でRestClientを呼び出し、結果に応じてDb/Cacheの値を更新する。その際にPublishProcessorに変動した保存状態を通知しUI Layerに反映する。保存状態の確認をする場合にはDb/Cacheの値を第一報として通知し、RestClientで正確な値を後から通知するという流れです

ユーザー操作を記録する

画像の通り、ユーザー操作を記録するのは簡単です。記録したい操作を行うUseCaseがイベントの記録を行うUseCaseに対してイベントを教えてあげればよいだけだからです

保存する・保存解除する・保存状態を確認するではDb/Cacheの2つのData Source、つまりSQLiteへの保存とインメモリーキャッシュを行っていました。ユーザー操作に関しては保存内容が多くなりそうなこと・インメモリーキャッシュを要求するほど速度を求めてないことからSQLiteへの保存のみを行うことにしました

またその際に保存している内容としては、ID・オブジェクトJSON文字列化したもの・イベントが起きた時刻という感じです

ユーザー操作を反映した最新のリストを構築する

リストを構築する上で、まずページングの基盤が必要です。ページングの基盤については以前の記事で紹介したのでよかったら見ていただければと思います

tech.dely.jp

基本方針としては、ページングの基盤から取ってきたリストにページングを開始した時刻以降のユーザー操作を反映させていくという感じです

画像にするとこのような感じですね

ページング基盤ではシンプルにSingle<List<T>>のような返り値なので、それに対してmapやflatMapを行い、ユーザー操作に応じてaddFirst, remove, moveFirstなどのオペレーションを加えるという形になります

そしてUI Layer側はOnStartやPullToRefreshなどのタイミングでUseCase側に最新のリストを要求するということになります。これにてめでたしめでたし、ということでは終わりません。この設計・実装ではリストを表示している他画面で行われたユーザー操作を反映することができますが、同じ画面内で行われたユーザー操作を反映することはできません。そのような場面になったら、BookmarkUseCaseがBookmarkEventUseCaseに対してイベントの購読を行い、イベントが起こったタイミングで最新のリストを計算し、それをUI Layerに通知するといった追加の処理が必要になります

終わりに

以上にてクラシルAndroidにおいてのユーザー操作を反映したリスト表示の設計と実装についての紹介を終わりにしたいと思います

クラシルAndroidで採用した手法は数ある手法の内の1つであります。古典的な方法で行くとEventBusのような仕組みもありえますし、ユーザー操作が行われた時点で画面の表示情報をリフレッシュするという手もあると思います。クラシルAndroidで採用した手法よりももっといい手法があればコメントなどで教えていただければと思います。また、詳細な設計や実装について気になればカジュアル面談でお話することもできると思うのでぜひお声がけいただければと思います

meety.net