Paging Library Datasource.factory For Multiple Data Sources
Solution 1:
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.
If you really want a very generic one and have no problem with reflection:
classGenericFactory<K, R>(privateval kClass: KClass<DataSource<K, R>>) : DataSource.Factory<K, R>() {
overridefuncreate(): DataSource<K, R> = kClass.java.newInstance()
}
Your example shows a DataSource.Factory that exposes the DataSource as a LiveData. This is just necessary in specific cases, for example, when the DataSource holds a retry method for the API call. In other cases, your DataSource.Factory will be as simple as 3 more lines in your DataSource:
classMySimpleDataSource<R> : PageKeyedDataSource<String, R>() {
overridefunloadBefore(params: LoadParams<String>,
callback: LoadCallback<String, R>) {
// do your thing
}
overridefunloadAfter(params: LoadParams<String>,
callback: LoadCallback<String, R>) {
// do your thing
}
overridefunloadInitial(params: LoadInitialParams<String>,
callback: LoadInitialCallback<String, R>) {
// do your thing
}
classFactory<R> : DataSource.Factory<String, R>() {
overridefuncreate(): DataSource<String, R> = MySimpleDataSource<R>()
}
}
I guess the most common case for custom DataSource.Factory is paginated REST API calls. 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.
dataclassMyCollection<R>(
var items: List<R>,
var nextPageToken: String
)
dataclassMyData(
var title: String = ""
)
abstractclassSomeLibraryPagedClientRequest<R> {
abstractfunsetNextPageToken(token: String?): SomeLibraryPagedClientRequest<R>
abstractfunenqueue(callback: (response: Response<R>) -> Unit): Unit
}
classMyRestApiDataSource(
privateval request: SomeLibraryPagedClientRequest<MyData>,
privateval handleResponse: (Response<R>) -> Unit
) : ItemKeyedDataSource<String, MyData>() {
var nextPageToken: String = ""overridefungetKey(item: MyData): String = nextPageToken
overridefunloadBefore(params: LoadParams<String>, callback: LoadCallback<MyData>) {
}
overridefunloadInitial(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)
}
}
overridefunloadAfter(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)
}
}
classFactory<R>(
privateval request: SomeLibraryPagedClientRequest<MyData>,
privateval handleResponse: (Response<R>) -> Unit
) : DataSource.Factory<String, R>() {
overridefuncreate(): DataSource<String, R> = MySimpleDataSource<R>()
}
}
Solution 2:
We can create multiple instances of DataSource.Factory class which holds multilpe LiveData objects.
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.
In switch condition you need to call factory.create(viewmodel).getLiveData method
For example
switch (service){
case1:
finalAdapteradapter=newAdapter();
factory.create(viewModel.getClass()).getPagedECListLiveData().observe((LifecycleOwner) activity, newObserver<PagedList<ECRecord>>() {
@OverridepublicvoidonChanged(@Nullable PagedList<ECRecord> ecRecords) {
Adapter.submitList(ecRecords);
}
});
recyclerView.setAdapter(adapter);
break;
case2:
finalCAdaptercadapter=newCAdapter();
factory.create(viewModel.getClass()).getPagedSTListLiveData().observe((LifecycleOwner) activity, newObserver<PagedList<STRecord>>() {
@OverridepublicvoidonChanged(@Nullable PagedList<STRecord> stRecords) {
ECTAdapter.submitList(stRecords);
}
});
recyclerView.setAdapter(cadapter);
break;
}
Happy Coding :)
Solution 3:
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. 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.
P.S: 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. For example:
publicclassMentionKeyedDataSourceextendsItemKeyedDataSource<Long, Mention> {
private Repository repository;
...
private List<Mention> cachedItems;
publicMentionKeyedDataSource(Repository repository, ..., List<Mention> cachedItems){
super();
this.repository = repository;
...
this.cachedItems = newArrayList<>(cachedItems);
}
@OverridepublicvoidloadInitial(@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));
}
...
Post a Comment for "Paging Library Datasource.factory For Multiple Data Sources"