惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

IT之家
IT之家
C
CXSECURITY Database RSS Feed - CXSecurity.com
V
Visual Studio Blog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
小众软件
小众软件
L
LangChain Blog
Cyberwarzone
Cyberwarzone
美团技术团队
The Register - Security
The Register - Security
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
T
Tor Project blog
V
V2EX
Security Archives - TechRepublic
Security Archives - TechRepublic
Hacker News: Ask HN
Hacker News: Ask HN
L
LINUX DO - 最新话题
Recent Announcements
Recent Announcements
H
Hackread – Cybersecurity News, Data Breaches, AI and More
酷 壳 – CoolShell
酷 壳 – CoolShell
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
aimingoo的专栏
aimingoo的专栏
人人都是产品经理
人人都是产品经理
F
Full Disclosure
V2EX - 技术
V2EX - 技术
The Cloudflare Blog
博客园 - 叶小钗
T
Threat Research - Cisco Blogs
阮一峰的网络日志
阮一峰的网络日志
G
GRAHAM CLULEY
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Latest news
Latest news
S
Security @ Cisco Blogs
Spread Privacy
Spread Privacy
Project Zero
Project Zero
K
Kaspersky official blog
MyScale Blog
MyScale Blog
Attack and Defense Labs
Attack and Defense Labs
云风的 BLOG
云风的 BLOG
博客园 - 【当耐特】
Hacker News - Newest:
Hacker News - Newest: "LLM"
大猫的无限游戏
大猫的无限游戏
P
Privacy International News Feed
Google DeepMind News
Google DeepMind News
WordPress大学
WordPress大学
C
Cybersecurity and Infrastructure Security Agency CISA
Webroot Blog
Webroot Blog
罗磊的独立博客
Vercel News
Vercel News
N
News and Events Feed by Topic
A
Arctic Wolf
C
CERT Recently Published Vulnerability Notes

Все публикации подряд на Хабре

Ловим музу за клавиатуру: как айтишнику стать автором Что умеет Midjourney в 2026? Мой немного грустный разбор этого шикарного инструмента Никто не любит писать тесты, но ИИ может исправить это IPv8 выглядит как мечта. Поэтому почти наверняка не взлетит Производители вернули в продажу материнки с DDR3. Что происходит? Управление агентом с телефона через Telegram теперь в KodaCode От координации к лидерству: как меняется роль руководителя разработки Я сделала родителям бизнес вместо пенсии: зарабатываем 70 тысяч, мама не даёт продать В три раза быстрее приемка товара и оптимизация трудозатрат на 73%: как «РСТ-Инвент» помог Gulliver Group ИИ-шечный мир победил? О влиянии искусственного интеллекта на игропром Кремль снижает давление на Телеграмм пока Европа строит интернет по паспорту Как CEO, CTO и CIO за 8 часов собрали ИИ-директора, который умеет держать позицию под давлением Как (не) потерять домен за выходные Вместо 8 разных VPS: как я организовал практику студентам на одном сервере Почему твой Open Source проект не замечают? R&D: искусство управления неопределенностью в разработке AI-дефляция: вакансий для разработчиков больше, а рост зарплат — худший за 15 лет Мы отдали управление роботами OpenClaw. Что из этого вышло Галактический ID: система идентификации для всех форм разумной жизни Шесть основ бизнес-анализа: начинаем с вопроса «Кто в игре?» Код-ревью, в котором дело не в коде Данные переехали. Команда — нет Системной подход к сдаче OSWE в 2025 Почему комната управления реактором покрашена в цвет морской пены 4 YAML-файла вместо PySpark: как аналитикам строить пайплайны без разработчиков LLM-агент для поиска свободных доменов: автоматизируем подбор Когда, зачем и как правильно начинать новую сессию в Claude Code? Как я заставил нейросеть писать макросы для FreeCAD Анатомия ИИ‑агента для подбора персонала. От тысячи резюме к топ‑10 за минуты Опыт разработчика как экономика внимания Автономность как точка невозврата: кто будет субъектом в цифровом будущем Обучение ИИ в «диких» условиях: как рутинные действия превращаются в датасеты Как измерить LLM для задач кибербеза: обзор открытых бенчмарков Где хранить код? Сравнение GitHub, GitLab и Bitbucket Математика объясняет, почему нормальное распределение встречается повсюду Почему ваш FinOps не работает: 12 тезисов от практиков Как подписать проектную документацию УКЭП с использованием бесплатных лицензий Pilot Адаптивное администрирование Sigla Vision Я грузил уран в бочки, а потом 20 лет строил ИТ в атомной отрасли Чем позвонить с Эвереста? История и обзор спутниковой связи. Часть 2 Как языковая модель помогает контролировать качество инструктажей по охране труда в металлургии Как не передать на desktop свой IP в РКН Анатомия SAP Privileges: как устроено управление правами в macOS MoneyDev: Сказка про три главных слова Обновлённый токенизатор видео K-VAE 2.0 от Сбера Как сделать диспетчеризацию дома на 1284 квартиры почти бесплатно Как мы разогнали железную дорогу Мы дали агентам рутину. Теперь надо решить — что делать с освободившимся временем Токсичный контент, промпт-хакинг и защита ИИ — всё о Guardrails для LLM Умный город начинается с точного взгляда: как «Фалькон Тех» меняет пространство к лучшему Навайбкодил приложение для анализа графов Почему Дюну так интересно читать? Упрощаем работу с рутиной или как стать Гендальфом Белым Деконструкция Go: CPU, RAM и что там происходит. Go Assembler база. Часть 1.1 Какие профессии исчезнут из-за ИИ, а какие появятся? И что с этим делать Как мы построили IT-отдел, где хочется расти: архитектурные встречи, прозрачные метрики и книжные подарки Rufler: Делаем из Claude Code автономный рой через один YAML-конфиг Sing-box и белый список приложений Как построить надёжный обмен сообщениями в микросервисах: лучшие практики для enterprise OpenAI строит MLM-пирамиду, а McKinsey и Accenture помогают ей в этом Дом, который не построил Фишер (Часть 2) «Сверхзвуковой математик» против «Вдумчивого логиста»: битва алгоритмов 3D-упаковки Мультимодальные модели – грубый и дорогой инструмент Разговоры ничего не стоят. Код тоже Проверки физических лиц: с кого начнет ФНС Топ-10 бесплатных нейросетей для создания видео в 2026 году Первые слои кода: как наши решения сегодня определяют архитектуру ИИ на десятилетия Разработка нового статического анализатора: PVS-Studio JavaScript Поиск уязвимостей ПО: базовый минимум или роскошный максимум Почему оценка персонала не работает как инструмент управления Как мы разработали ИИ-ассистента и сократили рутину продуктовой команды на 50% Как я ушел из найма, нажарил косточек и продал на маркетплейсах на 168 млн в год Когда 1С:ERP уже внедрена, а нормального производственного плана всё ещё нет Как я сделал Claude мультимодальным, подключив к нему Qwen Omni Как приглашение на вакансию мечты превращается в атаку Infrastructure as Code: философия и лучшие практики IaC Тестируем Yandex Code Assistant на задаче, в которой нужно хранить секреты nxs-universal-chart v3.0: новое поколение универсального Helm-чарта Callback Injection: Техника, которая отправила Microsoft Defender в глухой нокаут «Все идеи на стол»: митап как способ вывести проект из тупика Сегодня я узнал нечто новое о GPU благодаря багу в своей игре Как заставить LLM ̶ ̶г̶а̶л̶л̶ю̶ ̶ эволюционировать Карта событий как фундамент аналитики: практический кейс для E-commerce Что выбрать для AI: x86, ARM или RISC-V? Дайджест железа за март Роль соматических мутаций в развитии аутоиммунных заболеваний: путь к избирательной терапии Mythos от Anthropic — тревожный сигнал для всех, а не только для банков Guardrails для LLM на Java: как приручить промпт‑инъекции и токсичные ответы Green-VLA: как мы собрали VLA-модель для реального антропоморфного робота и не потеряли обобщение Финансовая гонка вооружений: почему умные люди добровольно в ней участвуют Эра ИИ-агентов наступила: выбираем лучшего цифрового сотрудника # Практический опыт внедрения WinCC Redundancy на производственном предприятии Сделал MVP за 3 дня, а потом неделю прикручивал оплату. Оно того стоило? Физика против Маска: почему Starship V3 может оказаться ещё одной катастрофой Нефть Венесуэлы: крупнейшие запасы в мире, но не крупнейшая нефтяная держава JPA 4. Переосмысление Hibernate Почему зеркальная фотокамера Nikon D5 десятилетней давности идеально подошла для миссии «Артемида-2» Проект «Уровень-Спутник» или как мы сделали платформу для гидрологов «Замедлиться, чтобы ускориться»: почему ИИ повышает цену ошибок в требованиях и архитектуре Как с нуля поднять трафик IT-компании на 1657% при бюджете 55 тыс. и выжить Pixel-perfect Downsampling — идеальная отрисовка 50 миллионов точек без потерь
Обновление Paginator с 3.x до 8.x
Jamal_Aliev · 2026-05-03 · via Все публикации подряд на Хабре

Уровень сложностиПростой

Время на прочтение6 мин

Охват и читатели407

Туториал

Если вы сидели на Paginator 3.3.0 и решили сразу прыгнуть на актуальную 8.6.2
это не совсем «обновление», а скорее миграция на, по сути, другую библиотеку. Концепции
те же (page state, закладки, прыжки, snapshot/cacheFlow), но между этими версиями —
200+ коммитов, пять мажорных релизов (4.x → 8.x), переезд с JitPack на Maven Central,
переход с Android-only на Kotlin Multiplatform, разделение на несколько артефактов
и довольно агрессивная серия переименований публичного API.

Эта статья — про то, что реально изменилось, и в каком порядке я бы рекомендовал это
трогать. Основано на диффе между (3.3.0) и (8.6.2).


1. Координаты артефакта и репозиторий

Это первое и обязательное изменение.

3.3.0 — JitPack:

repositories {
    maven { setUrl("https://jitpack.io") }
}
dependencies {
    implementation("com.github.jamal-wia:Paginator:3.3.0")
}

8.6.2 — Maven Central + BOM + многомодульная структура:

repositories {
    mavenCentral() // JitPack больше не нужен
}
dependencies {
    implementation(platform("io.github.jamal-wia:paginator-bom:8.6.2"))
    implementation("io.github.jamal-wia:paginator")           // ядро, обязательно
    implementation("io.github.jamal-wia:paginator-compose")   // опционально — Compose / CMP
    implementation("io.github.jamal-wia:paginator-view")      // опционально — Android Views
}

groupId сменился (com.github.jamal-wiaio.github.jamal-wia), артефакт разделился
на paginator / paginator-compose / paginator-view, и появился paginator-bom, чтобы
все три артефакта на classpath не разъезжались по версиям.

2. Платформы

3.3.0 живёт в paginator/src/main/java/... — это Android-only библиотека.

8.6.2 — Kotlin Multiplatform: исходники переехали в commonMain, опубликованные
таргеты — Android, JVM, iosX64, iosArm64, iosSimulatorArm64. Если у вас KMP-проект,
теперь можно класть paginator прямо в commonMain.dependencies, и Gradle сам подтянет
нужный платформенный артефакт из KMP-метаданных.

3. Номер страницы: UInt → Int

В 3.3.0 номер страницы был UInt (1u, BookmarkUInt(page = 5u), page: UInt). В 8.6.2
это Int. Все суффиксы u и любые упоминания UInt/BookmarkUInt придётся переписать.

// 3.3.0
paginator.setPageState(page = 1u, ...)
paginator.bookmarks += BookmarkUInt(page = 5u)

// 8.6.2
paginator.setState(page = 1, ...)
paginator.bookmarks += BookmarkInt(page = 5)

Контракт теперь такой: page >= 1, а 0 для контекстных страниц означает «ещё не начали».

4. Лямбда load

Это, пожалуй, первое breaking-изменение, которое вы увидите, как только начнёте писать код.

3.3.0:

val paginator = Paginator<Item> { page: UInt ->
    repo.loadPage(page.toInt()) // возвращает List<Item>
}

8.6.2:

val paginator = Paginator<Item> { page: Int ->
    LoadResult(repo.loadPage(page))
    // или с метаданными API:
    // LoadResult(items, MyMetadata(totalCount = response.total))
}

Поменялось три вещи сразу: свойство называется load, а не source; параметр стал Int;
лямбда теперь обязана возвращать LoadResult<T> (data + опциональный Metadata).
LoadResult — официальная точка расширения для всего, что вы хотите прокинуть в
PageState.metadata: total count, курсор, версия страницы и т.д.

5. Paginator VS MutablePaginator

В 3.3.0 один класс Paginator умел всё — навигацию, addElement, removeElement, resize,
setPageState. Начиная с 6.x это разделено:

  • Paginator<T> — read-only, только навигация (jump*, goNextPage, goPreviousPage, restart, refresh).

  • MutablePaginator<T> — наследник, в нём CRUD по элементам, resize, setState.

Если вы где-то вызываете addElement / removeElement / setPageState / resize /
setState, тип переменной нужно сменить на MutablePaginator<T>. Для чисто-читающих
сценариев можно оставить Paginator<T> и получить более узкий API.

6. Список переименований из 5.0.0

Бо́льшая часть ошибок компиляции после апгрейда придёт именно отсюда:

3.3.0

8.6.2

setPageState(...)

setState(...)

removePageState(...)

removeState(...)

getPageState(page)

getStateOf(page)

ignoreCapacity

isCapacityUnlimited

isValidSuccessState

isFilledSuccessState

enforceCapacity

coerceToCapacity (7.x)

fastSearchPageBefore

walkBackwardWhile

fastSearchPageAfter

walkForwardWhile

walkForwardWhile/... (методы)

extension-функции

removeElement(...) (метод)

extension-функция

refreshAll, часть addElement

extension-функции

Плюс: cache больше не торчит наружу как sortedMap. Он живёт за core: PagingCore /
cache: PagingCache. Если в 3.3.0 вы лазили во внутреннюю мапу — теперь только через API
core/cache.

7. Перепаковка типов

В 3.3.0 почти всё было вложено в сам Paginator (Paginator.PageState.SuccessPage,
Paginator.Bookmark.BookmarkUInt, Paginator.LockedException...). В 8.6.2 эти типы
разнесены по нормальным под-пакетам:

  • com.jamal_aliev.paginator.page.PageState (+ PlaceholderPageState, PaginatorUiState)

  • com.jamal_aliev.paginator.bookmark.BookmarkInt

  • com.jamal_aliev.paginator.exception.LockedException.*, FinalPageExceededException, LoadGuardedException

  • com.jamal_aliev.paginator.load.LoadResult, Metadata

  • com.jamal_aliev.paginator.cache.PagingCache и стратегии кэша

  • com.jamal_aliev.paginator.logger.PaginatorLogger

Все импорты придётся переписать. Хорошая новость — после правок из §6 IDE-автоимпорт
закроет почти всё это сам.

8. Что нового стоит знать, пока вы там

Это не breaking, но это меняет облик «идиоматичного» кода в 8.x. Раз уж всё равно правим —
имеет смысл сразу подцепить:

  • finalPage: Int — верхняя граница пагинации, кидает FinalPageExceededException (5.1.0). Полезно, когда API сообщает общее число страниц.

  • loadGuard — колбэк, который может «зарубить» загрузку до сетевого запроса, кидая LoadGuardedException. Заменяет самописные флаги «можно ли сейчас грузить».

  • Стратегии кэшаLRU / FIFO / TTL / SlidingWindow / Composite через PagingCache. В 3.3.0 единственной ручкой был capacity.

  • Сериализация состоянияpaginator.saveState(...) / restoreState(...) поверх kotlinx.serialization. Заменяет ручной танец «сохрани последние номера страниц в SavedStateHandle» при процесс-смерти.

  • PaginatorUiState — единый Flow для UI-слоя (loading / data / error / empty), рекомендуется в новом Quick Start вместо ручной сборки snapshot + флагов загрузки.

  • PlaceholderProgressPage — встроенная поддержка skeleton/shimmer-строк.

  • PaginatorLogger — pluggable логирование (AndroidLogger, NoOpLogger).

  • DSL-builderpaginator { … } вместо позиционного конструктора (8.1+).

  • paginator-compose — автоматический prefetch для LazyColumn / LazyRow / LazyVerticalGrid / LazyVerticalStaggeredGrid. Никаких больше LaunchedEffect + snapshotFlow.

  • paginator-viewbindPaginated / bindPrefetchToRecyclerView для Android Views.

  • CursorPaginator / MutableCursorPaginator — параллельный курсорный режим (prev / self / next), под GraphQL connections, чаты и любые API, которые вместо номеров страниц возвращают курсоры (8.3+). Если ваш бэкенд уже умеет курсоры — это строго лучше, чем эмулировать их поверх offset-пагинации.

  • Dirty pages — трекинг «грязных» страниц для частичной инвалидации вместо сноса всего кэша.

9. Наглядно

3.3.0:

class MainViewModel : ViewModel() {

    private val paginator = Paginator<Item> { page: UInt ->
        SampleRepository.loadPage(page.toInt())
    }

    init { viewModelScope.launch { paginator.jumpForward() } }

    override fun onCleared() {
        paginator.release()
        super.onCleared()
    }
}

8.6.2:

class MainViewModel : ViewModel() {

    private val paginator = MutablePaginator<Item> { page: Int ->
        LoadResult(SampleRepository.loadPage(page))
    }.apply {
        // logger = AndroidLogger()
        // finalPage = 50
    }

    val uiState = paginator.uiState // Flow<PaginatorUiState<Item>>

    init { viewModelScope.launch { paginator.jumpForward() } }

    override fun onCleared() {
        paginator.release()
        super.onCleared()
    }
}

10. Порядок миграции, который я бы рекомендовал

  1. Сначала Gradle. Убрать JitPack, добавить mavenCentral(), перейти на BOM и нужные артефакты.

  2. Импорты. Paginator.PageState.*com.jamal_aliev.paginator.page.PageState.* и аналогично для остальных переехавших типов (§7).

  3. UIntInt везде — страницы, закладки, параметры, возвращаемые типы (BookmarkUIntBookmarkInt).

  4. Обернуть источник данных. source = { … } с List<T>load = { … } с LoadResult(...).

  5. Сменить тип переменной на MutablePaginator, если вы вызываете какой-нибудь CRUD / setState / resize.

  6. Прогнать переименования из §6 (setPageState→setState, getPageState→getStateOf, ignoreCapacity→isCapacityUnlimited, enforceCapacity→coerceToCapacity, …).

  7. Скомпилировать. Компилятор подсветит остаток (в основном extension-функции, которые раньше были методами).

  8. Проверить runtime-поведение на репрезентативных данных. Контракт страницы теперь page >= 1, 0 означает «ещё не начали»; lock-флаги и исключения живут в пакете exception.

  9. Опционально, но имеет смысл: перейти на uiState, выбрать стратегию PagingCache, подцепить saveState / restoreState, DSL-builder и — если API курсорный — CursorPaginator.

11. Куда смотреть за деталями

Вся новая документация лежит в docs/. Самое полезное при миграции:

  • docs/1. core-concepts.md

  • docs/3. state.md — новый PaginatorUiState

  • docs/5. serialization.md — saveState / restoreState

  • docs/6. caching.md — стратегии кэша

  • docs/11. dsl-builder.md

  • docs/13. cursor-pagination.md и docs/14. paginator-vs-cursor.md


Итого

Концепции остались, написание — нет. Реально пожирающие время breaking-изменения:

  1. координаты артефакта и переезд на Maven Central + BOM,

  2. UIntInt для всех номеров страниц,

  3. source: () -> List<T>load: () -> LoadResult<T>,

  4. разделение на Paginator / MutablePaginator,

  5. партия переименований из 5.0.0,

  6. перепаковка PageState, Bookmark, исключений и кэша.

Закройте эти шесть пунктов — остальное будет либо новые опциональные фичи, либо косметика.