Как реализовать пагинацию списка чатов с учетом обновлений через сокет
Что проверяют
Умеете ли вы совместить два источника данных, постраничную загрузку и поток событий, так, чтобы список не дублировал и не терял чаты. Следом обычно спрашивают, что будет, если сообщение пришло в чат, который ещё не загружен, и как догнать события после разрыва соединения.
Как отвечать
Ключевая идея: единый источник правды это локальная база, обычно Room. Сеть и сокет пишут в базу, интерфейс читает только из неё.
- сортировка и ключ: список сортируется по
last_message_at DESC, id DESC. Новое сообщение поднимает чат наверх, поэтому страницы по смещению (page=2&size=20) съезжают, отсюда дубли и пропуски. Серверу нужен курсор вида «чаты старше этой парыlast_message_atиid»; - Paging 3:
PagerсPagingSourceиз DAO Room иRemoteMediator, который подгружает следующую страницу с сервера и складывает её в базу. Room сам инвалидируетPagingSourceпри изменении таблицы; - сокет: событие «новое сообщение» превращается в
@Upsertстроки чата с новымlast_message_atи счётчиком непрочитанных. Если чат ещё не был загружен, он всё равно появится наверху, потому что теперь он есть в базе. Пачку событий пишите одной транзакцией, иначе список будет перестраиваться на каждое; - пропуски после разрыва: сокет не гарантирует доставку. После переподключения или возврата из фона запросите изменения с последнего известного номера события или просто перезагрузите первую страницу;
- интерфейс:
PagingDataAdapterсDiffUtilпоidчата илиLazyColumnсkey = { it.id }в Compose. Если пользователь пролистал вниз, вставка наверху не должна его сдёргивать.
Где спотыкаются
Хранят список в ArrayList внутри экрана, добавляют сообщения из сокета в конец через notifyItemInserted, а потом пагинация приносит тот же чат ещё раз. Получается дубль, а notifyDataSetChanged сбрасывает анимации и позицию.
Используют пагинацию по смещению для списка, который постоянно переупорядочивается.
Считают сокет полным источником. Без досинхронизации после обрыва список тихо устаревает, а обновление интерфейса прямо из потока сокета без переключения на главный поток роняет приложение.