中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Paging庫怎么在Android 中使用

發布時間:2021-03-26 17:12:27 來源:億速云 閱讀:173 作者:Leah 欄目:移動開發

這篇文章將為大家詳細講解有關Paging庫怎么在Android 中使用,文章內容質量較高,因此小編分享給大家做個參考,希望大家閱讀完這篇文章后對相關知識有一定的了解。

添加分頁依賴

按照如下代碼添加依賴:

dependencies {
  def paging_version = "1.0.0"

  implementation "android.arch.paging:runtime:$paging_version"

  // alternatively - without Android dependencies for testing
  testImplementation "android.arch.paging:common:$paging_version"

  // optional - RxJava support, currently in release candidate
  implementation "android.arch.paging:rxjava2:1.0.0-rc1"
}

備注: 分頁包幫助開發者在UI的列表容器中順暢地展示數據, 而不管是使用設備內部的數據庫還是從應用后端拉取數據.

庫架構

分頁庫的核心構件是PagedList類, 它是一個集合, 用于異步加載應用數據塊或者數據頁. 該類在應用的其它架構之間充當中介.

Data

每一個PagedList實例從DataSource中加載最新的應用數據. 數據從應用后端或者數據庫流入PagedList對象. 分頁包支持多樣的應用架構, 包括脫機數據庫和與后臺服務器通訊的數據庫.

UI

PagedList類通過PagedListAdapter加載數據項到RecyclerView里面. 在加載數據的時候, 這些類協同工作, 拉取數據并展示內容, 包括預取看不見的內容并在內容改變時加載動畫.

支持不同的數據架構

分頁包支持應用架構, 包括應用拉取數據的地方是從后臺服務器, 還是本機數據庫, 還是兩者的結合.

只有網絡

要展示后臺數據, 需要使用Retrofit的同步版本, 加載信息到自定義的DataSource對象中.
備注: 分頁包的DataSource對象并沒有提供任何錯誤處理機制, 因為不同的應用需要用不同的方式處理和展示UI錯誤. 如果錯誤發生了, 順從結果的回調, 然后稍后重試.

只有數據庫

要設置RecyclerView觀測本地存儲, 偏向于使用Room持久化庫. 用這種方式, 無論任何時候數據庫數據插入或者修改, 這些改變會自動地在負責展示這些數據的RecyclerView展示出來.

網絡+數據庫

在開始觀測數據庫之后, 你能夠通過使用PagedList.BoundaryCallback來監聽數據庫什么時候過期. 之后, 你可能從網絡拉取更多的數據, 并把它們插入到數據庫中. 如果UI正在展示數據庫, 以上就是你所需要做的全部.

下面的代碼片斷展示了BoundaryCallback的使用實例:

class ConcertViewModel {
  fun search(query: String): ConcertSearchResult {
    val boundaryCallback =
        ConcertBoundaryCallback(query, myService, myCache)
    // Error-handling not shown in this snippet.
    val networkErrors = boundaryCallback.networkErrors
  }
}

class ConcertBoundaryCallback(
    private val query: String,
    private val service: MyService,
    private val cache: MyLocalCache
) : PagedList.BoundaryCallback<Concert>() {
  override fun onZeroItemsLoaded() {
    requestAndSaveData(query)
  }

  override fun onItemAtEndLoaded(itemAtEnd: Concert) {
    requestAndSaveData(query)
  }
}

處理網絡錯誤

在使用網絡拉取或者分頁的數據, 而這些數據正在使用分頁包展示的時候, 不總是把網絡分為要么"可用"要么"不可能"是很重要的, 因為許多連接是間歇性或者成片的:

  • 特定的服務器可能不能響應網絡請求;

  • 設備可能聯接了慢的或者弱的網絡;

應用應該檢查每一個請求是否成功, 并且在網絡不可用的情形下, 盡可能快地恢復. 比如, 你可以為用戶提供一個"重試"按鈕, 如果數據沒有刷新成功的話. 如果在數據分頁期間發生錯誤, 最好自動地重新分頁請求.

更新已有應用

如果應用已經從網絡或者數據庫消費數據, 很大可能可以直接升級到分頁庫提供的功能.

自定義分頁解決方案

如果你使用了自定義功能加載數據源中的小的數據集, 你可以使用PagedList類取代這個邏輯. PagedList類實例提供了內建的連接, 到通用的數據源. 這些實例也提供了在應用中引用的RecyclerView的適配器.

使用列表而非分頁加載的數據

如果你使用內存里的列表作為UI適配器的后備數據結構, 考慮使用PagedList類觀測數據更新, 如果列表中數據項變得很多的話. PagedList實例既可以使用LiveData<PagedList>也可以使用Observable<List>對UI傳遞數據更新, 同時最小化了加載時間和內存使用. 然而, 應用中使用PagedList對象代替List并不要求對UI結構和數據更新邏輯作任何改變.

使用CursorAdapter將數據cursor與列表視圖聯系起來

應用也許會使用CursorAdapter將數據從Cursor跟ListView連接起來. 在這種情況下, 通常需要從ListView遷移到RecyclerView, 然后使用Room或者PositionalDataSource構件代替Cursor, 當然, 這主要依據于Cursor實例能否訪問SQLite數據庫.

在一些情況下, 比如使用Spinner實例的時候, 你僅僅提供了Adapter本身. 然后一個庫使用了加載進adapter中的數據, 并展示了數據. 在這些情況下, 把adapter數據類型轉化為LiveData<PagedList>, 之后在嘗試使用將這些數據項在UI中填充起來之前, 將這個列表在ArrayAdapter對象中包裹起來.

使用AsyncListUtil異步加載內容

如果你在使用AsyncListUtil對象異步地加載和展示分組信息的話, 分頁包將會使得加載數據更加方便:

  • 數據并不需要定位. 分頁包讓你直接從后臺使用網絡提供的鍵加載數據.

  • 數據量太大. 使用分頁包可以將數據加載分頁直到沒有任何數據留下.

  • 更方便地觀測數據. 分頁包能夠展示應用在可觀測數據結構中持有的ViewModel.

 數據庫例子

 使用LiveData觀測分頁數據

下面的示例代碼展示了所有一起工作的碎片. 當演唱會事件在數據庫中添加, 刪除或者修改的修改的時候, RecyclerView中的內容自動且高效地更新:

@Dao
interface ConcertDao {
  // The Integer type parameter tells Room to use a PositionalDataSource
  // object, with position-based loading under the hood.
  @Query("SELECT * FROM user ORDER BY concert DESC")
  fun concertsByDate(): DataSource.Factory<Int, Concert>
}

class MyViewModel(concertDao: ConcertDao) : ViewModel() {
  val concertList: LiveData<PagedList<Concert>> = LivePagedListBuilder(
      concertDao.concertsByDate(),
      /* page size */ 20
  ).build()
}

class MyActivity : AppCompatActivity() {
  public override fun onCreate(savedState: Bundle?) {
    super.onCreate(savedState)
    val viewModel = ViewModelProviders.of(this)
        .get(MyViewModel::class.java!!)
    val recyclerView = findViewById(R.id.concert_list)
    val adapter = ConcertAdapter()
    viewModel.concertList.observe(this, { pagedList ->
        adapter.submitList(pagedList) })
    recyclerView.setAdapter(adapter)
  }
}

class ConcertAdapter() :
    PagedListAdapter<Concert, ConcertViewHolder>(DIFF_CALLBACK) {
  fun onBindViewHolder(holder: ConcertViewHolder, position: Int) {
    val concert = getItem(position)
    if (concert != null) {
      holder.bindTo(concert)
    } else {
      // Null defines a placeholder item - PagedListAdapter automatically
      // invalidates this row when the actual object is loaded from the
      // database.
      holder.clear()
    }
  }

  companion object {
    private val DIFF_CALLBACK = object : DiffUtil.ItemCallback<Concert>() {
      // Concert details may have changed if reloaded from the database,
      // but ID is fixed.
      override fun areItemsTheSame(oldConcert: Concert,
          newConcert: Concert): Boolean =
          oldConcert.id == newConcert.id

      override fun areContentsTheSame(oldConcert: Concert,
          newConcert: Concert): Boolean =
          oldConcert == newConcert
    }
  }
}

使用RxJava2觀測分頁數據

如果你偏愛使用RxJava2而非LiveData, 那么你可以創建Observable或者Flowable對象:

 class MyViewModel(concertDao: ConcertDao) : ViewModel() {
   val concertList: Flowable<PagedList<Concert>> = RxPagedListBuilder(
       concertDao.concertsByDate(),
       /* page size */ 50
   ).buildFlowable(BackpressureStrategy.LATEST)
 }

之后你可以按照如下代碼開始和停止觀測數據:

class MyActivity : AppCompatActivity() {
  private lateinit var adapter: ConcertAdapter<Concert>
  private lateinit var viewModel: MyViewModel

  private val disposable = CompositeDisposable()

  public override fun onCreate(savedState: Bundle?) {
    super.onCreate(savedState)
    val recyclerView = findViewById(R.id.concert_list)
    viewModel = ViewModelProviders.of(this).get(MyViewModel::class.java!!)
    adapter = ConcertAdapter()
    recyclerView.setAdapter(adapter)
  }

  override fun onStart() {
    super.onStart()
    disposable.add(viewModel.concertList.subscribe({
        flowableList -> adapter.submitList(flowableList)
    }))
  }

  override fun onStop() {
    super.onStop()
    disposable.clear()
  }
}

基于RxJava2解決方案的ConcertDao和ConcertAdapter代碼, 和基于LiveData解決方案的代碼是一樣的.

UI構件及其出發點

將UI和視圖模型聯接起來 

你可以按照如下方式, 將LiveData<PagedList>實例跟PagedListAdapter聯系起來:

private val adapter = ConcertPagedListAdapter()
private lateinit var viewModel: ConcertViewModel

override fun onCreate(savedInstanceState: Bundle?) {
  viewModel = ViewModelProviders.of(this)
      .get(ConcertViewModel::class.java)
  viewModel.concerts.observe(this, adapter::submitList)
}

當數據源提供一個新PagedList實例的時候, activity會將這些對象改善給adapter. PagedListAdapter實現, 定義了更新如何計算, 自動地處理分頁和列表不同. 由此, 你的ViewHolder只需要綁定到特定的提供項:

class ConcertPagedListAdapter() : PagedListAdapter<Concert, ConcertViewHolder>(
    object : DiffUtil.ItemCallback<Concert>() {
  // The ID property identifies when items are the same.
  override fun areItemsTheSame(oldItem: Concert, newItem: Concert)
      = oldItem.id = newItem.id

  // Use the "==" operator (or Object.equals() in Java-based code) to know
  // when an item's content changes. Implement equals(), or write custom
  // data comparison logic here.
  override fun areContentsTheSame(oldItem: Concert, newItem: Concert) =
      oldItem.name == newItem.name && oldItem.date == newItem.date
  }
) {
  override fun onBindViewHolder(holder: ConcertViewHolder, position: Int) {
    val concert: Concert? = getItem(position)

    // Note that "concert" is a placeholder if it's null
    holder.bind(concert)
  }
}

PagedListAdapter使用PagedList.Callback對象處理分頁加載事件. 當用戶滑動時, PagedListAdapter調用PagedList.loadAround()方法將從DataSource中拉聚攏數據項提示提供給基本的PagedList.
備注: PageList是內容不可變的. 這意味著, 盡管新內容能夠被加載到PagedList實例中, 但已加載項一旦加載完成便不能發生改變. 由此, 如果PagedList中的內容發生改變, PagedListAdapter對象將會接收到一個包含已更新信息的全新的PagedList.

實現diffing回調

先前的代碼展示了areContentsTheSame()的手動實現, 它比較了對象的相關的域. 你也可以使用Java中的Object.equals()方法或者Kotlin中的==操作符. 但是要確保要么實現了對象中的equals()方法或者使用了kotlin中的數據對象.

使用不同的adapter類型進行diffing

如果你選擇不從PagedListAdapter繼承--比如你在使用一個提供了自己的adapter的庫的時候--你依然可以通過直接使用AsyncPagedListDiffer對象使用分頁包adapter的diffing功能.

在UI中提供占位符

在應用完成拉取數據之前, 如果你想UI展示一個列表, 你可以向用戶展示占位符列表項. RecyclerView通過將列表項臨時地設置為null來處理這個情況.

備注: 默認情況下, 分頁包開啟了占位符行為.

占位符有如下好處:

  • 支持scrollbar. PagedList向PagedListAdapter提供了大量的列表項. 這個信息允許adapter繪制一個表示列表已滿的scrollbar. 當新的頁加載時, scrollbar并不會跳動, 因為列表是并不沒有改變它的size.

  • 不需要"正在加載"旋轉指針. 因為列表大小已知, 沒必要提醒用戶有更多的數據項正在加載. 占位符本身表達了這個信息.

在添加占位符的支持之前, 請牢記以下先置條件:

  • 要求集合中數據可數. 來自Room持久化庫的DataSource實例能夠高效地計算數據項. 然而, 如果你在用自定義本地存儲方案或者只有網絡的數據架構, 想了解數據集中有多少數據項可能代價很高, 甚至不可能.

  • 要求adapter負責未加載數據項. 你正在使用的adapter或者展示機制來準備填充列表, 需要處理null列表項. 比如, 當將數據綁定到ViewHolder的時候, 你需要提供默認值表示未加載數據.

  • 要求數據相同數量的item view. 如果列表項數目能夠基于內容發生改變, 比如, 社交網絡更新, 交叉淡入淡出看起來并不好. 在這種情況下, 強烈推薦禁掉占位符.

數據構件及其出發點

構建可觀測列表

通常情況下, UI代碼觀測LiveData<PagedList>對象(或者, 如果你在使用RxJava2, 是Flowable<PagedList>/Observable<PagedList>對象), 這個對象存在于應用的ViewModel中. 這個可觀測對象形成了應用列表數據內容和展示的連接.

要創建這么一個可觀測PagedList對象, 需要將DataSource.Factory實例傳給LivePageListBuilder/RxPagedListBuilder對象. 一個DataSource對象對單個PagedList加載分頁. 這個工廠類為內容更新創建PagedList實例, 比如數據庫表驗證, 網絡刷新等. Room持久化庫能夠提供DataSource.Factory, 或者自定義.

如下代碼展示了如何在應用的ViewModel類中使用Room的DataSource.Factory構建能力創建新的LiveData<PagedaList>實例:

ConcertDao.kt:

interface ConcertDao {
   // The Integer type parameter tells Room to use a PositionalDataSource
   // object, with position-based loading under the hood.
   @Query("SELECT * FROM concerts ORDER BY date DESC")
   public abstract DataSource.Factory<Integer, Concert> concertsByDate()
 }

ConcertViewModel.kt:

// The Integer type argument corresponds to a PositionalDataSource object.
val myConcertDataSource : DataSource.Factory<Integer, Concert> =
    concertDao.concertsByDate()

val myPagedList = LivePagedListBuilder(myConcertDataSource, /* page size */ 20)
    .build()

定義分頁配置

要想為復雜情形更深入地配置LiveData<PagedList>, 你也可以定義自己的分頁配置. 尤其是, 你可以定義如下屬性:

  • 頁大小: 每一頁的數據量.

  • 預取距離: 給定UI中最后可見項, 超過該項之后多少項, 分頁包要嘗試提前提取數據. 這個值應該比page size大幾倍.

  • 占位符展示: 決定了UI是否會為還沒有完成加載的數據項展示占位符.

如果你想要對分布包從數據庫加載中設置更多的控件, 要像下面的代碼一樣, 傳遞自定義的Executor對象給LivePagedListBuilder:

EventViewModel.kt:

val myPagingConfig = PagedList.Config.Builder()
    .setPageSize(50)
    .setPrefetchDistance(150)
    .setEnablePlaceholders(true)
    .build()

// The Integer type argument corresponds to a PositionalDataSource object.
val myConcertDataSource : DataSource.Factory<Integer, Concert> =
    concertDao.concertsByDate()

val myPagedList = LivePagedListBuilder(myConcertDataSource, myPagingConfig)
    .setFetchExecutor(myExecutor)
    .build()

選擇正確的數據源類型

連接更最好地處理源數據結構的數據源很重要:

  • 如果加載的頁嵌套了之前/之后頁的key的話, 使用PageKeyDataSource. 比如, 比如你正在從網絡中拉取社交媒體博客, 你也許需要傳遞從一次加載向下一次加載的nextPage token.

  • 如果需要使用每N項數據項的數據拉取每N+1項的話, 使用ItemKeyedDataSource. 比如, 你在為一個討論型應用拉取螺紋評論, 你可能需要傳遞最后一條評論的ID來獲取下一條評論的內容.

  • 如果你需要從數據商店中的任意位置拉取分頁數據的話, 使用PositionalDataSource. 這個類支持請求任意位置開始的數據集. 比如, 請求也許返回從位置1200開始的20條數據.

通知數據非法

在使用分頁包時, 在表或者行數據變得陳腐時, 取決于數據層來通知應用的其它層. 要想這么做的話, 需要從DataSource類中調用invalidate()方法.

備注: UI也可以使用"滑動刷新"模式來觸發數據非法功能.

構建自己的數據源

如果你使用了自定義的數據解決方案, 或者直接從網絡加載數據, 你可以實現一個DataSource子類. 下面的代碼展示了數據源從給定的concert起始時間切斷:

class ConcertTimeDataSource(private val concertStartTime: Date) :
    ItemKeyedDataSource<Date, Concert>() {
  override fun getKey(item: Concert) = item.startTime

  override fun loadInitial(
      params: LoadInitialParams<Date>,
      callback: LoadInitialCallback<Concert>) {
    val items = fetchItems(concertStartTime, params.requestedLoadSize)
    callback.onResult(items)
  }

  override fun loadAfter(
      params: LoadParams<Date>,
      callback: LoadCallback<Concert>) {
    val items = fetchItemsAfter(
      date = params.key,
      limit = params.requestedLoadSize)
    callback.onResult(items)
  }
}

通過創建真實的DataSource.Factory子類, 你之后能夠加載自定義的數據到PagedList對象. 下面的代碼展示了如何創建在之前代碼中定義的自定義數據源:

class ConcertTimeDataSourceFactory(private val concertStartTime: Date) :
    DataSource.Factory<Date, Concert>() {
  val sourceLiveData = MutableLiveData<ConcertTimeDataSource>()
  override fun create(): DataSource<Date, Concert> {
    val source = ConcertTimeDataSource(concertStartTime)
    sourceLiveData.postValue(source)
    return source
  }
}

考慮內容更新

當你構建可觀測PagedList對象的時候, 考慮一下內容是如何更新的. 如果你直接從Room數據庫中加載數據, 更新會自動地推送到UI上面.

如果你在使用分頁的網絡API, 通常你會有用戶交互, 比如"滑動刷新", 把它作為信號去驗證當前DataSource非法并請求一個新的. 這個行為出行在下面的代碼中:

class ConcertActivity : AppCompatActivity() {
  override fun onCreate(savedInstanceState: Bundle?) {
    ...

    concertViewModel.refreshState.observe(this, Observer {
      swipeRefreshLayout.isRefreshing =
          it == NetworkState.LOADING
    })
    swipeRefreshLayout.setOnRefreshListener {
      concertViewModel.invalidateDataSource()
    }
  }
}

提供數據表現之間的映射

對于DataSource加載的數據, 分頁包支持基于數據項和基于頁的轉換.

下面的代碼中, concert名和日期的聯合被映射成包含姓名和日期的字符串:

class ConcertViewModel : ViewModel() {
  val concertDescriptions : LiveData<PagedList<String>>
    init {
      val factory = database.allConcertsFactory()
          .map { concert ->
              concert.name + " - " + concert.date
          }
      concerts = LivePagedListBuilder(factory, 30).build()
    }
  }
}

如果在數據加載之后, 想要包裹, 轉換或者準備item, 這將非常有用. 因為這個工作是在獲取執行器中完成的, 你可以在其中執行花銷巨大的工作, 比如, 從硬盤中讀取, 查詢數據庫等.

關于Paging庫怎么在Android 中使用就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

南召县| 信阳市| 都江堰市| 任丘市| 清原| 沐川县| 三门峡市| 克东县| 武隆县| 金华市| 玉树县| 上饶市| 霍城县| 尚义县| 浏阳市| 雅安市| 大石桥市| 梓潼县| 平昌县| 兴海县| 丰原市| 广平县| 东光县| 沙雅县| 永德县| 瑞丽市| 武宣县| 济宁市| 桃源县| 林甸县| 石渠县| 中方县| 呼和浩特市| 门头沟区| 卓尼县| 松滋市| 安泽县| 望谟县| 舟山市| 水城县| 叶城县|