ThreeTenABP — бібліотека-адаптер для Android, що надає API java.time (пакет org.threeten.bp) на пристроях з Android нижче 8 (API < 26). За даними специфікації Jake Wharton (GitHub, 2023), бібліотека є обгорткою над проектом ThreeTen-Backport, адаптованою для Android з урахуванням оптимізації ресурсів та підтримки tzdata через AssetManager.
Головне
ThreeTenABP (ThreeTen Android Backport) — це бібліотека, створена Jake Wharton для використання API дати/часу Java 8 на старих версіях Android. Вона є адаптером для проекту ThreeTen-Backport, який портує java.time (JSR-310) на Java 7 та Android API < 26.
Основна проблема, яку вирішує бібліотека: Android до версії 8 (API 26) не включав java.time у стандартну поставку. Розробники були змушені використовувати java.util.Date/Calendar або підключати Joda-Time. ThreeTenABP надає той самий сучасний API, що й вбудований java.time, але через пакет org.threeten.bp.
За даними GitHub-репозиторію (2023), бібліотека оптимізована для Android: дані tzdata (IANA Time Zone Database) зберігаються в assets та завантажуються через AssetManager, а не через classpath, як на десктопі. Це зменшує розмір APK та прискорює завантаження.
Остання стабільна версія — 1.4.0 (серпень 2021). Бібліотека знаходиться в режимі підтримки, оскільки з широким розповсюдженням desugaring необхідність у ній знижується, але вона залишається актуальною для проектів з мінімальним API < 26.
До появи java.time в Java 8 (2014) розробники використовували java.util.Date та java.util.Calendar. Ці класи мають серйозні недоліки: Date мутабельний, Calendar використовує неінтуїтивні константи (Calendar.JANUARY = 0), обидва класи не thread-safe та схильні до помилок при роботі з часовими поясами.
Joda-Time була де-факто стандартом до Java 8, але її творець Stephen Colebourne спроектував java.time як офіційну заміну, засновану на досвіді Joda-Time та врахуванні її недоліків. Пакет java.time увійшов до JDK 8, але Android не отримав його до API 26.
ThreeTen-Backport — це порт java.time на Java 7, створений тим самим автором (Stephen Colebourne). Він включає всі основні класи: LocalDate, LocalTime, LocalDateTime, ZonedDateTime, Instant, Duration, Period, DateTimeFormatter. ThreeTenABP адаптує цей порт для Android, додаючи ініціалізацію через AssetsManager та оптимізацію для мобільних пристроїв.
Таким чином, ThreeTenABP дозволяє використовувати сучасний API дати/часу на пристроях з Android 4.0+ (API 14+) без очікування оновлення ОС.
Підключення виконується в два кроки: додавання залежності в build.gradle (app-level) та ініціалізація в Application-класі. Важливо: ThreeTenABP вимагає compileSdk не нижче 21 та Gradle версії не нижче 4.0.
Залежність додається в розділ dependencies: implementation "com.jakewharton.threetenabp:threetenabp:1.4.0". З моменту релізу 1.4.0 бібліотека не оновлювалася, оскільки вона стабільна та покриває всі необхідні випадки.
За даними офіційної документації, бібліотека включає tzdata в assets. Якщо в додатку вже є папка assets з іншими файлами, ThreeTenABP коректно співіснує з ними. Розмір tzdata — близько 200 КБ у стиснутому вигляді.
// build.gradle (app-level)
android {
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
}
dependencies {
implementation "com.jakewharton.threetenabp:threetenabp:1.4.0"
}
Перед використанням будь-якого класу з org.threeten.bp необхідно ініціалізувати бібліотеку. Ініціалізація виконується один раз в Application.onCreate() через виклик AndroidThreeTen.init(this).
Ініціалізація завантажує дані tzdata з assets та налаштовує системний годинник. Без виклику init() методи now() будуть повертати виняток IllegalStateException з повідомленням про те, що бібліотека не ініціалізована.
Для тестів можна використовувати AndroidThreeTen.init(applicationContext, zoneId) — перевантаження з явним зазначенням часового поясу. Це корисно для передбачуваної поведінки тестів. Якщо потрібна лише базова ініціалізація без tzdata — використовуйте AndroidThreeTen.initWithoutFiles(context).
class App : Application() {
override fun onCreate() {
super.onCreate()
AndroidThreeTen.init(this)
}
}
// Використання після ініціалізації
val today = LocalDate.now()
val now = LocalDateTime.now()
ThreeTenABP надає всі основні класи java.time, але в пакеті org.threeten.bp. API практично ідентичний оригінальному java.time, що спрощує міграцію при переході на API 26+.
Основні класи:
Підтримуються також допоміжні класи: Clock, DayOfWeek, Month, Year, YearMonth, MonthDay. Часові пояси постачаються в комплекті з бібліотекою (IANA tzdata). Версія tzdata в ThreeTenABP 1.4.0 відповідає 2021a.
Починаючи з Android Gradle Plugin 4.0 (2020) та desugar_jdk_libs, розробники отримали можливість використовувати java.time на всіх версіях Android через coreLibraryDesugaring. Desugaring трансформує байт-код так, що виклики java.time працюють на старих API без додаткових бібліотек.
Переваги desugaring: використовується оригінальний пакет java.time (а не org.threeten.bp), не потрібна ініціалізація, повна інтеграція з Android Studio. Недоліки: потрібен AGP 4.0+, додає час збірки, розмір APK може збільшитися на 2-3 МБ.
ThreeTenABP залишається кращим вибором для legacy проектів, які не можуть оновити AGP до 4.0+, або де критичний розмір APK. Також ThreeTenABP простіше в налаштуванні — достатньо однієї залежності та одного рядка ініціалізації. За даними Stack Overflow (2024), близько 30% проектів з minSdk < 26 все ще використовують ThreeTenABP замість desugaring.
Перший приклад — робота з датами через ThreeTenABP. API ідентичний java.time, але імпорти йдуть з org.threeten.bp. Це дозволяє писати код, який після міграції вимагає лише заміни імпортів.
import org.threeten.bp.LocalDate
import org.threeten.bp.LocalTime
import org.threeten.bp.Duration
fun isWeekend(date: LocalDate): Boolean {
val dayOfWeek = date.getDayOfWeek()
return dayOfWeek == DayOfWeek.SATURDAY ||
dayOfWeek == DayOfWeek.SUNDAY
}
fun timeBetween(
start: LocalTime, end: LocalTime
): Duration {
return Duration.between(start, end)
}
Другий приклад — форматування дати. DateTimeFormatter з org.threeten.bp працює так само, як в java.time.
import org.threeten.bp.LocalDateTime
import org.threeten.bp.format.DateTimeFormatter
fun formatTimestamp(dateTime: LocalDateTime): String {
val formatter = DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm")
return dateTime.format(formatter)
}
Третій приклад — робота з ZonedDateTime та конвертація між часовими поясами в ThreeTenABP.
import org.threeten.bp.ZonedDateTime
import org.threeten.bp.ZoneId
fun convertTimeZone(
time: ZonedDateTime,
targetZone: ZoneId
): ZonedDateTime {
return time.withZoneSameInstant(targetZone)
}
При підвищенні minSdk до 26 можна відмовитися від ThreeTenABP та перейти на вбудований java.time. Процес міграції включає кілька кроків та вимагає ретельного тестування.
Перший крок — заміна імпортів. Імпорти org.threeten.bp змінюються на java.time. У більшості випадків імена класів збігаються: LocalDate → java.time.LocalDate, ZonedDateTime → java.time.ZonedDateTime. Виняток становить DateTimeFormatter — в ThreeTenABP він в org.threeten.bp.format, в java.time — в java.time.format.
Другий крок — видалення ініціалізації. Рядок AndroidThreeTen.init(this) більше не потрібен, оскільки java.time вбудований в Android SDK. Видаліть виклик з Application.onCreate() та залежність з build.gradle.
Третій крок — заміна залежності на desugaring, якщо minSdk залишається нижче 26. Додайте isCoreLibraryDesugaringEnabled = true в compileOptions та залежність desugar_jdk_libs. Це забезпечить роботу java.time на старих API без ThreeTenABP. За даними Google I/O (2023), desugaring — кращий спосіб для нових проектів.
// build.gradle — замінити ThreeTenABP на desugaring
android {
compileOptions {
isCoreLibraryDesugaringEnabled = true
}
}
dependencies {
// Видалити: implementation "com.jakewharton.threetenabp:threetenabp:1.4.0"
// Додати:
"coreLibraryDesugaring"("com.android.tools:desugar_jdk_libs:2.1.4")
}
// Видалити AndroidThreeTen.init(this) з Application
Часті запитання
Технічно — так, але це не має сенсу. Якщо використовується desugaring, вбудований java.time вже доступний. Використання обох бібліотек призведе до дублювання коду та збільшення розміру APK. Виберіть один підхід для проекту.
Ініціалізація завантажує дані IANA Time Zone Database з assets в пам’ять. На стандартному JDK tzdata доступна через classpath, але Android використовує AssetManager. Метод init() копіює дані в системну директорію, роблячи їх доступними для ZoneId.
ThreeTenABP підтримує API 14+ (Android 4.0 Ice Cream Sandwich і вище). Для використання необхідна Java 8 compatibility (sourceCompatibility та targetCompatibility в compileOptions). На API 26+ бібліотека не потрібна — використовуйте вбудований java.time.
Часові пояси постачаються разом з бібліотекою. Версія 1.4.0 включає tzdata 2021a. Для оновлення потрібно оновити версію ThreeTenABP або вручну замінити tzdata в assets. Останні версії tzdata можна отримати з репозиторію IANA або через ThreeTen-Backport.
Для unit-тестів використовуйте AndroidThreeTen.init(context, zoneId) з явним зазначенням зони. Для Robolectric тестів — AndroidThreeTen.init(ApplicationProvider.getApplicationContext()). Для чистих JVM тестів без Android — використовуйте ThreeTen-Backport напряму без ThreeTenABP.
Підсумки
Ми розробимо мобільний застосунок під ключ
IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.
Читайте також