Библиотека подкачки DataSource.Factory для нескольких источников данных - PullRequest
0 голосов
/ 01 июля 2018

Новая библиотека подкачки позволяет нам указать пользовательский источник данных, который будет использоваться с разбиением на страницы данных. Документация библиотеки подкачки и пример кода на 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
    }
}

В реальном приложении вы, как правило, имеете несколько представлений с просмотрами переработчика и, следовательно, несколько пользовательских источников данных. Итак, вы в конечном итоге создаете несколько реализаций DataSource.Factory для одного источника данных или есть более общее решение?

Ответы [ 2 ]

0 голосов
/ 02 апреля 2019

Не всегда.

Если вы используете другие компоненты или библиотеки архитектуры 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 содержит метод retry для вызова API. В других случаях ваш DataSource.Factory будет так же прост, как и еще 3 строки в вашем DataSource:

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, который получает объект запроса и ответный обратный вызов в виде лямбды.

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>()
    }
}
0 голосов
/ 03 июля 2018

Как видно из Руководства по архитектуре приложения , рекомендуется иметь один источник правды, поэтому независимо от того, сколько источников данных у вас есть, у вас должен быть только один источник правды.

Примеры, используемые в библиотеке подкачки, основаны на этом факте, поэтому библиотека подкачки по умолчанию поддерживает 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));
    }
    ...
...