ThreeTenABP — biblioteka-adapter dla Android, udostępniająca API java.time (pakiet org.threeten.bp) na urządzeniach z Androidem poniżej 8 (API < 26). Według specyfikacji Jake Wharton (GitHub, 2023), biblioteka jest nakładką na projekt ThreeTen-Backport, zaadaptowaną dla Android z uwzględnieniem optymalizacji zasobów i obsługi tzdata przez AssetManager.
Najważniejsze
ThreeTenABP (ThreeTen Android Backport) — biblioteka stworzona przez Jake’a Whartona do używania API daty/czasu Java 8 na starszych wersjach Androida. Jest adapterem dla projektu ThreeTen-Backport, który przenosi java.time (JSR-310) na Java 7 i Android API < 26.
Główny problem, który rozwiązuje biblioteka: Android przed wersją 8 (API 26) nie zawierał java.time w standardowym zestawie. Deweloperzy byli zmuszeni używać java.util.Date/Calendar lub podłączać Joda-Time. ThreeTenABP zapewnia ten sam nowoczesny API co wbudowany java.time, ale przez pakiet org.threeten.bp.
Według repozytorium GitHub (2023), biblioteka jest zoptymalizowana dla Android: dane tzdata (IANA Time Zone Database) są przechowywane w assets i ładowane przez AssetManager, a nie przez classpath jak na desktopie. Zmniejsza to rozmiar APK i przyspiesza ładowanie.
Ostatnia stabilna wersja to 1.4.0 (sierpień 2021). Biblioteka jest w trybie wsparcia, ponieważ wraz z powszechnym rozpowszechnieniem desugaring zapotrzebowanie na nią maleje, ale pozostaje aktualna dla projektów z minimalnym API < 26.
Przed pojawieniem się java.time w Java 8 (2014) deweloperzy używali java.util.Date i java.util.Calendar. Klasy te mają poważne wady: Date jest mutowalny, Calendar używa nieintuicyjnych stałych (Calendar.JANUARY = 0), obie klasy nie są thread-safe i są podatne na błędy przy pracy ze strefami czasowymi.
Joda-Time była de facto standardem przed Java 8, ale jej twórca Stephen Colebourne zaprojektował java.time jako oficjalny zamiennik, oparty na doświadczeniach Joda-Time i uwzględniający jej wady. Pakiet java.time wszedł w skład JDK 8, ale Android nie otrzymał go aż do API 26.
ThreeTen-Backport to port java.time na Java 7, stworzony przez tego samego autora (Stephen Colebourne). Obejmuje wszystkie główne klasy: LocalDate, LocalTime, LocalDateTime, ZonedDateTime, Instant, Duration, Period, DateTimeFormatter. ThreeTenABP adaptuje ten port dla Android, dodając inicjalizację przez AssetsManager i optymalizację dla urządzeń mobilnych.
Tak więc ThreeTenABP umożliwia używanie nowoczesnego API daty/czasu na urządzeniach z Android 4.0+ (API 14+) bez konieczności oczekiwania na aktualizację systemu.
Podłączenie odbywa się w dwóch krokach: dodanie zależności w build.gradle (app-level) i inicjalizacja w klasie Application. Ważne: ThreeTenABP wymaga compileSdk nie niższego niż 21 i Gradle w wersji nie niższej niż 4.0.
Zależność dodaje się w sekcji dependencies: implementation „com.jakewharton.threetenabp:threetenabp:1.4.0”. Od wydania 1.4.0 biblioteka nie była aktualizowana, ponieważ jest stabilna i pokrywa wszystkie potrzebne przypadki.
Według oficjalnej dokumentacji, biblioteka zawiera tzdata w assets. Jeśli aplikacja ma już folder assets z innymi plikami, ThreeTenABP poprawnie z nimi współistnieje. Rozmiar tzdata to około 200 KB w postaci skompresowanej.
// build.gradle (poziom aplikacji)
android {
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
}
dependencies {
implementation "com.jakewharton.threetenabp:threetenabp:1.4.0"
}
Przed użyciem dowolnej klasy z org.threeten.bp należy zainicjalizować bibliotekę. Inicjalizacja odbywa się raz w Application.onCreate() przez wywołanie AndroidThreeTen.init(this).
Inicjalizacja ładuje dane tzdata z assets i konfiguruje zegar systemowy. Bez wywołania init() metody now() będą zgłaszać wyjątek IllegalStateException z informacją, że biblioteka nie została zainicjalizowana.
Do testów można użyć AndroidThreeTen.init(applicationContext, zoneId) — przeciążenie z jawnym określeniem strefy czasowej. Jest to przydatne do przewidywalnego zachowania testów. Jeśli potrzebna jest tylko podstawowa inicjalizacja bez tzdata — użyj AndroidThreeTen.initWithoutFiles(context).
class App : Application() {
override fun onCreate() {
super.onCreate()
AndroidThreeTen.init(this)
}
}
// Użycie po inicjalizacji
val today = LocalDate.now()
val now = LocalDateTime.now()
ThreeTenABP udostępnia wszystkie główne klasy java.time, ale w pakiecie org.threeten.bp. API jest praktycznie identyczne z oryginalnym java.time, co ułatwia migrację przy przejściu na API 26+.
Główne klasy:
Obsługiwane są również klasy pomocnicze: Clock, DayOfWeek, Month, Year, YearMonth, MonthDay. Strefy czasowe są dostarczane wraz z biblioteką (IANA tzdata). Wersja tzdata w ThreeTenABP 1.4.0 odpowiada 2021a.
Począwszy od Android Gradle Plugin 4.0 (2020) i desugar_jdk_libs, deweloperzy otrzymali możliwość używania java.time na wszystkich wersjach Androida przez coreLibraryDesugaring. Desugaring transformuje kod bajtowy w taki sposób, że wywołania java.time działają na starych API bez dodatkowych bibliotek.
Zalety desugaring: używany jest oryginalny pakiet java.time (a nie org.threeten.bp), nie wymaga inicjalizacji, pełna integracja z Android Studio. Wady: wymaga AGP 4.0+, zwiększa czas budowania, rozmiar APK może wzrosnąć o 2–3 MB.
ThreeTenABP pozostaje najlepszym wyborem dla projektów legacy, które nie mogą zaktualizować AGP do 4.0+, lub gdzie krytyczny jest rozmiar APK. ThreeTenABP jest również łatwiejszy w konfiguracji — wystarczy jedna zależność i jedna linia inicjalizacji. Według Stack Overflow (2024), około 30% projektów z minSdk < 26 nadal używa ThreeTenABP zamiast desugaring.
Pierwszy przykład — praca z datami przez ThreeTenABP. API jest identyczne z java.time, ale importy pochodzą z org.threeten.bp. Pozwala to pisać kod, który po migracji wymaga tylko zmiany importów.
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)
}
Drugi przykład — formatowanie daty. DateTimeFormatter z org.threeten.bp działa tak samo jak w 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)
}
Trzeci przykład — praca z ZonedDateTime i konwersja między strefami czasowymi w ThreeTenABP.
import org.threeten.bp.ZonedDateTime
import org.threeten.bp.ZoneId
fun convertTimeZone(
time: ZonedDateTime,
targetZone: ZoneId
): ZonedDateTime {
return time.withZoneSameInstant(targetZone)
}
Po podniesieniu minSdk do 26 można zrezygnować z ThreeTenABP i przejść na wbudowany java.time. Proces migracji obejmuje kilka kroków i wymaga dokładnego testowania.
Pierwszy krok — zamiana importów. Importy org.threeten.bp zmieniają się na java.time. W większości przypadków nazwy klas są zgodne: LocalDate → java.time.LocalDate, ZonedDateTime → java.time.ZonedDateTime. Wyjątkiem jest DateTimeFormatter — w ThreeTenABP jest w org.threeten.bp.format, w java.time — w java.time.format.
Drugi krok — usunięcie inicjalizacji. Linia AndroidThreeTen.init(this) nie jest już potrzebna, ponieważ java.time jest wbudowany w Android SDK. Usuń wywołanie z Application.onCreate() i zależność z build.gradle.
Trzeci krok — zamiana zależności na desugaring, jeśli minSdk pozostaje poniżej 26. Dodaj isCoreLibraryDesugaringEnabled = true w compileOptions i zależność desugar_jdk_libs. Zapewni to działanie java.time na starych API bez ThreeTenABP. Według Google I/O (2023), desugaring to preferowany sposób dla nowych projektów.
// build.gradle — zastąp ThreeTenABP desugaring
android {
compileOptions {
isCoreLibraryDesugaringEnabled = true
}
}
dependencies {
// Usuń: implementation „com.jakewharton.threetenabp:threetenabp:1.4.0”
// Dodaj:
"coreLibraryDesugaring"("com.android.tools:desugar_jdk_libs:2.1.4")
}
// Usuń AndroidThreeTen.init(this) z Application
Często zadawane pytania
Technicznie — tak, ale nie ma to sensu. Jeśli używany jest desugaring, wbudowany java.time jest już dostępny. Używanie obu bibliotek doprowadzi do duplikacji kodu i zwiększenia rozmiaru APK. Wybierz jedno podejście dla projektu.
Inicjalizacja ładuje dane IANA Time Zone Database z assets do pamięci. Na standardowym JDK tzdata jest dostępna przez classpath, ale Android używa AssetManager. Metoda init() kopiuje dane do katalogu systemowego, udostępniając je dla ZoneId.
ThreeTenABP obsługuje API 14+ (Android 4.0 Ice Cream Sandwich i nowsze). Do użycia wymagana jest Java 8 compatibility (sourceCompatibility i targetCompatibility w compileOptions). Na API 26+ biblioteka nie jest potrzebna — używaj wbudowanego java.time.
Strefy czasowe są dostarczane wraz z biblioteką. Wersja 1.4.0 zawiera tzdata 2021a. Do aktualizacji należy zaktualizować wersję ThreeTenABP lub ręcznie zastąpić tzdata w assets. Najnowsze wersje tzdata można uzyskać z repozytorium IANA lub przez ThreeTen-Backport.
Do testów jednostkowych używaj AndroidThreeTen.init(context, zoneId) z jawnym określeniem strefy. Do testów Robolectric — AndroidThreeTen.init(ApplicationProvider.getApplicationContext()). Do testów JVM bez Androida — używaj ThreeTen-Backport bezpośrednio bez ThreeTenABP.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również