簡體   English   中英

發出 Paging3 流后無法收集任何流

[英]Can not collect any flow after emitting Paging3 flow

我正在使用paging3 ,並且有兩個不同的分頁源。 問題是Coroutine Scope只發出第一個尋呼流

ViewModel我有兩個分頁流程

val pagingFlow1 = Pager(PagingConfig(pageSize = 50, prefetchDistance = 1)) {
    pagingSource
}.flow.cachedIn(viewModelScope)

val pagingFlow2 = Pager(PagingConfig(pageSize = 50, prefetchDistance = 1)) {
    pagingSource2
}.flow.cachedIn(viewModelScope)

在活動中收集它們

    lifecycleScope.launch(Dispatchers.IO) {
        viewModel.pagingFlow1.collectLatest { pagingData ->
            pagingAdapter.submitData(pagingData)
        }
        viewModel.pagingFlow2.collectLatest { pagingData ->
            pagingAdapter2.submitData(pagingData)
        }
    }

但是lifecycleScope只發出pagingFlow1換句話說分頁只在第一個 recyclerView 工作。

當我這次更改順序時僅適用於pagingFlow2

    lifecycleScope.launch(Dispatchers.IO) {
        viewModel.pagingFlow2.collectLatest { pagingData ->
            pagingAdapter.submitData(pagingData)
        }
        viewModel.pagingFlow1.collectLatest { pagingData ->
            pagingAdapter2.submitData(pagingData)
        }
    }

為了確保我用基本流程測試它並正常工作

// Flows in ViewModel
val testFlow1 = flowOf(1,2,3)
val testFlow2 = flowOf(4,5,6)

// Activity
    lifecycleScope.launch(Dispatchers.IO) {
        viewModel.testFlow1.collectLatest { item ->
            Log.d(item)
        }
        viewModel.testFlow2.collectLatest { item ->
            Log.d(item)
        }
    }

我不明白為什么在使用分頁時只發出第一個流? 有人給我線索嗎?

在嘗試不同的事情時,我發現了一些有趣的行為。 如果先收集 pagingFlow,我們將無法收集任何東西

    val flow3 = flowOf(1,2,3)
    lifecycleScope.launch(Dispatchers.IO) {
        flow3.collectLatest { pagingData ->
            LogUtils.d("PagingFlow3 $pagingData")
        }
        viewModel.pagingFlow1.collectLatest { pagingData ->
            LogUtils.d("PagingFlow1 $pagingData")
            pagingAdapter.submitData(pagingData)
        }
        viewModel.pagingFlow2.collectLatest { pagingData ->
            LogUtils.d("PagingFlow2 $pagingData")
            pagingAdapter2.submitData(pagingData)
        }
    }

第一個flow3收集比pagingFlow1收集但pagingFlow2收集

如果我們將 flow3 放在pagingFlow1下面,它就不會被收集

    val flow3 = flowOf(1,2,3)
    lifecycleScope.launch(Dispatchers.IO) {
        viewModel.pagingFlow1.collectLatest { pagingData ->
            LogUtils.d("PagingFlow1 $pagingData")
            pagingAdapter.submitData(pagingData)
        }
        flow3.collectLatest { pagingData ->
            LogUtils.d("PagingFlow3 $pagingData")
        }
        viewModel.pagingFlow2.collectLatest { pagingData ->
            LogUtils.d("PagingFlow2 $pagingData")
            pagingAdapter2.submitData(pagingData)
        }
    }

僅收集pagingFlow1

collectLatest 暫停直到流程完成,因此您需要啟動單獨的作業。

此外,您不需要在 IO 調度員上進行調度。

編輯:自發布此答案以來對 Paging 進行了一些更改 - 您調用.submitData的 Dispatcher 不再重要。 它唯一影響的可能是 init 的分配發生的位置,並且您可能想從非 ui 線程啟動這些分配,但通常它不會對性能產生影響。

例如,

lifecycleScope.launch {
    viewModel.pagingFlow1.collectLatest { pagingData -> 
        pagingAdapter.submitData(pagingData) } }

lifecycleScope.launch {
    viewModel.pagingFlow2.collectLatest { pagingData -> 
        pagingAdapter2.submitData(pagingData) } }
  1. 首先,您必須意識到,無論何時在代碼中使用流,您都在異步檢索值。 這意味着對您的活動的 the.collect 調用必須等到從流中發出一個值。 我假設這只會在您從您的案例中讀取數據庫時發生,如果您在 DAO 返回類型中使用流,則只有在某些 function 更改數據庫字段時才會觸發。 這意味着,在有人更改數據庫之前,不要 go 在代碼中傳遞這一點(換句話說,它會阻塞線程)。 對此的修復是同時運行 your.collect 並行而不是順序運行。 這在 dlam 的回答中得到了展示,他/她並行運行它們並且它們可以工作。

  2. 好的,他/她的回答有效,那我們為什么需要這篇文章呢? 嗯,雖然它適用於用戶,但所有這些操作都在主線程上運行,這基本上會凍結 UI 一定數量的幀,因為負責更新 UI 的線程 (Dispatchers.Main) 正忙於執行您的操作。 這可以在您的 logcat 上看到以下消息:

我/編舞:跳過了 xAmountOf 幀。 應用程序可能在其主線程上做了太多工作。

為避免凍結 UI,主線程應僅用於更新 UI。 解決方法是使用 Dispatchers.IO 在后台線程上運行這些操作

  1. 如果你到達#2,你可能會得到一個錯誤:

java.lang.IllegalArgumentException:已注冊具有給定密鑰的 SavedStateProvider

這是由於您在流程上調用 .cachedIn(ViewmodelScope) 造成的。 此方法緩存在您的 scope 上的分頁數據中,因此您可以在下次需要時更快地訪問它,並且當您傳入的 scope 終止時,它會被垃圾收集,該錯誤基本上表示您試圖保存第二組數據與已存儲的集合相同的密鑰。

  1. 我假設這些 SavedStateProvider 鍵可能是自動生成的。 這意味着 whichever.collect 首先被觸發(記住它們現在並行運行,所以它可以是任何一個)將使用自動生成的密鑰保存,為了簡單起見,可以說“1”很好,但是當 second.collect 得到觸發並保存其 SavedStateProvider 由於某種原因它還會自動生成“1”作為導致沖突的鍵。 找到解決此問題的關鍵是找出導致它們自動生成相同密鑰的原因。

  2. 我認為他們生成相同密鑰的原因是因為它們在並行線程上運行,第二個被觸發的密鑰在第一個完成保存之前生成密鑰,因此第二個不知道該密鑰已經存在被緩存。 在 dlam 的回答中沒有出現這個錯誤這一事實支持了這一點,為什么不呢? 最有可能與運行它們的主調度程序有關,而不是 IO 調度程序。

  3. 在這一點上我會說修復是為了確保密鑰只生成一次,但由於我們無權訪問代碼,我們在這方面不能做太多......我會說你最好的選擇是從您的流程中刪除 the.cachedIn 運算符,然后大概這會起作用。

     lifecycleScope.launch(Dispatchers.IO) { viewModel.pagingFlow1.collectLatest { pagingData -> LogUtils.d("PagingFlow1 $pagingData") pagingAdapter.submitData(pagingData) } } lifecycleScope.launch(Dispatchers.IO) { viewModel.pagingFlow2.collectLatest { pagingData -> LogUtils.d("PagingFlow2 $pagingData") pagingAdapter2.submitData(pagingData) } }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM