Android 上的 ThreeTen-Backport 错误 - ZoneRulesException:未注册时区数据文件

Posted

技术标签:

【中文标题】Android 上的 ThreeTen-Backport 错误 - ZoneRulesException:未注册时区数据文件【英文标题】:ThreeTen-Backport error on Android - ZoneRulesException: No time-zone data files registered 【发布时间】:2016-11-11 22:00:29 【问题描述】:

我正在为我的 android 项目使用 ThreeTen-Backport 库(因为 java.time 尚未在 android 开发中实现)。

当我写 LocalDate today=LocalDate.now();LocalTime time=LocalTime.now(); 时,我得到以下异常:

Caused by: org.threeten.bp.zone.ZoneRulesException: 
  No time-zone data files registered   
      at org.threeten.bp.zone.ZoneRulesProvider.getProvider(ZoneRulesProvider.java:176)
      at org.threeten.bp.zone.ZoneRulesProvider.getRules(ZoneRulesProvider.java:133)
      at org.threeten.bp.ZoneRegion.ofId(ZoneRegion.java:143)
      at org.threeten.bp.ZoneId.of(ZoneId.java:357)
      at org.threeten.bp.ZoneId.of(ZoneId.java:285)
      at org.threeten.bp.ZoneId.systemDefault(ZoneId.java:244)
      at org.threeten.bp.Clock.systemDefaultZone(Clock.java:137)
      at org.threeten.bp.LocalDate.now(LocalDate.java:165)

同一行代码在我拥有的另一个 java 项目中运行良好,该项目使用本机 java.time 库。

我搜索了一个可能的解决方案,但找不到任何有用的解决方案:一个解决方案建议我需要使用另一个包含时区规则的 jar,而另一个解决方案建议类路径中可能有两个或多个 ThreeTenBP 库。 这些情况与我的情况不符。

build.gradle 文件的依赖项部分,我尝试了一些配置:

一开始我用的是-compile 'com.jakewharton.threetenabp:threetenabp:1.0.3' 然后,我尝试了—— compile 'org.threeten:threetenbp:1.0.3' 在那之后,我尝试了—— compile 'org.threeten:threetenbp:1.3.1' 目前,我使用compile 'org.threeten:threetenbp:1.3.2'

我不知道那行代码有什么问题以及如何修复它。LocalDate.now()LocalTime.now() 方法应该可以在不指定时区的情况下工作。

【问题讨论】:

【参考方案1】:

对于 Android 项目,您应该使用

implementation 'com.jakewharton.threetenabp:threetenabp:1.0.3'

确保在使用库中的类之前调用​​AndroidThreeTen.init(this);。这将读取时区数据(包含在库中)。您可以在 onCreate 方法中的 Application 类中初始化库,就像在 README 中推荐的那样。

【讨论】:

谢谢,我的项目之前没有应用类,所以无法使用AndroidThreeTen.init(this);代码。我想在活动的onCreate 方法中添加它,但我发现它不正确。现在我创建了一个扩展应用程序类的新类,并将该行放在onCreate 方法中。现在一切都很好! 在 onCreate 中调用 init 违反了严格模式,甚至会延迟应用启动。如果你异步调用 init,你必须确保在它完成之前不要使用它,这对于 JUnit 测试来说可能很困难。 @ErikB 它确实违反了严格模式,因为它从资产中读取时区文件,因此会延迟启动时间。对于单元测试,您应该使用原始库 threeten.org/threetenbp,因为这个库具有 Android 依赖项,如此处所述 github.com/JakeWharton/ThreeTenABP/issues/14 如何在单元测试中初始化这个库? 你没有。您只需添加原始的单元测试 (testImplementation) 就可以了【参考方案2】:

你可以试试这个,而不是初始化库:

LocalDateEx.kt

object LocalDateEx 
    /**an alternative of LocalDate.now(), as it requires initialization using AndroidThreeTen.init(context), which takes a bit time (loads a file)*/
    @JvmStatic
    fun getNow(): LocalDate = Calendar.getInstance().toLocalDate()


fun Calendar.toLocalDate(): LocalDate = LocalDate.of(get(Calendar.YEAR), get(Calendar.MONTH) + 1, get(Calendar.DAY_OF_MONTH))

LocalTimeEx.kt

object LocalTimeEx 
    /**an alternative of LocalDateTime.now(), as it requires initialization using AndroidThreeTen.init(context), which takes a bit time (loads a file)*/
    @JvmStatic
    fun getNow(): LocalTime = Calendar.getInstance().toLocalTime()


fun Calendar.toLocalTime(): LocalTime = LocalTime.of(get(Calendar.HOUR_OF_DAY), get(Calendar.MINUTE), get(Calendar.SECOND), get(Calendar.MILLISECOND) * 1000000)

LocalDateTimeEx.kt

object LocalDateTimeEx 
    /**an alternative of LocalDateTime.now(), as it requires initialization using AndroidThreeTen.init(context), which takes a bit time (loads a file)*/
    @JvmStatic
    fun getNow(): LocalDateTime = Calendar.getInstance().toLocalDateTime()


private fun Calendar.toLocalDateTime(): LocalDateTime = LocalDateTime.of(get(Calendar.YEAR), get(Calendar.MONTH) + 1, get(Calendar.DAY_OF_MONTH), get(Calendar.HOUR_OF_DAY), get(Calendar.MINUTE), get(Calendar.SECOND),
        get(Calendar.MILLISECOND) * 1000000)

用法:

   val today=LocalDateEx.getNow()
   val today2=LocalTimeEx.getNow()
   val today3=LocalDateTimeEx.getNow()

【讨论】:

【参考方案3】:

对我来说,它不适用于启用 proguard 的签名版本构建。检查你的 proguard 规则是否包含

-keep class org.threeten.bp.zone.*

除非,添加它。它对我有帮助。

【讨论】:

【参考方案4】:

AGP 4.0 现在支持 Java 8 Desugar API,请参见此处:https://developer.android.com/studio/write/java8-support

因此您可以使用java.time. 而无需使用反向移植版本

注意事项:

如果您尝试支持低于 21 的 API 仍然存在问题(由 minSdk 21 解决):https://github.com/google/bundletool/issues/182#issuecomment-753269941

如官方文档中所述,脱糖还会增加构建时间

为了支持这些语言 API,插件会编译一个单独的 DEX 文件,其中包含缺失 API 的实现,并将其包含在您的应用中。脱糖过程会重写您的应用程序代码,以在运行时使用此库。

【讨论】:

【参考方案5】:

当我使用 API 25 时 java.time 不起作用。使用 Jake Wharton 的threetenadp 做到了。所以这里有一个小更新,可以在 Studio 2020.3.1 上使用它

在 build.gradle 模块中

dependencies 
 implementation 'com.jakewharton.threetenabp:threetenabp:1.3.1'


import com.jakewharton.threetenabp.AndroidThreeTen

 override fun onCreate(savedInstanceState: Bundle?) 
        super.onCreate(savedInstanceState)
    AndroidThreeTen.init(application);

当 IDE 要求导入时,请务必选择 com.jakewharton.threetenabp.AndroidThreeTen 而不是 java.time

LocalTime.now().toSecondOfDay() 是我需要的。

【讨论】:

以上是关于Android 上的 ThreeTen-Backport 错误 - ZoneRulesException:未注册时区数据文件的主要内容,如果未能解决你的问题,请参考以下文章

ListView 上的 LongPress 与 Android 上的超链接

Android 9 上的 android.database.sqlite.SQLiteCantOpenDatabaseException

为啥我不能从 Android 上的串行端口打开/写入?

Android 上的协程(第二部分):入门

Android 7.0 上的 Android WebView InflateException

为啥 Android 上的 JUnit 4 不工作?