【问题标题】:Paging library DataSource.Factory for multiple data sources用于多个数据源的分页库 DataSource.Factory
【发布时间】:2022-05-30 03:04:05
【问题描述】:

新的分页库允许我们指定用于数据分页的自定义数据源。 github 上的分页库文档和示例代码向我们展示了如何通过创建 DataSource.Factory 的子类来创建自定义数据源实例,如下所示:

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
    }
}

在一个真实的应用程序中,您通常会有多个带有 recyclerviews 的视图,因此会有多个自定义数据源。那么,您最终是为每个数据源创建多个 DataSource.Factory 实现,还是有更通用的解决方案?

【问题讨论】:

  • 我也在寻找这个问题的答案?你找到了吗?
  • 不,到目前为止,我们每个数据源都有一个 DataSourceFactory。
  • 我找到了解决方案。在这里查看stackoverflow.com/questions/54768784/…

标签: android android-architecture-components android-jetpack android-paging


【解决方案1】:

并非总是如此。

如果您正在使用提供良好支持的其他 Android 架构组件或库,在大多数情况下,DataSource.Factory 将作为方法调用的结果交付,例如 Room 数据库。

如果你真的想要一个非常通用的并且对反射没有问题:

class GenericFactory<K, R>(private val kClass: KClass<DataSource<K, R>>) : DataSource.Factory<K, R>() {
    override fun create(): DataSource<K, R> = kClass.java.newInstance()
}

您的示例显示了将 DataSource 公开为 LiveData 的 DataSource.Factory。这仅在特定情况下是必需的,例如,当 DataSource 持有 API 调用的重试方法时。在其他情况下,您的 DataSource.Factory 将像 DataSource 中的 3 行一样简单:

class MySimpleDataSource<R> : PageKeyedDataSource<String, R>() {

    override fun loadBefore(params: LoadParams<String>,
                            callback: LoadCallback<String, R>) {
        // do your thing
    }

    override fun loadAfter(params: LoadParams<String>,
                           callback: LoadCallback<String, R>) {
        // do your thing
    }

    override fun loadInitial(params: LoadInitialParams<String>,
                             callback: LoadInitialCallback<String, R>) {
        // do your thing
    }

    class Factory<R> : DataSource.Factory<String, R>() {
        override fun create(): DataSource<String, R> = MySimpleDataSource<R>()
    }
}

我猜自定义 DataSource.Factory 最常见的情况是分页的 REST API 调用。在这种情况下,您可以只实现一个通用 DataSource 和一个 DataSource.Factory,以 lambda 形式接收请求对象和响应回调。

data class MyCollection<R>(
        var items: List<R>,
        var nextPageToken: String
)

data class MyData(
        var title: String = ""
)

abstract class SomeLibraryPagedClientRequest<R> {
    abstract fun setNextPageToken(token: String?): SomeLibraryPagedClientRequest<R>
    abstract fun enqueue(callback: (response: Response<R>) -> Unit): Unit
}

class MyRestApiDataSource(
        private val request: SomeLibraryPagedClientRequest<MyData>,
        private val handleResponse: (Response<R>) -> Unit
) : ItemKeyedDataSource<String, MyData>() {

    var nextPageToken: String = ""

    override fun getKey(item: MyData): String = nextPageToken

    override fun loadBefore(params: LoadParams<String>, callback: LoadCallback<MyData>) {
    }

    override fun loadInitial(params: LoadInitialParams<String>, callback: LoadInitialCallback<MyData>) {
        request.setNextPageToken(params.requestedInitialKey).enqueue { data ->
            nextPageToken = response.data.nextPageToken
            if(response.isSucefull) callback.onResult(response.data.items)
            handleResponse.invoke(response)
        }
    }

    override fun loadAfter(params: LoadParams<String>, callback: LoadCallback<MyData>) {
        request.setNextPageToken(params.key).enqueue { response ->
            nextPageToken = response.data.nextPageToken
            if(response.isSucefull) callback.onResult(response.data.items)
            handleResponse.invoke(response)
        }
    }

    class Factory<R>(
        private val request: SomeLibraryPagedClientRequest<MyData>,
        private val handleResponse: (Response<R>) -> Unit
    ) : DataSource.Factory<String, R>() {
        override fun create(): DataSource<String, R> = MySimpleDataSource<R>()
    }
}

【讨论】:

    【解决方案2】:

    我们可以创建多个包含多个 LiveData 对象的 DataSource.Factory 类实例。

    首先在主活动中创建工厂和视图模型的实例,然后编写一个切换条件或 if else 梯形图,用于从 DataSource.Factory 类中选择数据源。

    在切换条件下需要调用 factory.create(viewmodel).getLiveData 方法

    例如

    switch (service){
            case 1:
                final Adapter adapter = new Adapter();
                factory.create(viewModel.getClass()).getPagedECListLiveData().observe((LifecycleOwner) activity, new Observer<PagedList<ECRecord>>() {
                    @Override
                    public void onChanged(@Nullable PagedList<ECRecord> ecRecords) {
                        Adapter.submitList(ecRecords);
                    }
                });
                recyclerView.setAdapter(adapter);
                break;
            case 2:
                final CAdapter cadapter = new CAdapter();
                factory.create(viewModel.getClass()).getPagedSTListLiveData().observe((LifecycleOwner) activity, new Observer<PagedList<STRecord>>() {
                    @Override
                    public void onChanged(@Nullable PagedList<STRecord> stRecords) {
                        ECTAdapter.submitList(stRecords);
                    }
                });
                recyclerView.setAdapter(cadapter);
                break;
    }
    

    快乐编码:)

    【讨论】:

      【解决方案3】:

      应用架构指南中所述,建议使用单一数据源,因此无论您拥有多少数据源,都应该只有一个单一数据源。

      分页库中使用的示例都依赖于这一事实,这就是分页库默认支持 Room 的原因。但这并不意味着您必须使用数据库,事实上:

      在此模型中,数据库作为唯一的事实来源,并且 应用程序的其他部分通过存储库访问它。不管 无论您使用磁盘缓存,我们都建议您的存储库 将数据源指定为其他人的唯一真实来源 你的应用程序。

      P.S:即使您不想指定单一事实来源,也不必定义多个DataSource,您只需实现一个自定义数据源,将多个数据流组合起来创建一个可显示的列表项目。例如:

      public class MentionKeyedDataSource extends ItemKeyedDataSource<Long, Mention> {
      
          private Repository repository;
          ...
          private List<Mention> cachedItems;
      
          public MentionKeyedDataSource(Repository repository, ..., List<Mention> cachedItems){
              super();
      
              this.repository = repository;
              ...
              this.cachedItems = new ArrayList<>(cachedItems);
          }
      
          @Override
          public void loadInitial(@NonNull LoadInitialParams<Long> params, final @NonNull ItemKeyedDataSource.LoadInitialCallback<Mention> callback) {
              Observable.just(cachedItems)
                      .filter(() -> return cachedItems != null && !cachedItems.isEmpty())
                      .switchIfEmpty(repository.getItems(params.requestedLoadSize))
                      .subscribeOn(Schedulers.io())
                      .observeOn(AndroidSchedulers.mainThread())
                      .subscribe(response -> callback.onResult(response.data.list));
          }
          ...
      

      【讨论】:

      • 这根本不能解释问题。
      猜你喜欢
      • 1970-01-01
      • 2019-10-16
      • 1970-01-01
      • 2011-09-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-07-24
      • 2011-05-06
      相关资源
      最近更新 更多