← Назад к вопросам

Как работает Paging 3 библиотека? Какие основные компоненты и как интегрировать с Room?

3.0 Senior🔥 181 комментариев
#Архитектура и паттерны#Работа с данными

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Основы работы Paging 3

Paging 3 — это библиотека Android Jetpack для эффективной работы с большими списками данных. Она решает ключевые проблемы: экономия памяти (не загружая все данные сразу), снижение нагрузки на сеть/базу данных и плавный UX при пагинации. Библиотека работает на трех уровнях: DataSource (источник данных), PagingData (поток данных) и UI (отображение).

Основные компоненты

  1. PagingSource — базовый источник данных, определяет логику загрузки "страниц" (page). Ключевые методы:

    • load(): загружает данные для конкретной страницы
    • getRefreshKey(): вычисляет ключ для восстановления состояния после refresh
  2. RemoteMediator — компонент для гибридной пагинации (локальная БД + сетевой источник). Он координирует загрузку из сети и сохранение в Room, затем делегирует PagingSource для локальной выборки.

  3. Pager — основной класс, который конфигурирует поток пагинации через PagingConfig (размер страницы, prefetch расстояние, initial load size).

  4. PagingDataAdapter — адаптер для RecyclerView, который автоматически подгружает данные при скролле. Работает с DiffUtil для эффективных обновлений.

  5. PagingData — тип данных, представляющий поток страниц, который можно наблюдать через Flow, LiveData или RxJava.

Интеграция Paging 3 с Room

Room напрямую поддерживает Paging 3 через возврат PagingSource из DAO методов. Это позволяет эффективно пагинировать результаты SQL запросов.

Пример реализации

1. Создание PagingSource в DAO:

@Dao
interface UserDao {
    @Query("SELECT * FROM users ORDER BY name ASC")
    fun getUsersPaged(): PagingSource<Int, User>
}

2. Конфигурация Pager в Repository или UseCase:

class UserRepository(private val userDao: UserDao) {
    fun getUsersFlow(): Flow<PagingData<User>> {
        return Pager(
            config = PagingConfig(
                pageSize = 20,
                prefetchDistance = 10,
                enablePlaceholders = false // или true если нужны placeholder'ы
            ),
            pagingSourceFactory = { userDao.getUsersPaged() }
        ).flow
    }
}

3. Подключение к UI через адаптер:

class UserAdapter : PagingDataAdapter<User, UserViewHolder>(USER_COMPARATOR) {
    override fun onBindViewHolder(holder: UserViewHolder, position: Int) {
        val user = getItem(position)
        holder.bind(user)
    }

    companion object {
        private val USER_COMPARATOR = object : DiffUtil.ItemCallback<User>() {
            override fun areItemsTheSame(oldItem: User, newItem: User): Boolean =
                oldItem.id == newItem.id

            override fun areContentsTheSame(oldItem: User, newItem: User): Boolean =
                oldItem == newItem
        }
    }
}

4. Наблюдение данных в Activity/Fragment:

lifecycleScope.launch {
    userRepository.getUsersFlow().collectLatest { pagingData ->
        userAdapter.submitData(pagingData)
    }
}

Ключевые особенности интеграции с Room

  • Автоматическое управление ключами: Room использует LIMIT и OFFSET SQL параметры для пагинации, где ключом является номер страницы (Int).
  • Эффективные обновления: При изменении данных в таблице Room автоматически invalidates PagingSource, вызывая новый load().
  • Placeholders: Можно использовать enablePlaceholders = true, но для этого запрос должен возвращать общее количество (COUNT) — Room не поддерживает это автоматически, нужно отдельная реализация.
  • Совместно с RemoteMediator: Для пагинации из сети с кэшированием в Room:
@ExperimentalPagingApi
class UserRemoteMediator(
    private val localDb: AppDatabase,
    private val api: UserApi
) : RemoteMediator<Int, User>() {
    
    override suspend fun load(loadType: LoadType, state: PagingState<Int, User>): MediatorResult {
        // Логика: загрузка из API, сохранение в Room, возвращение успеха/ошибки
    }
}

// Использование в Pager
Pager(
    config = PagingConfig(pageSize = 20),
    remoteMediator = UserRemoteMediator(db, api),
    pagingSourceFactory = { db.userDao().getUsersPaged() }
).flow

Преимущества такого подхода

  • Единая абстракция: одинаковый API для локальной и сетевой пагинации.
  • Оптимизация запросов: Room минимизирует запросы к БД через эффективные SQL пагинации.
  • Reactive обновления: при изменении данных в БД через @Insert, @Delete etc., PagingData автоматически обновляет UI.
  • Сочетание с Coroutines Flow: естественная интеграция с Kotlin корутинами и архитектурой ViewModel.

Таким образом, Paging 3 с Room создают мощный симбиоз для работы с большими данными, обеспечивая плавную пагинацию, эффективное использование ресурсов и чистую архитектуру с поддержкой как локальных, так и гибридных (сеть + БД) источников данных.

Как работает Paging 3 библиотека? Какие основные компоненты и как интегрировать с Room? | PrepBro