В этом уроке рассмотрим, чем отличаются существующие виды DataSource: PositionalDataSource, PageKeyedDataSource, ItemKeyedDataSource

 


Полный список уроков курса:


 

Напомню, что DataSource - это мост между PagedList и Storage. Где Storage может быть базой данных, сервером или еще каким-либо источником данных, которые вы хотите отобразить в списке. И т.к. разные Storage могут иметь разный формат запроса данных, DataSource помогает привести эти форматы в понятные для PagedList методы.

У каждого DataSource есть метод для первоначальной загрузки данных и метод (или методы) для последующей подгрузки. Первоначальная загрузка выполняется в момент создания PagedList, чтобы списку было что показать, когда адаптер получает PagedList. Дальше мы скроллим список, а PagedList подгружает данные, используя для этого методы последующей подгрузки.

На данный момент есть три различных DataSource. В целом они похожи. Общие принципы и нюансы их работы мы уже рассмотрели очень подробно в прошлых уроках (рекомендую просмотреть их, чтобы лучше понимать дальнейший текст). В этом уроке я сделаю краткий обзор методов и параметров, чтобы увидеть, в чем отличия.

 

 

 

PositionalDataSource

Презентация с Google IO.

Этот DataSource позволяет запрашивать данные по позиции. Т.е. если тянем данные, например, из БД, то можем указать, с какой позиции и сколько данных грузить. Если данные из файла, то указываем с какой строки и сколько строк грузить.

PagedList сам определяет необходимую позицию, с которой надо грузить очередную порцию данных, и передает ее в DataSource.

 

Пустая реализация выглядит так:

class MyDataSource extends PositionalDataSource<Value> {

   @Override
   public void loadInitial(@NonNull LoadInitialParams params, @NonNull LoadInitialCallback<Value> callback) {
      
   }

   @Override
   public void loadRange(@NonNull LoadRangeParams params, @NonNull LoadRangeCallback<Value> callback) {

   }
}

Value - это тип данных которые ожидает получить PagedList

 

Метод loadInitial - первоначальная загрузка данных из Storage.

LoadInitialParams содержит следующие параметры:

  • int requestedStartPosition - с какой позиции грузить
  • int requestedLoadSize - сколько грузить
  • int pageSize - размер страницы (число загруженных данных должно быть кратно значению pageSize)
  • boolean placeholdersEnabled - включены ли placeholders 

LoadInitialCallback имеет два варианта метода для передачи данных в PagedList:

  •  onResult (List<Value> data, int position) - передаем данные и указываем с какой позиции они начинаются. Если список с данными пустой, то PagedList будет считать, что данных нет совсем.
  •  onResult(List<Value> data, int position, int totalCount) - то же самое, что и предыдущий вариант, но дополнительно указываем общее число записей. Этот вариант используется, если включены placeholders.

 

Метод loadRange - последующая подгрузка данных из Storage. Вызывается при прокрутке списка.

LoadRangeParams содержит следующие параметры:

  • int startPosition - с какой позиции грузить
  • int loadSize - сколько грузить

LoadRangeCallback имеет один вариант метода для передачи данных в PagedList:

  • onResult(List<T> data) - просто передаем данные. Если список будет пустым, PagedList будет считать, что данные закончились.

 

 

 

PageKeyedDataSource

Презентация с Google IO.

Этот DataSource подходит для общения со Storage, который вместе с очередной порцией данных передает нам какой-то ключ для получения следующей порции данных.

Это может быть постраничная загрузка с параметром page. Мы просим данные, например, с page = 4. Storage возвращает нам их и сообщает, что следующую порцию можно получить, передав ему page с значением 5.

Для числовых значений page это выглядит бессмысленным, потому что идет просто прибавление единицы. Но ключ может быть и текстовым. Например, так раньше работал API Youtube (не знаю, как сейчас). Т.е. мы ищем видео по какому-то поисковому запросу. Youtube возвращает нам первую порцию данных и с ними текстовый токен. В следующем запросе к Youtube мы передаем этот токен, чтобы получить следующую порцию результатов нашего поиска. И так далее.

Также сервер может в качестве ключа вообще передавать готовую ссылку, которую надо будет использовать, чтобы получить следующую порцию данных.

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

 

Пустая реализация

class MyDataSource extends PageKeyedDataSource<Key, Value> {

   @Override
   public void loadInitial(@NonNull LoadInitialParams<Key> params, @NonNull LoadInitialCallback<Key, Value> callback) {
      
   }

   @Override
   public void loadBefore(@NonNull LoadParams<Key> params, @NonNull LoadCallback<Key, Value> callback) {

   }

   @Override
   public void loadAfter(@NonNull LoadParams<Key> params, @NonNull LoadCallback<Key, Value> callback) {

   }
}

 

Key - тип ключа (page, токена и т.п.), который используется для получения данных

Value - это тип данных которые ожидает получить PagedList

 

Метод loadInitial - первоначальная загрузка данных.

LoadInitialParams содержит следующие параметры:

  • int requestedLoadSize - сколько данных грузить
  • boolean placeholdersEnabled - включены ли placeholders

LoadInitialCallback имеет два варианта метода для передачи данных в PagedList

  • onResult(List<Value> data, Key previousPageKey, Key nextPageKey) - передаем данные и указываем ключи для загрузки предыдущей и последующей порций данных. Если вместо ключа мы передаем null, то PagedList понимает, что в том направлении данных нет. Соответственно, previousPageKey чаще всего будет null, т.к. мы грузим данные с начала и предыдущих порций нет.
  • onResult(List<Value> data, int position, int totalCount, Key previousPageKey, Key nextPageKey) - то же самое, что и предыдущий вариант, но дополнительно указываем общее число записей и позицию, с которой эти данные начинаются. Этот вариант используется, если включены placeholders.

 

Метод loadBefore - загрузка предыдущей порции данных. Он вызывается при прокрутке списка вверх, если вы указали previousPageKey при вызове колбэка в loadInitial. Но чаще всего там previousPageKey = null и метод loadBefore не нуждается в реализации, т.к. он просто не вызывается.

LoadParams содержит следующие параметры:

  • Key key - ключ для получения данных
  • int requestedLoadSize - желаемый размер порции данных. Этот параметр не является обязательным. У сервера может быть свое видение размера порции.

LoadCallback имеет один вариант метода для передачи данных в PagedList:

  • onResult(List<Value> data, Key adjacentPageKey) - передаем данные и указываем какой ключ использовать для получения очередной предыдущей порции. Если в качестве ключа передать null, то PagedList поймет, что в этом направлении данных больше нет.

 

Метод loadAfter - загрузка следующей порции данных. Он вызывается при прокрутке списка вниз, если вы указали nextPageKey при вызове колбэка в loadInitial.

LoadlParams содержит следующие параметры:

  • Key key - ключ для получения данных
  • int requestedLoadSize - желаемый размер порции данных. Этот параметр не является обязательным. У сервера может быть свое видение размера порции.

LoadCallback имеет один вариант метода для передачи данных в PagedList:

  • onResult(List<Value> data, Key adjacentPageKey) - передаем данные и указываем какой ключ использовать для получения очередной следующей порции. Если в качестве ключа передать null, то PagedList поймет, что в этом направлении данных больше нет.

  

 

Важное замечание! Возможны варианты, когда мы можем обойтись без получения ключей от сервера. Например, мы запрашиваем данные с сервера, используя номер страницы (page). Сервер возвращает данные, но не возвращает номер следующей страницы. В этом случае мы можем создать счетчик внутри DataSource и сами его изменять на единицу при каждом получении данных.

Это, конечно, хуже, чем получать ключ с сервера, потому что в результате какой-нибудь ошибки в нашей логике может возникнуть рассинхрон с сервером. Но если мы не можем повлиять на структуру ответа сервера, то другого выбора у нас нет.

 

 

  

ItemKeyedDataSource

Презентация с Google IO.

Этот DataSource похож на только что рассмотренный нами PageKeyedDataSource. Он также использует предыдущие полученные от сервера данные для загрузки последующих данных. Но в качестве ключей он использует не отдельные значения типа page или токена, а непосредственно данные.

Когда мы скроллим список и приближаемся к его концу, PagedLIst возьмет последний элемент списка и даст его нам. А мы, используя этот элемент должны загрузить следующую порцию данных, которая следует за этим последним элементом.

Самый показательный кейс для этого DataSource - это отсортированный список. Например, мы выводим из БД записи, отсортированные по какому-либо ключу. При скролле списка мы в DataSource получаем последний элемент списка, и зная его ключ, сможем подгрузить следующую за ним порцию данных, используя ту же сортировку.

 

Пустая реализация

class MyDataSource extends ItemKeyedDataSource<Key, Value> {

   @Override
   public void loadInitial(@NonNull LoadInitialParams<Key> params, @NonNull LoadInitialCallback<Value> callback) {
      
   }

   @Override
   public void loadAfter(@NonNull LoadParams<Key> params, @NonNull LoadCallback<Value> callback) {

   }

   @Override
   public void loadBefore(@NonNull LoadParams<Key> params, @NonNull LoadCallback<Value> callback) {

   }

   @NonNull
   @Override
   public Key getKey(@NonNull Value item) {
       return null;
   }
}

Key - это тип данных, который будет использоваться в качестве ключа.

Value - это тип данных, которые ожидает получить PagedList

 

Метод getKey - здесь от нас требуется указать, как получить ключ из элемента списка. Т.е. если у нас тип данных (Value) - это, например, Employee, то тип Key может быть Long, а метод будет выглядеть так:

public Long getKey(@NonNull Employee item) {
   return item.id;
}

Теперь PagedList знает, как из любого элемента списка получить ключ, который будет использоваться для загрузки последующих данных.

 

Может быть так, что вам в качестве ключа нужен весь элемент. Тогда реализация будет выглядеть так:

public Employee getKey(@NonNull Employee item) {
   return item;
}

Т.е. типы Key и Value оба равны Employee.

 

Как получать Key из Value - зависит от вашей задачи.

 

 

Метод loadInitial - первоначальная загрузка данных.

LoadInitialParams содержит следующие параметры:

  • Key requestedInitialKey - ключ для начальной загрузки данных (может быть указан методом setInitialKey в PagedList.Builder или в LivePagedListBuilder)
  • int requestedLoadSize - сколько данных грузить
  • boolean placeholdersEnabled - включены ли placeholders

LoadInitialCallback имеет два варианта метода для передачи данных в PagedList

  • onResult(@NonNull List<Value> data) - только передаем данные
  • onResult(@NonNull List<Value> data, int position, int totalCount) - то же самое, что и предыдущий вариант, но дополнительно указываем общее число записей и позицию, с которой эти данные начинаются. Этот вариант используется, если включены placeholders.

 

Метод loadBefore - загрузка предыдущей порции данных. Он вызывается при прокрутке списка вверх, чтобы подгрузить предыдущие записи.

LoadParams содержит следующие параметры:

  • Key key - ключ для получения данных. Т.е. PagedList берет первый элемент списка, получает из него ключ методом getKey и дает этот ключ нам.
  • int requestedLoadSize - желаемый размер порции данных. Этот параметр не является обязательным. У сервера может быть свое видение размера порции.

LoadCallback имеет один вариант метода для передачи данных в PagedList 

  • onResult(List<Value> data) - передаем данные. Если передать пустой список, то PagedList поймет, что в этом направлении данных больше нет.

 

Метод loadAfter - загрузка следующей порции данных. Он вызывается при прокрутке списка вниз, чтобы подгрузить следующие записи.

LoadParams содержит следующие параметры:

  • Key key - ключ для получения данных. Т.е. PagedList берет последний элемент списка, получает из него ключ методом getKey и дает этот ключ нам.
  • int requestedLoadSize - желаемый размер порции данных. Этот параметр не является обязательным. У сервера может быть свое видение размера порции.

LoadCallback имеет один вариант метода для передачи данных в PagedList

  • onResult(List<Value> data) - передаем данные. Если передать пустой список, то PagedList поймет, что в этом направлении данных больше нет.

 

 


Присоединяйтесь к нам в Telegram:

- в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.

- в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Kotlin, RxJava, Dagger, Тестирование 

- ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня

- новый чат Performance для обсуждения проблем производительности и для ваших пожеланий по содержанию курса по этой теме 




Language

Социальные сети

 

Telegram канал



Android чат в Telegram



Группа ВКонтакте



Поддержка проекта

Яндекс
410011180491924

WebMoney
R248743991365
Z551306702056

Paypal