ThreeTenABP — bibliotecă adaptor pentru Android, care oferă API java.time (pachetul org.threeten.bp) pe dispozitive cu Android sub 8 (API < 26). Conform specificației Jake Wharton (GitHub, 2023), biblioteca este un înveliș peste proiectul ThreeTen-Backport, adaptat pentru Android ținând cont de optimizarea resurselor și suportul tzdata prin AssetManager.
Principalele puncte
ThreeTenABP (ThreeTen Android Backport) — o bibliotecă creată de Jake Wharton pentru utilizarea API-ului de dată/oră Java 8 pe versiunile vechi de Android. Este un adaptor pentru proiectul ThreeTen-Backport, care portează java.time (JSR-310) pe Java 7 și Android API < 26.
Problema principală pe care o rezolvă biblioteca: Android până la versiunea 8 (API 26) nu includea java.time în setul standard. Dezvoltatorii erau forțați să folosească java.util.Date/Calendar sau să adauge Joda-Time. ThreeTenABP oferă același API modern ca și java.time încorporat, dar prin pachetul org.threeten.bp.
Conform repository-ului GitHub (2023), biblioteca este optimizată pentru Android: datele tzdata (IANA Time Zone Database) sunt stocate în assets și încărcate prin AssetManager, nu prin classpath ca pe desktop. Aceasta reduce dimensiunea APK și accelerează încărcarea.
Ultima versiune stabilă — 1.4.0 (august 2021). Biblioteca este în modul de suport, deoarece odată cu răspândirea largă a desugaring necesitatea ei scade, dar rămâne relevantă pentru proiectele cu API minim < 26.
Înainte de apariția java.time în Java 8 (2014), dezvoltatorii foloseau java.util.Date și java.util.Calendar. Aceste clase au defecte serioase: Date este mutabil, Calendar folosește constante neintuitive (Calendar.JANUARY = 0), ambele clase nu sunt thread-safe și sunt predispuse la erori la lucrul cu fusele orare.
Joda-Time era standardul de facto înainte de Java 8, dar creatorul său Stephen Colebourne a proiectat java.time ca înlocuitor oficial, bazat pe experiența Joda-Time și ținând cont de deficiențele sale. Pachetul java.time a intrat în JDK 8, dar Android nu l-a primit până la API 26.
ThreeTen-Backport — este un port al java.time pe Java 7, creat de același autor (Stephen Colebourne). Include toate clasele principale: LocalDate, LocalTime, LocalDateTime, ZonedDateTime, Instant, Duration, Period, DateTimeFormatter. ThreeTenABP adaptează acest port pentru Android, adăugând inițializarea prin AssetsManager și optimizarea pentru dispozitive mobile.
Astfel, ThreeTenABP permite utilizarea API-ului modern de dată/oră pe dispozitive cu Android 4.0+ (API 14+) fără a aștepta actualizarea sistemului de operare.
Conectarea se face în doi pași: adăugarea dependenței în build.gradle (app-level) și inițializarea în clasa Application. Important: ThreeTenABP necesită compileSdk nu mai mic de 21 și Gradle versiunea nu mai mică de 4.0.
Dependența se adaugă în secțiunea dependencies: implementation „com.jakewharton.threetenabp:threetenabp:1.4.0”. De la lansarea 1.4.0, biblioteca nu a fost actualizată, deoarece este stabilă și acoperă toate cazurile necesare.
Conform documentației oficiale, biblioteca include tzdata în assets. Dacă aplicația are deja un folder assets cu alte fișiere, ThreeTenABP coexistă corect cu ele. Dimensiunea tzdata este de aproximativ 200 KB în formă comprimată.
// build.gradle (nivel aplicație)
android {
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
}
dependencies {
implementation "com.jakewharton.threetenabp:threetenabp:1.4.0"
}
Înainte de utilizarea oricărei clase din org.threeten.bp, trebuie inițializată biblioteca. Inițializarea se face o dată în Application.onCreate() prin apelul AndroidThreeTen.init(this).
Inițializarea încarcă datele tzdata din assets și configurează ceasul sistemului. Fără apelul init(), metodele now() vor arunca excepția IllegalStateException cu mesajul că biblioteca nu este inițializată.
Pentru teste se poate folosi AndroidThreeTen.init(applicationContext, zoneId) — suprascrierea cu specificarea explicită a fusului orar. Acest lucru este util pentru un comportament previzibil al testelor. Dacă este necesară doar inițializarea de bază fără tzdata — folosiți AndroidThreeTen.initWithoutFiles(context).
class App : Application() {
override fun onCreate() {
super.onCreate()
AndroidThreeTen.init(this)
}
}
// Utilizare după inițializare
val today = LocalDate.now()
val now = LocalDateTime.now()
ThreeTenABP oferă toate clasele principale java.time, dar în pachetul org.threeten.bp. API-ul este practic identic cu java.time original, ceea ce simplifică migrarea la API 26+.
Clase principale:
Sunt suportate și clasele auxiliare: Clock, DayOfWeek, Month, Year, YearMonth, MonthDay. Fusele orare sunt furnizate împreună cu biblioteca (IANA tzdata). Versiunea tzdata în ThreeTenABP 1.4.0 corespunde cu 2021a.
Începând cu Android Gradle Plugin 4.0 (2020) și desugar_jdk_libs, dezvoltatorii au primit posibilitatea de a folosi java.time pe toate versiunile de Android prin coreLibraryDesugaring. Desugaring transformă bytecode-ul astfel încât apelurile java.time funcționează pe API-uri vechi fără biblioteci suplimentare.
Avantajele desugaring: se folosește pachetul original java.time (nu org.threeten.bp), nu necesită inițializare, integrare completă cu Android Studio. Dezavantaje: necesită AGP 4.0+, adaugă timp de compilare, dimensiunea APK poate crește cu 2-3 MB.
ThreeTenABP rămâne cea mai bună alegere pentru proiectele legacy care nu pot actualiza AGP la 4.0+, sau unde dimensiunea APK este critică. De asemenea, ThreeTenABP este mai simplu de configurat — o singură dependență și o linie de inițializare. Conform Stack Overflow (2024), aproximativ 30% din proiectele cu minSdk < 26 încă folosesc ThreeTenABP în loc de desugaring.
Primul exemplu — lucrul cu date prin ThreeTenABP. API-ul este identic cu java.time, dar importurile vin din org.threeten.bp. Acest lucru permite scrierea de cod care după migrare necesită doar înlocuirea importurilor.
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)
}
Al doilea exemplu — formatarea datei. DateTimeFormatter din org.threeten.bp funcționează la fel ca în 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)
}
Al treilea exemplu — lucrul cu ZonedDateTime și conversia între fuse orare în ThreeTenABP.
import org.threeten.bp.ZonedDateTime
import org.threeten.bp.ZoneId
fun convertTimeZone(
time: ZonedDateTime,
targetZone: ZoneId
): ZonedDateTime {
return time.withZoneSameInstant(targetZone)
}
La creșterea minSdk la 26 se poate renunța la ThreeTenABP și trece la java.time încorporat. Procesul de migrare include mai mulți pași și necesită testare atentă.
Primul pas — înlocuirea importurilor. Importurile org.threeten.bp se schimbă în java.time. În majoritatea cazurilor numele claselor coincid: LocalDate → java.time.LocalDate, ZonedDateTime → java.time.ZonedDateTime. Excepție face DateTimeFormatter — în ThreeTenABP este în org.threeten.bp.format, în java.time — în java.time.format.
Al doilea pas — eliminarea inițializării. Linia AndroidThreeTen.init(this) nu mai este necesară, deoarece java.time este încorporat în Android SDK. Ștergeți apelul din Application.onCreate() și dependența din build.gradle.
Al treilea pas — înlocuirea dependenței cu desugaring, dacă minSdk rămâne sub 26. Adăugați isCoreLibraryDesugaringEnabled = true în compileOptions și dependența desugar_jdk_libs. Aceasta va asigura funcționarea java.time pe API-uri vechi fără ThreeTenABP. Conform Google I/O (2023), desugaring este metoda preferată pentru proiecte noi.
// build.gradle — înlocuiește ThreeTenABP cu desugaring
android {
compileOptions {
isCoreLibraryDesugaringEnabled = true
}
}
dependencies {
// Șterge: implementation „com.jakewharton.threetenabp:threetenabp:1.4.0”
// Adaugă:
"coreLibraryDesugaring"("com.android.tools:desugar_jdk_libs:2.1.4")
}
// Șterge AndroidThreeTen.init(this) din Application
Întrebări frecvente
Tehnic — da, dar nu are sens. Dacă se folosește desugaring, java.time încorporat este deja disponibil. Utilizarea ambelor biblioteci va duce la duplicarea codului și creșterea dimensiunii APK. Alegeți o singură abordare pentru proiect.
Inițializarea încarcă datele IANA Time Zone Database din assets în memorie. Pe JDK standard, tzdata este disponibilă prin classpath, dar Android folosește AssetManager. Metoda init() copiază datele în directorul sistemului, făcându-le disponibile pentru ZoneId.
ThreeTenABP suportă API 14+ (Android 4.0 Ice Cream Sandwich și mai sus). Pentru utilizare este necesară Java 8 compatibility (sourceCompatibility și targetCompatibility în compileOptions). Pe API 26+ biblioteca nu este necesară — folosiți java.time încorporat.
Fusele orare sunt furnizate împreună cu biblioteca. Versiunea 1.4.0 include tzdata 2021a. Pentru actualizare, trebuie actualizată versiunea ThreeTenABP sau înlocuit manual tzdata în assets. Ultimele versiuni de tzdata pot fi obținute din repository-ul IANA sau prin ThreeTen-Backport.
Pentru teste unitare, folosiți AndroidThreeTen.init(context, zoneId) cu specificarea explicită a zonei. Pentru teste Robolectric — AndroidThreeTen.init(ApplicationProvider.getApplicationContext()). Pentru teste JVM pure fără Android — folosiți direct ThreeTen-Backport fără ThreeTenABP.
Concluzii
Vom dezvolta o aplicație mobilă la cheie
IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.
Citiți și