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

时间:2018-07-01 13:37:40

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

新的分页库使我们可以指定用于数据分页的自定义数据源。 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的多个实现,还是有一个更通用的解决方案?

3 个答案:

答案 0 :(得分:0)

并非总是如此。

如果您正在使用其他提供良好支持的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.Factory,它将DataSource公开为LiveData。这仅在特定情况下才有必要,例如,当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>()
    }
}

答案 1 :(得分:0)

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

首先在主要活动中创建factory和viewmodel的实例,然后编写一个切换条件,或者从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;
}

快乐编码:)

答案 2 :(得分:-2)

应用程序体系结构指南中可以看到,建议使用单一的事实来源,因此无论您拥有多少数据源,都应仅使用单一的事实来源。

分页库中使用的示例均依赖此事实,这就是为什么分页库默认情况下支持Room的原因。但这实际上并不意味着您必须使用数据库:

  

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

PS:即使您不想指定一个单一的事实来源,也不必定义多个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));
    }
    ...