APIから取得したデータをDBに保存してキャッシュをする場合
Repositoryの実装
そして、APIから取得したデータを表示するだけの場合とPagerで指定する引数が異なります。
表示するデータを取得するpagingSourceFactoryにAnimeのPagingSourceを返却するDaoのメソッドを指定し、データが不足した際にloadが呼び出されるRemoteMediatorには実装したAnimeMediatorを指定します。
今回の要件では、Repositoryの引数と返却するPagerの引数は以下のようになります。
class AnimeRepository @Inject constructor(
private val animeService: AnimeService,
private val animeDao: AnimeDao,
private val pageKeyDao: PageKeyDao
) {
fun getAnime(): Pager<Int, Anime> {
val sessionId = UUID.randomUUID().toString()
return Pager(
config = PagingConfig(
pageSize = 20,
prefetchDistance = 80,
enablePlaceholders = false,
),
remoteMediator = AnimeMediator(
sessionId = sessionId,
animeService = animeService,
animeDao = animeDao,
pageKeyDao = pageKeyDao
),
pagingSourceFactory = {
animeDao.getAnimePagingSource(sessionId)
},
)
}
}
AdapterやFragmentの実装は、共通のものと同じですが、改めて記載しておきます。
共通の部分の説明でコードの解説をしているため、今回は記載だけに留めます。
ViewModelの実装
// Flowの場合
fun getAnimesAsFlow(): Flow<PagingData<Anime>> =
animeRepository.getAnimes().flow
// LiveDataの場合
fun getAnimesAsLiveData(): LiveData<PagingData<Anime>> =
animeRepository.getAnimes().liveData
RecyclerViewでリスト表示するレイアウトの実装
<layout xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="anime"
type="com.example.pagingsampleapp.data.vo.Anime" />
</data>
<com.google.android.material.card.MaterialCardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:layout_width="80dp"
android:layout_height="80dp"
app:loadImage="@{ anime.images.recommended_url }" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{ anime.title }"/>
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
</layout>
AnimeAdapterの実装
class AnimeAdapter : PagingDataAdapter<Anime, AnimeViewHolder>(
object : DiffUtil.ItemCallback<Anime>() {
override fun areItemsTheSame(oldItem: Anime, newItem: Anime): Boolean {
return oldItem.id == oldItem.id
}
override fun areContentsTheSame(oldItem: Anime, newItem: Anime): Boolean {
return oldItem == newItem
}
}
) {
override fun onBindViewHolder(holder: AnimeViewHolder, position: Int) {
val item = getItem(position)
holder.binding.anime = item
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AnimeViewHolder =
AnimeViewHolder(
DataBindingUtil.inflate(
LayoutInflater.from(parent.context),
R.layout.view_anime_title,
parent,
false
)
)
class AnimeViewHolder(val binding: ViewAnimeTitleBinding) : RecyclerView.ViewHolder(binding.root)
}
Fragmentの実装
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data></data>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/anime_recycler"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />
</layout>
val adapter = AnimeAdapter()
binding.animeRecycler.adapter = adapter
// Flowの場合
lifecycleScope.launch {
viewModel.getReposAsFlow().collectLatest {
adapter.submitData(it)
}
}
// LiveDataの場合
viewModel.getReposAsLiveData().observe(viewLifecycleOwner) {
lifecycleScope.launch {
adapter.submitData(it)
}
}
これで、APIから取得したデータをDBに保存してキャッシュをする場合の実装は以上になります。試しにアプリを実行してみましょう。
APIから取得したデータを表示するだけの際と同様にRecyclerViewでリスト表示ができています。そしてスクロールすることでページング処理を行えていることが確認できます。
都合上サンプルアプリと異なり上記の画面キャプチャではサムネイルは非表示にしています。
