欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  科技

Android Jetpack Paging3分页库的使用(概述以及网络加载)

程序员文章站 2022-03-31 08:00:12
概述使用分页库的好处分页库包含以下功能:分页数据的内存中缓存。这可确保您的应用在处理页面数据时有效地使用系统资源。内置请求重复数据删除功能,确保您的应用有效地使用网络带宽和系统资源。可配置 RecyclerView 适配器,当用户滚动到已加载数据的末尾时会自动请求数据。对Kotlin协程和Flow以及LiveData的支持 。内置的错误处理支持,包括刷新和重试功能。架构设计分页库隶属于安卓推荐架构设计的一部分。该库的组件在应用程序的使用中,涉及到三层结构。The repositor...

概述

使用分页库的好处

分页库包含以下功能:

  • 分页数据的内存中缓存。这可确保您的应用在处理页面数据时有效地使用系统资源。
  • 内置请求重复数据删除功能,确保您的应用有效地使用网络带宽和系统资源。
  • 可配置 RecyclerView 适配器,当用户滚动到已加载数据的末尾时会自动请求数据。
  • 对Kotlin协程和Flow以及LiveData的支持 。
  • 内置的错误处理支持,包括刷新和重试功能。

架构设计

分页库隶属于安卓推荐架构设计的一部分。该库的组件在应用程序的使用中,涉及到三层结构。

  • The repository layer
  • The ViewModel layer
  • The UI layer

Android Jetpack Paging3分页库的使用(概述以及网络加载)

上图描述了在每个层上运行的分页库组件,以及它们如何协同工作以加载和显示分页数据。

储存库层

存储库层中的主要分页库组件为 PagingSource。每个 PagingSource对象都定义一个数据源以及如何从该源中检索数据。PagingSource对象可以从任何单个源,包括网络源和本地数据库加载数据。

您可能会使用的另一个分页库组件是 RemoteMediatorRemoteMediator对象用以处理分层数据源,例如有本地数据库缓存的网络数据源。

ViewModel层

Pager组件提供了一个公共API,用于构造PagingData对象(基于PagingSource,在响应流中公开的实例)和 PagingConfig配置对象。

ViewModel图层连接到UI的组件是 PagingDataPagingData 目的是用于分页数据的快照的容器。它查询一个 PagingSource对象并存储结果。

UI层

UI层中的主要分页库组件是 PagingDataAdapterRecyclerView 用于处理分页数据的 适配器。

或者,您可以使用随附的 AsyncPagingDataDiffer 组件来构建自己的自定义适配器

**注意:**如果您的应用程序使用Compose for UI,请使用 androidx.paging:paging-compose 工件将Paging与UI层集成。要了解更多信息,请参阅的API文档 collectAsLazyPagingItems()

加载并显示网络数据源的分页数据流

定义数据源

第一步是定义一个 PagingSource实现以识别数据源。该PagingSourceAPI类包括 load() 方法,它必须重写,以指示如何从相应的数据源中检索分页数据。

PagingSource直接使用该类可将Kotlin协程用于异步加载。

Paging库还提供了支持其他异步框架的类:

选择键和值类型

PagingSource<Key, Value>有两个类型参数:KeyValue。键定义用于加载数据的标识符,值是数据本身的类型。

例如,如果您通过传递Int页码给 Retrofit,从网络加载的User对象的分页数据。

应该选择Int作为Key类型,User作为Value类型。

定义PagingSource

以下示例实现了一个 PagingSource按页码加载分页数据。该Key类型是IntValue类型 User

class ExamplePagingSource(
    val backend: ExampleBackendService,
    val query: String
) : PagingSource<Int, User>() {
  override suspend fun load(
    params: LoadParams<Int>
  ): LoadResult<Int, User> {
    try {
      // Start refresh at page 1 if undefined.
      val nextPageNumber = params.key ?: 1
      val response = backend.searchUsers(query, nextPageNumber)
      return LoadResult.Page(
        data = response.users,
        prevKey = null, // Only paging forward.
        nextKey = response.nextPageNumber
      )
    } catch (e: Exception) {
      // Handle errors in this block and return LoadResult.Error if it is an
      // expected error (such as a network failure).
    }
  }
}

一个典型的PagingSource实现将其构造函数中提供的参数传递给该load()方法,以为查询加载适当的数据。

在上面的示例中,这些参数是:

  • backend:提供数据的后端服务的实例。
  • query:搜索查询以发送到表示的服务backend

LoadParams 对象包含有关要执行的加载操作的信息。这包括要加载的key和要加载的项目数。

LoadResult 对象包含加载操作的结果。LoadResult是一个密封类,采用两种形式之一,具体取决于load()调用是否成功:

  • 如果加载成功,则返回一个LoadResult.Page对象。

  • 如果加载不成功,则返回一个LoadResult.Error对象。

下图说明了load()此示例中的功能如何为每个加载接收key并为后续加载提供key。
Android Jetpack Paging3分页库的使用(概述以及网络加载)

处理错误

加载数据的请求可能由于多种原因而失败,尤其是在通过网络加载时。

通过LoadResult.Errorload()方法返回对象来报告加载过程中遇到的错误 。

例如,您可以ExamplePagingSource 通过将以下内容添加到load()方法中来捕获并报告上一示例中的加载错误:

catch (e: IOException) {
  // IOException for network failures.
  return LoadResult.Error(e)
} catch (e: HttpException) {
  // HttpException for any non-2xx HTTP status codes.
  return LoadResult.Error(e)
}

有关处理改造错误的更多信息,请参阅PagingSourceAPI参考中的示例 。

PagingSource收集LoadResult.Error对象并将其传递到UI,以便您可以对它们进行操作。有关在UI中公开加载状态的更多信息,请参阅显示加载状态

设置PagingData流

接下来,您需要实现中的分页数据流PagingSource

通常,您应该在ViewModel中设置数据流。 Pager类提供方法,可以获得来自PagingSource的响应流PagingData对象。

分页库支持使用多个流类型,包括FlowLiveDataFlowableObservable类型从RxJava。

创建Pager实例以设置响应流时,必须为该实例提供一个 PagingConfig配置对象和一个函数,该函数告知Pager如何获取实现的实例 PagingSource

val flow = Pager(
  // Configure how data is loaded by passing additional properties to
  // PagingConfig, such as prefetchDistance.
  PagingConfig(pageSize = 20)
) {
  ExamplePagingSource(backend, query)
}.flow
  .cachedIn(viewModelScope)

cachedIn()方法通过传入的CoroutineScope提供数据流,并缓存加载的数据。

Pager对象从PagingSource中调用load()方法,向其传入 LoadParams对象并接收LoadResult

定义一个RecyclerView适配器

您还需要设置适配器以将数据接收到RecyclerView 列表中。

分页库PagingDataAdapter为此提供了类。

定义一个可扩展的类PagingDataAdapter。在该示例中, UserAdapter继承PagingDataAdapter作为RecyclerView提供适配器, 列表类型UserUserViewHolder用作ViewHolder

class UserAdapter(diffCallback: DiffUtil.ItemCallback<User>) :
  PagingDataAdapter<User, UserViewHolder>(diffCallback) {
  override fun onCreateViewHolder(
    parent: ViewGroup,
    viewType: Int
  ): UserViewHolder {
    return UserViewHolder(parent)
  }

  override fun onBindViewHolder(holder: UserViewHolder, position: Int) {
    val item = getItem(position)
    // Note that item may be null. ViewHolder must support binding a
    // null item as a placeholder.
    holder.bind(item)
  }
}

您的适配器还必须定义onCreateViewHolder()onBindViewHolder()方法并指定 DiffUtil.ItemCallback:

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

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

在用户界面中显示分页数据

现在,您已经定义了一个PagingSource,为您的应用程序生成了数据流PagingData,并定义了一个PagingDataAdapter,您可以将这些元素连接在一起并在活动中显示分页数据了。

在activity onCreate或fragment的 onViewCreated方法执行以下步骤:

  1. 创建您的PagingDataAdapter类的实例。
  2. PagingDataAdapter实例传递到RecyclerView 要显示分页数据的 列表。
  3. 观察PagingData流,并将每个生成的值传递给适配器的submitData()方法。
val viewModel by viewModels<ExampleViewModel>()

val pagingAdapter = UserAdapter(UserComparator)
val recyclerView = findViewById<RecyclerView>(R.id.recycler_view)
recyclerView.adapter = pagingAdapter

// Activities can use lifecycleScope directly, but Fragments should instead use
// viewLifecycleOwner.lifecycleScope.
lifecycleScope.launch {
  viewModel.flow.collectLatest { pagingData ->
    pagingAdapter.submitData(pagingData)
  }
}

RecyclerView现在能够显示来自数据源的分页数据,并在必要时自动加载另一页。

显示加载状态

分页库公开了加载状态,以通过LoadState对象在UI中使用 。

LoadState 根据当前的加载状态,采用以下三种形式之一:

  • 如果没有活动的加载操作且没有错误,LoadState则为 LoadState.NotLoading对象。
  • 如果有活动的加载操作,LoadState则为 LoadState.Loading对象。
  • 如果有错误,则LoadState是一个LoadState.Error对象。

LoadState在UI中有两种使用方法:

  • 使用加载状态监听
  • 使用特殊的列表适配器。

使用监听器获取加载状态

要获取通常在UI中使用的加载状态,请 PagingDataAdapter 包含addLoadStateListener()方法。

// Activities can use lifecycleScope directly, but Fragments should instead use
// viewLifecycleOwner.lifecycleScope.
lifecycleScope.launch {
  pagingAdapter.loadStateFlow.collectLatest { loadStates ->
    progressBar.isVisible = loadStates.refresh is LoadState.Loading
    retry.isVisible = loadState.refresh !is LoadState.Loading
    errorMsg.isVisible = loadState.refresh is LoadState.Error
  }
}

使用适配器显示加载状态

分页库提供了另一个列表适配器 LoadStateAdapter,其目的是直接在显示的页面数据列表中显示加载状态。

首先,创建一个实现的类LoadStateAdapter,并定义 onCreateViewHolder()onBindViewHolder()方法:

class LoadStateViewHolder(
  parent: ViewGroup,
  retry: () -> Unit
) : RecyclerView.ViewHolder(
  LayoutInflater.from(parent.context)
    .inflate(R.layout.load_state_item, parent, false)
) {
  private val binding = LoadStateItemBinding.bind(itemView)
  private val progressBar: ProgressBar = binding.progressBar
  private val errorMsg: TextView = binding.errorMsg
  private val retry: Button = binding.retryButton
    .also {
      it.setOnClickListener { retry() }
    }

  fun bind(loadState: LoadState) {
    if (loadState is LoadState.Error) {
      errorMsg.text = loadState.error.localizedMessage
    }

    progressBar.isVisible = loadState is LoadState.Loading
    retry.isVisible = loadState is LoadState.Error
    errorMsg.isVisible = loadState is LoadState.Error
  }
}

// Adapter that displays a loading spinner when
// state = LoadState.Loading, and an error message and retry
// button when state is LoadState.Error.
class ExampleLoadStateAdapter(
  private val retry: () -> Unit
) : LoadStateAdapter<LoadStateViewHolder>() {

  override fun onCreateViewHolder(
    parent: ViewGroup,
    loadState: LoadState
  ) = LoadStateViewHolder(parent, retry)

  override fun onBindViewHolder(
    holder: LoadStateViewHolder,
    loadState: LoadState
  ) = holder.bind(loadState)
}

然后,withLoadStateHeaderAndFooter()从您的PagingDataAdapter对象中调用该方法 :

pagingAdapter
  .withLoadStateHeaderAndFooter(
    header = ExampleLoadStateAdapter(adapter::retry),
    footer = ExampleLoadStateAdapter(adapter::retry)
  )

本文地址:https://blog.csdn.net/u012591761/article/details/110197209