Зачем нужна постраничная загрузка
Цена загрузки всех данных одновременно
«Зачем нужна постраничная загрузка» — бесплатный урок Android Academy на CoddyKit. Это урок 1 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Android Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Android Academy содержит 4 уроков всего.
Загрузка всех данных обходится дорого
Представьте ленту из 50 000 элементов. Если загрузить и хранить их все сразу, вы потратите лишнюю память, замедлите сеть и заморозите интерфейс на время разбора ответа.
Большинство пользователей просматривают только первые несколько экранов. Постраничная загрузка означает загрузку данных небольшими частями (страницами) по мере прокрутки, а не всех данных сразу.
Проблема в коде
Наивный подход загружает весь список в память. Даже если сервер это поддерживает, ответ может быть огромным, а разобранный список занимает большую часть кучи.
Такой подход не масштабируется и на больших наборах данных может привести к OutOfMemoryError.
// Anti-pattern: load the whole table at once
suspend fun loadAllArticles(): List<Article> {
// Could be tens of thousands of rows / megabytes of JSON
return api.getArticles(limit = 50_000)
}
// UI holds ALL of them in memory at once
val articles = loadAllArticles()
LazyColumn {
items(articles) { article -> ArticleRow(article) }
}Как выглядит страница
Страница — это небольшой фрагмент набора данных, обычно от 20 до 50 элементов. Сервер возвращает одну страницу и указатель на следующую страницу — число, смещение или токен.
Приложение хранит в памяти только несколько страниц и удаляет старые, когда пользователь прокручивает список дальше.
data class ArticlePage(
val items: List<Article>,
val nextKey: Int? // null means no more pages
)
// Example REST call returning one page
suspend fun getArticlePage(page: Int, size: Int = 20): ArticlePageЗнакомство с Paging 3
Paging 3 — это библиотека Jetpack, которая берет на себя все сложные задачи, связанные с постраничной загрузкой:
- запрос следующей страницы, когда пользователь приближается к концу списка;
- хранение в памяти только ограниченного диапазона элементов;
- предоставление состояний загрузки и ошибки;
- встроенная поддержка корутин, Flow и Jetpack Compose.
Добавление зависимости
Paging 3 поставляется в виде отдельных артефактов: среды выполнения и интеграции с Compose. Добавьте их в файл build.gradle.kts вашего модуля.
Артефакт paging-compose предоставляет вспомогательные средства для LazyColumn.
// build.gradle.kts (module)
dependencies {
val pagingVersion = "3.3.6"
implementation("androidx.paging:paging-runtime:$pagingVersion")
implementation("androidx.paging:paging-compose:$pagingVersion")
}Три основных компонента
Paging 3 строится вокруг трех типов, которые вы будете использовать снова и снова:
- PagingSource — знает, как загрузить одну страницу из источника;
- Pager — настраивает и создает поток постранично загружаемых данных;
- PagingData — контейнер элементов, выдаваемых как
Flow.
В следующих уроках мы создадим каждый из них.
PagingData и Flow
Ваш репозиторий предоставляет Flow<PagingData<T>>. Интерфейс собирает этот поток и отображает все элементы, загруженные к текущему моменту.
Вам не нужно самостоятельно собирать полный список — Paging передает элементы в интерфейс по мере необходимости.
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.PagingData
import kotlinx.coroutines.flow.Flow
class ArticleRepository(private val api: ArticleApi) {
fun articles(): Flow<PagingData<Article>> = Pager(
config = PagingConfig(pageSize = 20)
) {
ArticlePagingSource(api)
}.flow
}PagingConfig управляет диапазоном данных
PagingConfig настраивает способ загрузки и хранения данных в Paging:
pageSize— количество элементов в одном запросе страницы;prefetchDistance— расстояние до края списка, при котором запускается следующая загрузка;enablePlaceholders— показывать места-заполнители для еще не загруженных элементов;maxSize— максимальное количество элементов, хранящихся в памяти.
import androidx.paging.PagingConfig
val config = PagingConfig(
pageSize = 20,
prefetchDistance = 5,
enablePlaceholders = false,
maxSize = 100
)Сеть или база данных — или и то и другое
PagingSource может получать данные из REST API, базы данных Room или любого другого пользовательского источника.
Для полноценной работы без подключения к сети можно объединить оба источника с помощью RemoteMediator (он рассматривается в последнем уроке): сеть заполняет локальную базу данных, а интерфейс загружает страницы из этой базы.
Плавная прокрутка и меньше памяти
При постраничной загрузке прокрутка остается плавной, поскольку каждый запрос небольшой, а размер хранящегося в памяти диапазона ограничен параметром maxSize.
Индикаторы загрузки и ошибки предоставляются автоматически через LoadState, поэтому вы можете показывать индикатор внизу или кнопку повтора без ручного управления состояниями.
// LoadState exposes loading/error per direction
when (val state = adapterLoadState.append) {
is LoadState.Loading -> showBottomSpinner()
is LoadState.Error -> showRetry(state.error)
is LoadState.NotLoading -> hideBottomSpinner()
}Когда нужна постраничная загрузка
Используйте Paging 3, когда:
- набор данных большой или неограниченный — например, ленты, результаты поиска или история чата;
- данные поступают из сетевого API, возвращающего страницы или курсоры;
- вам нужны автоматический интерфейс загрузки и ошибок и контроль памяти.
Для короткого фиксированного списка, например меню настроек, обычный LazyColumn проще и вполне подходит.
Быстрая проверка
Какое основное преимущество дает использование Paging 3 для большого списка?
Итоги: зачем нужен Paging
Вы узнали, почему одновременная загрузка всех данных ухудшает производительность и увеличивает расход памяти, а также как Paging 3 решает эту проблему.
- Данные загружаются небольшими страницами по мере прокрутки списка;
- три основных компонента — PagingSource, Pager и PagingData;
PagingConfigнастраивает размер страницы, предварительную загрузку и диапазон данных в памяти;- Paging интегрируется с корутинами, Flow и Compose и автоматически предоставляет состояния загрузки и ошибки.
Далее: создание PagingSource и подключение его к Pager.
Часто задаваемые вопросы
Урок «Зачем нужна постраничная загрузка» бесплатный?
Да — полный текст урока «Зачем нужна постраничная загрузка» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Android Academy, подпишись на CoddyKit PRO. Курс Android Academy содержит 4 уроков всего.
Чему я научусь в уроке «Зачем нужна постраничная загрузка»?
Цена загрузки всех данных одновременно Ты практикуешь Android Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Android Academy?
Предыдущий опыт не требуется. Android Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 1 из 4.
Сколько времени занимает урок «Зачем нужна постраничная загрузка»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Android Academy?
Да. Каждый урок Android Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Зачем нужна постраничная загрузка
- PagingSource и Pager
- Постраничная загрузка в списках Compose
- RemoteMediator и кэширование