如何在Android中使用Paging 3库

重点 (Top highlight)

Most apps displays a large list of data to the users, but at a particular time the user sees only a small chunk of data in your app, so fetching all the data from the network is not an efficient solution. The solution is to fetch small chunks of data at a time, and as soon as the user reach at the end of the list, then the app will load more data. This is called Paging.

大多数应用程序会向用户显示大量数据,但是在特定时间,用户只能看到应用程序中的一小部分数据,因此从网络中获取所有数据并不是一种有效的解决方案。 解决方案是一次获取一小块数据,并且一旦用户到达列表末尾,应用程序就会加载更多数据 。 这称为分页。

With the release of Android 11 beta, Google also updated the Paging architectural components library for android. Paging 3 greatly simplifies implementing paging in your android app.

随着Android 11 Beta的发布,Google还更新了Android的Paging建筑组件库。 分页3大大简化了在Android应用中实现分页的过程。

分页3.0的新增功能 (What’s New in Paging 3.0)

Paging 3.0 is significantly different from the earlier versions of Paging Library. Some of the new features of Paging 3.0 includes:

Paging 3.0与早期版本的Paging Library明显不同。 Paging 3.0的一些新功能包括:

  • Support for Kotlin coroutines, Flow, as well as LiveData and RxJava.

    支持Kotlin协程,Flow以及LiveData和RxJava。
  • Built in support for error handling, refresh and retry functionality.

    内置对错误处理,刷新和重试功能的支持。
  • Built in support for loading state headers, footers and list separators.

    内置支持加载状态页眉,页脚和列表分隔符。
  • In memory caching of data, ensures efficient use of system resources.

    在数据的内存缓存中,确保有效利用系统资源。
  • Prevents api request duplication.

    防止api请求重复。
  • Improvements to the repository layer, including cancellation support and a simplified data source interface.

    对存储库层的改进,包括取消支持和简化的数据源界面。

设置您的项目 (Setup Your Project)

To use the Paging 3.0 library, add it to your app level build.gradle file.

要使用Paging 3.0库,请将其添加到应用程序级别的build.gradle文件中。

dependencies {
def paging_version = "3.0.0-alpha03"
implementation "androidx.paging:paging-runtime:$paging_version"
}

If you want to use RxJava or LiveData, you need to also include:

如果要使用RxJavaLiveData ,还需要包括:

// optional - RxJava2 support
implementation "androidx.paging:paging-rxjava2:$paging_version"
// optional - Guava ListenableFuture support
implementation "androidx.paging:paging-guava:$paging_version"

创建数据源 (Create a Data Source)

Unlike the previous versions of Paging library, in Paging 3.0 we have to implement a PagingSource<Key, Value> to define a data source. The PagingSource takes two parameters a Key and a Value. The Key parameter is the identifier of the data to be loaded such as page number and the Value is the type of the data itself.

PagingSource<Key, Value>版本的Paging库不同,在Paging 3.0中,我们必须实现PagingSource<Key, Value>来定义数据源。 PagingSource具有两个参数KeyValueKey参数是要加载的数据的标识符,例如页码,而Value是数据本身的类型。

class MoviePagingSource(
    val movieApiService: MovieApiService,
) : PagingSource<Int, Movie>() {
  
  override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Movie> {
    try {
      // Start refresh at page 1 if undefined.
      val nextPage = params.key ?: 1
      val response = movieApiService.getPopularMovies(nextPage)
      
      return LoadResult.Page(
        data = response.movies,
        prevKey = if (nextPage == 1) null else nextPage - 1,
        nextKey = response.page + 1
      )
    } catch (e: Exception) {
        return LoadResult.Error(e)
    }
  }
}
  • Notice the overridden load() function is a suspend function so we can make api requests here to get data from a network or a room database easily.

    注意,重写的load()函数是一个suspend函数,因此我们可以在此处发出api请求,以轻松地从网络或会议室数据库获取数据。

  • The LoadParams object holds the information about the load operation such as key and page size.

    LoadParams对象保存有关加载操作的信息,例如key和页面大小。

  • If the api request is successful, then we will return the response data wrapped in a LoadResult.Page object along with the previous and next keys.

    如果api请求成功,则我们将返回包装在LoadResult.Page对象中的响应数据以及上一个和下一个键。

  • If the api request is unsuccessful then we will return the occurred exception wrapped in a LoadResult.Error object.

    如果api请求失败,那么我们将返回包装在LoadResult.Error对象中的发生的异常。

The figure below shows exactly how the load() function recieves keys from the previous load and provides the keys for the next load.

下图准确显示了load()函数如何从上一次加载中获取键并为下一次加载提供键。

Image for post
Official Docs of Paging 3 官方文档中的照片

在ViewModel中获取PagingData (Get the PagingData in ViewModel)

Now we will create an instance of Pager in our viewmodel to get a stream of data from the MoviePagingSource that we just created.

现在,我们将在视图模型中创建Pager实例,以从刚刚创建的MoviePagingSource中获取数据流。

val movies: Flow<PagingData<Movie>> = Pager(PagingConfig(pageSize = 20)) {
  MoviePagingSource(movieApiService)
}.flow
  .cachedIn(viewModelScope)
  • The Pager object calls the load() method from the MoviePagingSource object, providing it with the LoadParams object and receiving the LoadResult object in return.

    Pager对象从MoviePagingSource对象调用load()方法,为它提供LoadParams对象,并作为回报接收LoadResult对象。

  • We also have to provide configurations such as pageSize with the PagingConfig object.

    我们还必须通过PagingConfig对象提供诸如pageSizePagingConfig配置。

  • The cachedIn(viewModelScope) caches the data from the MoviePagingSource to survive the screen orientation changes.

    cachedIn(viewModelScope)缓存从数据MoviePagingSource生存的屏幕方向的变化。

在RecyclerView中显示数据 (Display data in RecyclerView)

First we have to create a RecyclerView adapter class which extends from the PagingDataAdapter. This is the same as a normal RecyclerView adapter. The PagingDataAdapter takes two parameters, the first one is the type of the data(which in our case is the Movie object), and the second one is a RecyclerView.ViewHolder.

首先,我们必须创建一个从PagingDataAdapter扩展的RecyclerView适配器类。 这与普通的RecyclerView适配器相同。 PagingDataAdapter具有两个参数,第一个是数据的类型(在我们的示例中是Movie对象),第二个是RecyclerView.ViewHolder

class MovieAdapter : 
    PagingDataAdapter<Movie, MovieAdapter.MovieViewHolder>(MovieComparator){


    override fun onBindViewHolder(holder: MovieViewHolder, position: Int) {
        holder.itemView.movie_title.text = getItem(position)!!.title
    }


    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MovieViewHolder {
        return MovieViewHolder(
            LayoutInflater.from(parent.context)
                .inflate(R.layout.movie_item, parent, false)
        )
    }


    class MovieViewHolder(view: View) : RecyclerView.ViewHolder(view)


    object MovieComparator : DiffUtil.ItemCallback<Movie>() {
        override fun areItemsTheSame(oldItem: Movie, newItem: Movie): Boolean {
            // Id is unique.
            return oldItem.id == newItem.id
        }


        override fun areContentsTheSame(oldItem: Movie, newItem: Movie): Boolean {
            return oldItem == newItem
        }
    }


}

Finally, in OnActivityCreated() of our fragment, we can collect the data from PagingData and then submit it into our MovieAdapter.

最后,在片段的OnActivityCreated()中,我们可以从PagingData收集数据,然后将其提交到MovieAdapter

movieAdapter = MovieAdapter()


movie_recyclerView.apply {
    layoutManager = LinearLayoutManager(context)
    setHasFixedSize(true)
    adapter = movieAdapter
}


lifecycleOwner.lifecycleScope.launch {
    viewModel.movies.collectLatest {
        movieAdapter.submitData(it)
    }
}

The RecyclerView list now displays data from the data source and automatically loads more data when we reach at the end of the list.

现在, RecyclerView列表显示来自数据源的数据,并在到达列表末尾时自动加载更多数据。

显示LoadState和错误消息 (Display LoadState and Error messages)

The Paging 3.0 library has support for displaying loading states and handling errors. The Paging library exposes the loading state for use in the UI through the LoadState object.

Paging 3.0库支持显示加载状态和处理错误。 分页库公开了加载状态,以通过LoadState对象在UI中使用。

  • If the LoadState is a LoadState.NotLoading object, then there is no active load operation and no errors.

    如果LoadStateLoadState.NotLoading对象,则没有活动的加载操作,也没有错误。

  • If the LoadState is a LoadState.Loading object, then there is an active load operation.

    如果LoadState是一个 LoadState .Loading对象,则有一个活动的加载操作。

  • If the LoadState is a LoadState.Error object, then there is an error.

    如果LoadStateLoadState.Error对象,则存在错误。

We can attach a addLoadStateListener() in our MovieAdapter to listen for LoadState updates.

我们可以附加一个addLoadStateListener()在我们的MovieAdapter监听LoadState更新。

movieAdapter.addLoadStateListener { loadState ->
                                   
    /**
        This code is taken from https://medium.com/@yash786agg/jetpack-paging-3-0-android-bae37a56b92d
    **/


    if (loadState.refresh is LoadState.Loading){
        progressBar.visibility = View.VISIBLE
    }
    else{
        progressBar.visibility = View.GONE


        // getting the error
        val error = when {
            loadState.prepend is LoadState.Error -> loadState.prepend as LoadState.Error
            loadState.append is LoadState.Error -> loadState.append as LoadState.Error
            loadState.refresh is LoadState.Error -> loadState.refresh as LoadState.Error
            else -> null
        }
        errorState?.let {
            Toast.makeText(this, it.error.message, Toast.LENGTH_LONG).show()
        }
    }
}

In the next article, we learn how to create header or footers to show the loading state in the RecyclerView itself, and then we will learn to create list separators.

在下一篇文章中,我们将学习如何创建页眉或页脚以在RecyclerView本身中显示加载状态,然后将学习创建列表分隔符。

结论 (Conclusion)

The Paging 3.0 architectural components library is a major update over the previous versions of paging library, and it is completely rewritten from the previous versions of Paging library. It has complete support for the Kotlin coroutines and other reactive streams such as RxJava and LiveData. It also has inbuilt error handling functionality and support for managing loading states which makes implementing paging in our app super easy.

Paging 3.0体系结构组件库是对早期版本的页面库的重大更新,并且已从先前版本的Paging库中完全重写。 它完全支持Kotlin coroutines和其他React流,例如RxJavaLiveData 。 它还具有内置的错误处理功能,并支持管理加载状态,这使得在我们的应用程序中实现分页非常容易。

翻译自: https://proandroiddev.com/how-to-use-the-paging-3-library-in-android-5d128bb5b1d8

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值