简体   繁体   English

用于多个数据源的分页库 DataSource.Factory

[英]Paging library DataSource.Factory for multiple data sources

The new paging library allows us to specify a custom data source to use with data pagination.新的分页库允许我们指定用于数据分页的自定义数据源。 Paging library documentation and sample code on github show us how to create your custom data source instances by creating a subclass of DataSource.Factory like so: 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
    }
}

In a real app, you'd generally have multiple views with recyclerviews and hence multiple custom data sources.在一个真实的应用程序中,您通常会有多个带有 recyclerviews 的视图,因此有多个自定义数据源。 So, do you end up creating multiple implementations of DataSource.Factory per data source or is there a more generic solution?那么,您最终是为每个数据源创建多个 DataSource.Factory 实现,还是有更通用的解决方案?

Not always.不总是。

If you are using other Android Architecture components or libraries that give it good support, in most cases the DataSource.Factory will be delivered as a result of a method call like Room database does.如果您正在使用提供良好支持的其他 Android 架构组件或库,在大多数情况下,DataSource.Factory 将作为方法调用的结果交付,例如 Room 数据库。

If you really want a very generic one and have no problem with reflection:如果您真的想要一个非常通用的并且对反射没有问题:

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

Your example shows a DataSource.Factory that exposes the DataSource as a LiveData.您的示例显示了将 DataSource 公开为 LiveData 的 DataSource.Factory。 This is just necessary in specific cases, for example, when the DataSource holds a retry method for the API call.这仅在特定情况下是必需的,例如,当 DataSource 持有 API 调用的重试方法时。 In other cases, your DataSource.Factory will be as simple as 3 more lines in your DataSource:在其他情况下,您的 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>()
    }
}

I guess the most common case for custom DataSource.Factory is paginated REST API calls.我猜自定义 DataSource.Factory 最常见的情况是分页的 REST API 调用。 In this case, you may just implement one generic DataSource and one DataSource.Factory that receives the request object and response callback as a lambda.在这种情况下,您可以只实现一个通用数据源和一个接收请求对象和响应回调作为 lambda 的 DataSource.Factory。

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

We can create multiple instances of DataSource.Factory class which holds multilpe LiveData objects.我们可以创建包含多个 LiveData 对象的 DataSource.Factory 类的多个实例。

First create instance of factory and viewmodel in main activity then write a switch condition or if else ladder for choosing data source from DataSource.Factory class.首先在主要活动中创建工厂和视图模型的实例,然后编写一个切换条件或梯形图,用于从 DataSource.Factory 类中选择数据源。

In switch condition you need to call factory.create(viewmodel).getLiveData method在切换条件下,您需要调用 factory.create(viewmodel).getLiveData 方法

For example例如

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

Happy Coding :)快乐编码:)

As seen in the Guide to the app architecture it's advised to have a single source of truth so no matter how many data sources you have you should only have one single source of truth.正如应用程序架构指南中所见,建议使用单一事实来源,因此无论您拥有多少数据源,都应该只有一个单一事实来源。

Examples used in the Paging Library all rely on this fact and that is why paging library support Room by default.分页库中使用的示例都依赖于这个事实,这就是分页库默认支持 Room 的原因。 But it don't means you have to use database, as a matter of fact:但这并不意味着您必须使用数据库,事实上:

In this model, the database serves as the single source of truth, and other parts of the app access it via the repository.在此模型中,数据库充当单一事实来源,应用程序的其他部分通过存储库访问它。 Regardless of whether you use a disk cache, we recommend that your repository designate a data source as the single source of truth to the rest of your app.无论您是否使用磁盘缓存,我们都建议您的存储库将数据源指定为应用程序其余部分的唯一真实来源。

PS: Even if you don't want to designate a single source of truth you don't have to define multiple DataSource you can just implement a custom data source that combine multiple stream of data to create a displayable list of items. PS:即使您不想指定单一数据源,也不必定义多个DataSource ,您只需实现一个自定义数据源,将多个数据流组合起来以创建可显示的项目列表。 For example:例如:

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));
    }
    ...

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 Android 分页库 - Map 房间 DataSource.Factory&lt;*, DatabaseModel&gt; 到 DataSource.Factory&lt;*, PresenterModel&gt; - Android Paging Library - Map Room DataSource.Factory<*, DatabaseModel> to DataSource.Factory<*, PresenterModel> 自定义构造函数PageKeyedDataSource()在分页库的datasource.factory()中崩溃应用程序 - Custom constructor PageKeyedDataSource() crashes app in datasource.factory() of paging library 具有多视图 RecyclerView 的 Room DataSource.Factory - Room DataSource.Factory with multiple view RecyclerView Android体系结构组件分页DataSource.Factory错误 - Android architecture components paging DataSource.Factory error 如何从 DataSource.Factory 获取数据 - How to get data from DataSource.Factory DataSource.Factory 未使用正确的变量值 Android 分页 - DataSource.Factory is not using correct variable value Android Paging 无法从Android的分页中的DataSource.Factory获得toLiveData - toLiveData not available from DataSource.Factory in Android's Paging 房间本地单元测试 - 从 DataSource.Factory 查询 PagedList - Room Local Unit Test - Query PagedList from DataSource.Factory DataSource.Factory的create方法不会得到调用 - create method of DataSource.Factory doesn't get call Kotlin 协程挂起错误与 Room DataSource.Factory - Kotlin coroutine suspend error with Room DataSource.Factory
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM