KunMinX / MVI-Dispatcher-KTX

简明易用框架,解决 MVI 实战痛点
Apache License 2.0
158 stars 12 forks source link

集成Paging3时,paging3 不生效,无法进行网络请求操作 #4

Closed KXwonderful closed 1 year ago

KXwonderful commented 1 year ago

在项目中集成 Paging 3,但用在这个框架里发现并不能触发请求操作,参考代码如下:

// interface apiService
@GET("/xxx/xxx")
fun getList(@Query("page") page: Int,@Query("num") num: Int): Call<List<TestBean>>

// PagingSource
class TestPagingSource(private val apiService: ApiService) : PagingSource<Int, TestBean>() {
    override fun getRefreshKey(state: PagingState<Int, FaultLogBean>): Int? = null

    override suspend fun load(params: LoadParams<Int>): LoadResult<Int, TestBean> {
        return try {
            LogUtils.e("paging load")  // 2. 并没有走这里的方法
            val page = params.key ?: 1
            val pageSize = params.loadSize
            val testList= apiService.getList(page, pageSize).await()
            val prevKey = if (page > 1) page - 1 else null
            val nextKey = if (testList.isNotEmpty()) page + 1 else null
            LoadResult.Page(testList, prevKey, nextKey)
        } catch (e: Exception) {
            LoadResult.Error(e)
        }
    }
}

// TestRepository
val pageSize = 10
fun getTestList(): Flow<PagingData<TestBean>> {
    return Pager(
        config = PagingConfig(pageSize),
        pagingSourceFactory = { TestPagingSource(apiService) }
    ).flow
}

// intent or event
sealed class TestEvent {
    data class GetTestList(val pagingData: Flow<PagingData<TestBean>>? = null) : TestEvent()
}

//  dispatcher 
class TestRequester : MviDispatcherKTX<TestEvent>() {
    ...
    is TestEvent.GetTestList -> sendResult(event.copy(testRepo.getTestList()))
}

// ui
class TestFragment {
    override fun onInput() {
        testRequest.input(TestEvent.GetTestList()) // 1. 触发请求
    }

    override fun onOutput() {
        // 3. 这里收不到消息
        testRequest.output(this) { testEvent ->
            // ...
        }
    }
}

如果用传统的 ViewModel 测试是没问题,可以触发,参考代码如下:

// viewModel
@HiltViewModel
class TestViewModel @Inject constructor(
    private val testRepo: TestRepository,
) : ViewModel() {

    fun getTestList(): Flow<PagingData<TestBean>> {
        return testRepo.getTestList().cachedIn(viewModelScope)
    }
}

// ui
class TestFragment {
    override fun onInitView() {
        lifecycleScope.launch {
            testViewModel.getTestList().collect { adapter.submitData(it) }
        } // 这边是ok的
    }
}
KunMinX commented 1 year ago

// dispatcher class TestRequester : MviDispatcherKTX() { ... is TestEvent.GetTestList -> sendResult(event.copy(testRepo.getTestList())) }

可以看看此处的 testRepo.getTestList() 后边是否遗漏了 .cachedIn(viewModelScope) 所致,

另外 paging 有自己独立的 flow 作为返回,适合传统方式使用, 如果要走 Dispatcher 的 input output(这里面是 Dispatcher 自身的 shareFlow 及其 collect,而非 paging 的 flow 和 collect),建议是在 Dispatcher 中调用 paging flow 的 collect,再将 “普通的实体类对象” 作为结果 sendResult 回推。

(对此可参考 complexRequest 案例:is ComplexIntent.ResultTest2 -> timer(1000).collect { sendResult(event) }

KXwonderful commented 1 year ago

// dispatcher class TestRequester : MviDispatcherKTX() { ... is TestEvent.GetTestList -> sendResult(event.copy(testRepo.getTestList())) }

可以看看此处的 testRepo.getTestList() 后边是否遗漏了 .cachedIn(viewModelScope) 所致,

和这个没关系的。

主要原因还是在 viewModelScope 作用域中并不能成功消费 PagingData,只能在 Activity 或者 Fragment 中 collect 才会触发paging3中数据请求,因此在 Dispatcher 中也无法分发 PagingData。

(对此可参考 complexRequest 案例:is ComplexIntent.ResultTest2 -> timer(1000).collect { sendResult(event) }

这个案例用在 PagingData 的回推也是无效。

KunMinX commented 1 year ago

感谢反馈,暂时建议传统方式。后续如有破局的好招,欢迎随时补充。