7

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

4

3 回答 3

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 公开为 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 调用。在这种情况下,您可以只实现一个通用数据源和一个接收请求对象和响应回调作为 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>()
    }
}
于 2019-04-01T22:25:31.407 回答
0

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

首先在主活动中创建工厂和视图模型的实例,然后编写一个切换条件或梯形图,用于从 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;
}

快乐编码:)

于 2019-12-10T15:39:21.853 回答
-3

正如应用程序架构指南中所见,建议使用单一事实来源,因此无论您拥有多少数据源,都应该只有一个单一事实来源。

分页库中使用的示例都依赖于这个事实,这就是分页库默认支持 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));
    }
    ...
于 2018-07-03T05:24:49.880 回答