ThreeTenABP é uma biblioteca adaptadora para Android que fornece a API java.time (pacote org.threeten.bp) em dispositivos com Android inferior ao 8 (API < 26). De acordo com a especificação de Jake Wharton (GitHub, 2023), a biblioteca é um wrapper sobre o projeto ThreeTen-Backport, adaptado para Android com otimização de recursos e suporte a tzdata através do AssetManager.
Pontos principais
ThreeTenABP (ThreeTen Android Backport) é uma biblioteca criada por Jake Wharton para usar a API de data/hora do Java 8 em versões antigas do Android. É um adaptador para o projeto ThreeTen-Backport, que porta java.time (JSR-310) para Java 7 e Android API < 26.
O principal problema que a biblioteca resolve: o Android antes da versão 8 (API 26) não incluía java.time na distribuição padrão. Os desenvolvedores eram forçados a usar java.util.Date/Calendar ou adicionar Joda-Time. O ThreeTenABP fornece a mesma API moderna que o java.time integrado, mas através do pacote org.threeten.bp.
De acordo com o repositório GitHub (2023), a biblioteca é otimizada para Android: os dados tzdata (IANA Time Zone Database) são armazenados em assets e carregados via AssetManager, em vez de classpath como no desktop. Isso reduz o tamanho do APK e acelera o carregamento.
A última versão estável é 1.4.0 (agosto de 2021). A biblioteca está em modo de manutenção, pois com a adoção generalizada do desugaring, a necessidade dela diminui, mas permanece relevante para projetos com API mínima < 26.
Antes do java.time ser introduzido no Java 8 (2014), os desenvolvedores usavam java.util.Date e java.util.Calendar. Essas classes têm sérias desvantagens: Date é mutável, Calendar usa constantes não intuitivas (Calendar.JANUARY = 0), ambas as classes não são thread-safe e são propensas a erros ao trabalhar com fusos horários.
Joda-Time era o padrão de facto antes do Java 8, mas seu criador Stephen Colebourne projetou java.time como a substituição oficial, baseado na experiência do Joda-Time e abordando suas deficiências. O pacote java.time foi incluído no JDK 8, mas o Android não o recebeu até a API 26.
ThreeTen-Backport é uma porta do java.time para Java 7, criada pelo mesmo autor (Stephen Colebourne). Inclui todas as principais classes: LocalDate, LocalTime, LocalDateTime, ZonedDateTime, Instant, Duration, Period, DateTimeFormatter. O ThreeTenABP adapta esta porta para Android, adicionando inicialização através de AssetsManager e otimização para dispositivos móveis.
Assim, o ThreeTenABP permite usar a API moderna de data/hora em dispositivos com Android 4.0+ (API 14+) sem esperar por uma atualização do sistema operacional.
A adição é feita em duas etapas: adicionar a dependência no build.gradle (nível do app) e inicializar na classe Application. Importante: o ThreeTenABP requer compileSdk de pelo menos 21 e versão do Gradle de pelo menos 4.0.
A dependência é adicionada na seção de dependências: implementation "com.jakewharton.threetenabp:threetenabp:1.4.0". Desde o lançamento da 1.4.0, a biblioteca não foi atualizada, pois é estável e cobre todos os casos necessários.
De acordo com a documentação oficial, a biblioteca inclui tzdata em assets. Se o aplicativo já tiver uma pasta assets com outros arquivos, o ThreeTenABP coexiste corretamente com eles. O tamanho do tzdata é de aproximadamente 200 KB compactado.
// build.gradle (nível do app)
android {
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
}
dependencies {
implementation "com.jakewharton.threetenabp:threetenabp:1.4.0"
}
Antes de usar qualquer classe do org.threeten.bp, a biblioteca deve ser inicializada. A inicialização é realizada uma vez em Application.onCreate() chamando AndroidThreeTen.init(this).
A inicialização carrega os dados tzdata dos assets e configura o relógio do sistema. Sem chamar init(), os métodos now() lançarão uma IllegalStateException com uma mensagem de que a biblioteca não foi inicializada.
Para testes, você pode usar AndroidThreeTen.init(applicationContext, zoneId) — uma sobrecarga com especificação explícita de fuso horário. Isso é útil para comportamento previsível em testes. Se apenas a inicialização básica sem tzdata for necessária, use AndroidThreeTen.initWithoutFiles(context).
class App : Application() {
override fun onCreate() {
super.onCreate()
AndroidThreeTen.init(this)
}
}
// Uso após a inicialização
val today = LocalDate.now()
val now = LocalDateTime.now()
O ThreeTenABP fornece todas as principais classes do java.time, mas no pacote org.threeten.bp. A API é praticamente idêntica ao java.time original, facilitando a migração ao mudar para API 26+.
Classes principais:
Classes auxiliares também são suportadas: Clock, DayOfWeek, Month, Year, YearMonth, MonthDay. Os fusos horários são fornecidos com a biblioteca (IANA tzdata). A versão do tzdata no ThreeTenABP 1.4.0 corresponde a 2021a.
A partir do Android Gradle Plugin 4.0 (2020) e desugar_jdk_libs, os desenvolvedores obtiveram a capacidade de usar java.time em todas as versões do Android através de coreLibraryDesugaring. O desugaring transforma o bytecode para que as chamadas java.time funcionem em APIs antigas sem bibliotecas adicionais.
Vantagens do desugaring: usa o pacote java.time original (não org.threeten.bp), não requer inicialização, integração completa com o Android Studio. Desvantagens: requer AGP 4.0+, aumenta o tempo de compilação, o tamanho do APK pode aumentar em 2-3 MB.
O ThreeTenABP continua sendo a melhor escolha para projetos legados que não podem atualizar o AGP para 4.0+, ou onde o tamanho do APK é crítico. O ThreeTenABP também é mais fácil de configurar — apenas uma dependência e uma linha de inicialização. De acordo com o Stack Overflow (2024), cerca de 30% dos projetos com minSdk < 26 ainda usam ThreeTenABP em vez de desugaring.
O primeiro exemplo demonstra o trabalho com datas usando o ThreeTenABP. A API é idêntica ao java.time, mas as importações vêm do org.threeten.bp. Isso permite escrever código que, após a migração, requer apenas a substituição das importações.
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)
}
O segundo exemplo mostra a formatação de datas. O DateTimeFormatter do org.threeten.bp funciona da mesma forma que no 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)
}
O terceiro exemplo demonstra o trabalho com ZonedDateTime e a conversão entre fusos horários no ThreeTenABP.
import org.threeten.bp.ZonedDateTime
import org.threeten.bp.ZoneId
fun convertTimeZone(
time: ZonedDateTime,
targetZone: ZoneId
): ZonedDateTime {
return time.withZoneSameInstant(targetZone)
}
Ao aumentar o minSdk para 26, você pode abandonar o ThreeTenABP e mudar para o java.time integrado. O processo de migração inclui várias etapas e requer testes completos.
O primeiro passo é substituir as importações. As importações de org.threeten.bp são alteradas para java.time. Na maioria dos casos, os nomes das classes coincidem: LocalDate → java.time.LocalDate, ZonedDateTime → java.time.ZonedDateTime. A exceção é o DateTimeFormatter — no ThreeTenABP está em org.threeten.bp.format, no java.time está em java.time.format.
O segundo passo é remover a inicialização. A linha AndroidThreeTen.init(this) não é mais necessária, pois o java.time está integrado ao Android SDK. Remova a chamada de Application.onCreate() e a dependência do build.gradle.
O terceiro passo é substituir a dependência pelo desugaring, se o minSdk permanecer abaixo de 26. Adicione isCoreLibraryDesugaringEnabled = true em compileOptions e a dependência desugar_jdk_libs. Isso garantirá que o java.time funcione em APIs antigas sem o ThreeTenABP. De acordo com o Google I/O (2023), o desugaring é a abordagem preferida para novos projetos.
// build.gradle — substituir ThreeTenABP por desugaring
android {
compileOptions {
isCoreLibraryDesugaringEnabled = true
}
}
dependencies {
// Remover: implementation "com.jakewharton.threetenabp:threetenabp:1.4.0"
// Adicionar:
"coreLibraryDesugaring"("com.android.tools:desugar_jdk_libs:2.1.4")
}
// Remover AndroidThreeTen.init(this) de Application
Perguntas frequentes
Tecnicamente — sim, mas não faz sentido. Se o desugaring for usado, o java.time integrado já está disponível. Usar ambas as bibliotecas levará à duplicação de código e aumento do tamanho do APK. Escolha uma abordagem para o seu projeto.
A inicialização carrega o IANA Time Zone Database dos assets para a memória. No JDK padrão, o tzdata está disponível via classpath, mas o Android usa AssetManager. O método init() copia os dados para o diretório do sistema, tornando-os disponíveis para o ZoneId.
O ThreeTenABP suporta API 14+ (Android 4.0 Ice Cream Sandwich e superior). É necessária compatibilidade com Java 8 (sourceCompatibility e targetCompatibility em compileOptions). Na API 26+, a biblioteca não é necessária — use o java.time integrado.
Os fusos horários são fornecidos com a biblioteca. A versão 1.4.0 inclui tzdata 2021a. Para atualizar, você precisa atualizar a versão do ThreeTenABP ou substituir manualmente o tzdata em assets. As versões mais recentes do tzdata podem ser obtidas do repositório IANA ou através do ThreeTen-Backport.
Para testes unitários, use AndroidThreeTen.init(context, zoneId) com uma especificação explícita de zona. Para testes Robolectric — AndroidThreeTen.init(ApplicationProvider.getApplicationContext()). Para testes JVM puros sem Android — use o ThreeTen-Backport diretamente sem o ThreeTenABP.
Resumo
Vamos desenvolver um aplicativo móvel chave na mão
A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.
Leia também