ThreeTenABP เป็นไลบรารีอะแดปเตอร์สำหรับ Android ที่ให้ API java.time (แพ็กเกจ org.threeten.bp) บนอุปกรณ์ที่ใช้ Android ต่ำกว่า 8 (API < 26) ตามข้อกำหนดของ Jake Wharton (GitHub, 2023) ไลบรารีนี้เป็น wrapper รอบโครงการ 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 ใช้ค่าคงที่ที่ไม่ intuitive (Calendar.JANUARY = 0) ทั้งสองคลาสไม่ปลอดภัยต่อเธรดและมีแนวโน้มที่จะเกิดข้อผิดพลาดเมื่อทำงานกับเขตเวลา
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+) โดยไม่ต้องรอการอัปเดตระบบปฏิบัติการ
การเพิ่มทำได้สองขั้นตอน: เพิ่ม dependencies ใน build.gradle (ระดับแอป) และเริ่มต้นในคลาส Application สำคัญ: ThreeTenABP ต้องการ compileSdk อย่างน้อย 21 และ Gradle เวอร์ชันอย่างน้อย 4.0
dependencies ถูกเพิ่มในส่วน dependencies: implementation "com.jakewharton.threetenabp:threetenabp:1.4.0" ตั้งแต่การเปิดตัว 1.4.0 ไลบรารียังไม่ได้รับการอัปเดต เนื่องจากมันเสถียรและครอบคลุมทุกกรณีที่จำเป็น
ตาม เอกสารทางการ ไลบรารีรวม tzdata ไว้ใน assets หากแอปมีโฟลเดอร์ assets พร้อมไฟล์อื่นอยู่แล้ว ThreeTenABP ทำงานร่วมกับไฟล์เหล่านั้นได้อย่างถูกต้อง ขนาดของ tzdata ประมาณ 200 KB เมื่อบีบอัด
// build.gradle (ระดับแอป)
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 แปลง bytecode เพื่อให้การเรียก java.time ทำงานบน API เก่าโดยไม่ต้องใช้ไลบรารีเพิ่มเติม
ข้อดีของ desugaring: ใช้แพ็กเกจ java.time ดั้งเดิม (ไม่ใช่ org.threeten.bp) ไม่ต้องเริ่มต้น ผสานรวมกับ Android Studio อย่างสมบูรณ์ ข้อเสีย: ต้องการ AGP 4.0+, เพิ่มเวลาในการ build, ขนาด APK อาจเพิ่มขึ้น 2-3 MB
ThreeTenABP ยังคงเป็นตัวเลือกที่ดีที่สุดสำหรับโปรเจกต์เดิมที่ไม่สามารถอัปเดต AGP เป็น 4.0+ หรือที่ขนาด APK มีความสำคัญ ThreeTenABP ยังตั้งค่าได้ง่ายกว่า — แค่ dependencies หนึ่งรายการและการเริ่มต้นหนึ่งบรรทัด ตาม Stack Overflow (2024) ประมาณ 30% ของโปรเจกต์ที่มี minSdk < 26 ยังคงใช้ ThreeTenABP แทน desugaring
ตัวอย่างแรกแสดงการทำงานกับวันที่โดยใช้ ThreeTenABP API เหมือนกับ java.time แต่ import มาจาก org.threeten.bp ซึ่งช่วยให้คุณเขียนโค้ดที่หลังจากการย้ายต้องการเพียงแค่เปลี่ยน import
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 ในตัว กระบวนการย้ายรวมหลายขั้นตอนและต้องการการทดสอบอย่างละเอียด
ขั้นตอนแรกคือการเปลี่ยน import การ import จาก 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() และ dependencies จาก build.gradle
ขั้นตอนที่สามคือการแทนที่ dependencies ด้วย desugaring หาก minSdk ยังคงต่ำกว่า 26 เพิ่ม isCoreLibraryDesugaringEnabled = true ใน compileOptions และ dependencies 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 (sourceCompatibility และ targetCompatibility ใน compileOptions) บน API 26+ ไม่จำเป็นต้องใช้ไลบรารี — ใช้ java.time ในตัว
เขตเวลามาพร้อมกับไลบรารี เวอร์ชัน 1.4.0 รวม tzdata 2021a ในการอัปเดต คุณต้องอัปเดตเวอร์ชัน ThreeTenABP หรือแทนที่ tzdata ใน assets ด้วยตนเอง เวอร์ชันล่าสุดของ tzdata สามารถหาได้จากพื้นที่เก็บ IANA หรือผ่าน ThreeTen-Backport
สำหรับการทดสอบหน่วย ใช้ AndroidThreeTen.init(context, zoneId) พร้อมการระบุเขตอย่างชัดเจน สำหรับการทดสอบ Robolectric — AndroidThreeTen.init(ApplicationProvider.getApplicationContext()) สำหรับการทดสอบ JVM บริสุทธิ์โดยไม่มี Android — ใช้ ThreeTen-Backport โดยตรงโดยไม่ต้องใช้ ThreeTenABP
สรุป
เราจะพัฒนาแอปพลิเคชันบนมือถือแบบครบวงจร
IT Sectr สร้างแอปพลิเคชัน iOS และ Android สำหรับสตาร์ทอัพและธุรกิจตั้งแต่ปี 2017 เราจะให้คำแนะนำและเสนอวิธีแก้ปัญหาที่ดีที่สุดแก่คุณ
อ่านเพิ่มเติม