Skip to content

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

1 watching

Forks

Repository files navigation

EpubReader

Android-приложение для офлайн-чтения EPUB в рамках тестового задания.

1) Цель и статус реализации

Цель проекта: реализовать устойчивый офлайн-сценарий чтения EPUB с корректным отображением текста/изображений, навигацией по документу и сохранением прогресса чтения.

Реализовано в текущем состоянии:

  • открытие EPUB из локального источника (через выбор файла пользователем);
  • распаковка EPUB и локальная обработка без сети;
  • отображение контента главы (текст и изображения);
  • навигация по главам и оглавлению;
  • сохранение и восстановление позиции чтения;
  • базовая модульная архитектура с разделением слоев.

2) Архитектура

Проект построен как модульная Clean Architecture:

  • :app — точка входа, DI-граф, корневая навигация;
  • :domain — бизнес-модели, интерфейсы репозиториев, use-case;
  • :data — реализация репозиториев, парсинг/распаковка EPUB, локальное хранение;
  • :core:resources, :core:ui — общие ресурсы и UI-блоки;
  • :feature:* — presentation-слой отдельных экранов (splash, library, reader, toc, settings).

Разделение ответственности

  • domain не зависит от Android/инфраструктуры и описывает контракты.
  • data зависит от domain, маппит инфраструктурные ошибки в прикладное поведение, хранит данные.
  • feature использует use-case из domain через ViewModel и не содержит логики доступа к данным напрямую.
  • app связывает модули и инициализирует приложение.

Управление состоянием

  • В экранах используется ViewModel + StateFlow.
  • UI подписывается на state и отправляет user-action во ViewModel.
  • Для длительных/тяжелых операций используются корутины.

DI

  • Hilt (@HiltAndroidApp, @HiltViewModel, модули в data).
  • Основные привязки репозиториев и инфраструктуры находятся в data/.../di/DataModule.kt.

Навигация

  • Навигация реализована через NavKey/Navigator в domain и корневой host в app.
  • Экранные модули изолированы друг от друга и коммуницируют через слой domain.

Ключевые архитектурные решения и trade-offs

  • Модульная декомпозиция вместо монолита UI: упрощает сопровождение и изоляцию изменений, но увеличивает количество Gradle-конфигурации.
  • Собственный парсер EPUB вместо тяжелого SDK: дает контроль над поведением и зависимостями, но переносит стоимость edge-case поддержки на команду.
  • Room + DataStore: четкое разделение between durable progress и user preferences; цена — два механизма хранения и необходимость согласованности миграций.
  • StateFlow в ViewModel: предсказуемый state lifecycle и удобство тестирования; цена — необходимость дисциплины в разделении state и one-shot событий.

3) Работа с EPUB и данными

Поток обработки EPUB

  1. Пользователь выбирает локальный EPUB (URI).
  2. Файл распаковывается в filesDir/books/<bookId> через EpubUnpacker.
  3. Метаданные и структура книги извлекаются парсером.
  4. Контент (текст + изображения) читается из распакованной структуры.
  5. Книга добавляется в локальную БД.

Локальное хранение

  • Метаданные книги и прогресс чтения: Room (epub-reader.db).
  • Настройки читалки (шрифт/тема и т.п.): DataStore Preferences.
  • Данные книги хранятся локально в filesDir, сеть для чтения не требуется.

Работа рендера EPUB

  • EPUB распаковывается как ZIP.
  • Контент глав извлекается из OPF/spine и XHTML.
  • Изображения извлекаются из внутренних ресурсов EPUB.
  • Для отрисовки изображений в UI используется Coil.

4) Обработка ошибок и нестандартных сценариев

Текущая стратегия:

  • операции импорта/парсинга обернуты в runCatching;
  • при неуспешной распаковке/парсинге временные файлы удаляются;
  • при частично проблемном EPUB используется fallback на читаемые главы/контент;
  • при отсутствии корректного контента возвращается безопасный fallback-текст вместо падения.

Нестандартные сценарии, которые учитываются:

  • битый/неполный EPUB;
  • отсутствие отдельных глав или изображений;
  • некорректные пути внутри архива (защита от zip-slip при распаковке);
  • пустой или частично читаемый документ.

5) Эксплуатация в production

Поддержка и мониторинг

Текущее состояние:

  • внешние production-инструменты мониторинга (Crashlytics/Sentry) пока не подключены.

Рекомендуемый минимальный production-профиль:

  • Crashlytics для сбора crash/ANR;
  • структурированные логи критических бизнес-событий (импорт, чтение, ошибка парсинга);
  • приоритизация инцидентов по уровню: blocker/critical/major/minor.

Процесс реагирования на инциденты

  1. Детект ошибки (краш, массовая ошибка импорта, деградация рендера).
  2. Классификация по влиянию и воспроизводимости.
  3. Горячее исправление в отдельной ветке + регресс-тест критического сценария.
  4. Выкатка на ограниченный процент пользователей (staged rollout).
  5. Постмортем: корневая причина и профилактика.

Релизы и снижение рисков

  • Рекомендуемый контур: Internal -> Closed testing -> Production.
  • Перед релизом: smoke-проход сценария чтения, проверка импорта нескольких EPUB, проверка восстановления позиции.
  • Для снижения рисков: staged rollout, фиксированный changelog, откат на предыдущий стабильный релиз при критике.

SLO/операционные ориентиры (рекомендуемые)

  • Crash-free sessions: не ниже 99.5% на стабильном канале.
  • Доля неуспешных импортов EPUB: не выше 1-2% на выборке реальных файлов.
  • Время первичного открытия книги: целевой p95 в рамках приемлемого UX для офлайн-ридера (измеряется на типовом наборе EPUB).

6) Производительность

Потенциальные узкие места:

  • большие главы с большим количеством XHTML-контента;
  • EPUB с большим числом изображений;
  • перерасчет пагинации на изменении параметров чтения.

Что уже сделано:

  • кэширование распарсенных глав в ReaderRepositoryImpl;
  • тяжелые операции вынесены из UI-потока;
  • аккуратная обработка контента с fallback-ветками, чтобы избегать зависаний/crash;
  • пересчет прогресса и пагинации выполняется в фоне.

Что можно усилить в следующих итерациях:

  • профилирование конкретных книг и оптимизация частых hot path;
  • кеширование промежуточных результатов парсинга между сессиями;
  • ограничение и батчинг дорогостоящих пересчетов.
  • Добавить стратегию progressive loading: на старте загружать минимально достаточную часть EPUB (первая доступная глава/фрагмент), открывать экран чтения сразу, а полный парсинг и догрузку остального контента выполнять в фоне после открытия; это уменьшает perceived latency и убирает ожидание полного времени разбора файла.

7) Ограничения и риски решения

  • В текущем состоянии используется пользовательский импорт EPUB по URI, а не предзагрузка файла в app/src/main/assets.
  • Не подключены Crashlytics/Sentry, поэтому наблюдаемость production пока базовая.
  • Нет полного набора автоматических UI/интеграционных тестов для всех edge-case EPUB.
  • Качество рендера зависит от структуры конкретного EPUB (нестандартная разметка может требовать расширения парсера).
  • Навигация сейчас централизована в :app; при росте числа feature и сценариев это может увеличить связность.

8) Ключевые зависимости и обоснование

  • Kotlin 2.0.21, AGP 8.13.2, Gradle 8.13: современный стабильный toolchain.
  • Compose BOM 2024.09.00, Material 3: основной UI-стек.
  • Hilt 2.57: DI и масштабируемая композиция зависимостей.
  • Room 2.7.2: надежное локальное хранение книг и прогресса.
  • DataStore Preferences 1.1.1: хранение пользовательских настроек.
  • Navigation 3 1.1.0: навигационная инфраструктура.
  • Coil 2.7.0: отрисовка изображений в reader UI.

Ограничения по зависимостям:

  • Выбран собственный парсинг EPUB вместо внешнего epublib/readium, что дает контроль, но требует поддержки edge cases.

9) Требования к окружению

  • OS: Windows/macOS/Linux.
  • JDK: 11.
  • Android SDK: compileSdk 36, targetSdk 36, minSdk 26.
  • Android Studio / стандартная Gradle-среда без закрытых инструментов.

10) Сборка и запуск

Сборка debug

./gradlew assembleDebug

Для Windows PowerShell:

.\gradlew.bat assembleDebug

Сборка release (APK для передачи)

./gradlew assembleRelease

Для Windows PowerShell:

.\gradlew.bat assembleRelease

Ожидаемый путь APK:

  • app/build/outputs/apk/release/app-release.apk

Запуск локально

  1. Открыть проект в Android Studio.
  2. Дождаться sync Gradle.
  3. Запустить app-модуль на эмуляторе или устройстве (Android 8.0+).
  4. Добавить локальный EPUB через экран библиотеки.
  5. Пройти базовый сценарий: открыть книгу -> читать -> сменить главу -> перезапустить приложение -> проверить восстановление позиции.

11) Тестирование и GitLab CI

Какие тесты использовать

  • Unit-тесты (domain, data): use-case, мапперы, репозитории, парсер EPUB на наборе тестовых файлов (валидный, частично поврежденный, с большим числом изображений).
  • Инструментальные тесты (androidTest): проверка Room/DataStore интеграции и восстановления прогресса чтения после перезапуска.
  • Compose UI-тесты (feature:*): ключевой пользовательский путь (открытие книги, перелистывание, оглавление, настройки шрифта/темы, возврат в чтение).
  • Smoke-регрессия перед релизом: быстрый ручной прогон на 2-3 реальных EPUB разных размеров.

Базовый пайплайн в GitLab CI

Рекомендуемый набор стадий:

  1. lint -> ./gradlew lint
  2. unit_tests -> ./gradlew testDebugUnitTest
  3. android_tests (по возможности с эмулятором/раннером) -> ./gradlew connectedDebugAndroidTest
  4. assemble -> ./gradlew assembleDebug assembleRelease

Практический минимум, если нет доступного эмулятора в CI: запускать lint + unit_tests + assemble, а connectedDebugAndroidTest выполнять по расписанию или на выделенном раннере.

Плюсы GitLab CI для проекта

  • Единый и воспроизводимый контроль качества на каждом merge request.
  • Прозрачные quality gates до попадания в основную ветку.
  • Удобная интеграция с веточной моделью, protected branches и approvals.
  • Кэш Gradle и артефакты APK ускоряют обратную связь и упрощают проверку сборок.

Недостатки/ограничения GitLab CI

  • Настройка Android UI-тестов в CI сложнее из-за эмулятора и требований к раннеру.
  • Более длинный pipeline может замедлить цикл разработки без грамотного кэширования.
  • Поддержка инфраструктуры раннеров и образов требует отдельного внимания команды.
  • Ложные падения инструментальных тестов (flaky) требуют стабилизации и ретраев.

12) Roadmap усиления решения

Этап 1 (ближайший)

  • Добавить Crashlytics и базовую дашбордизацию ошибок импорта/парсинга.
  • Зафиксировать регрессионный набор EPUB-файлов (малый/средний/большой, с изображениями и сложным XHTML).
  • Ввести автоматические проверки базового сценария чтения на CI.

Этап 2

  • Добавить персистентный кеш промежуточного разбора EPUB и метрики времени открытия.
  • Оптимизировать memory footprint для книг с большим количеством inline-изображений.
  • Расширить покрытие parser edge cases (нестандартные nav/toc, сложные вложенные блоки XHTML).
  • Вынести навигационные контракты и инфраструктуру в отдельный модуль (например, :core:navigation) для уменьшения связности :app и упрощения масштабирования.

Этап 3

  • Перейти к более строгим release-gates: обязательные quality checks перед promotion в production.
  • Ввести формализованный incident playbook и SLA по срокам реакции/фикса.
  • Масштабировать архитектуру добавлением новых корневых модулей по бизнес-доменам (например, :auth, :notifications, :analytics), без сквозных изменений существующих feature-модулей.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages