Android的Java – 乔达date是缓慢的
在Android上使用Joda 1.6.2
以下代码挂起大约15秒。
DateTime dt = new DateTime();
最初发布这个postAndroid Java – Joda Date在Eclipse / Emulator中很慢 –
只是试了一遍,它仍然没有更好的。 有没有其他人有这个问题或知道如何解决它?
我也遇到了这个问题。 Jon Skeet的怀疑是正确的,问题在于时区的加载效率非常低,打开jar文件,然后阅读清单以获取这些信息。
但是,简单地调用DateTimeZone.setProvider([custom provider instance ...])
是不够的,因为由于我没有意义的原因,DateTimeZone有一个静态初始化程序,它调用getDefaultProvider()
。
为了完全安全,您可以在调用joda中的任何内容之前通过设置此系统属性来覆盖此默认值。
例如,在你的活动中,添加这个:
@Override public void onCreate(Bundle savedInstanceState) { System.setProperty("org.joda.time.DateTimeZone.Provider", "com.your.package.FastDateTimeZoneProvider"); }
那么你所要做的就是定义FastDateTimeZoneProvider
。 我写了以下内容:
package com.your.package; public class FastDateTimeZoneProvider implements Provider { public static final Set<String> AVAILABLE_IDS = new HashSet<String>(); static { AVAILABLE_IDS.addAll(Arrays.asList(TimeZone.getAvailableIDs())); } public DateTimeZone getZone(String id) { if (id == null) { return DateTimeZone.UTC; } TimeZone tz = TimeZone.getTimeZone(id); if (tz == null) { return DateTimeZone.UTC; } int rawOffset = tz.getRawOffset(); //sub-optimal. could be improved to only create a new Date every few minutes if (tz.inDaylightTime(new Date())) { rawOffset += tz.getDSTSavings(); } return DateTimeZone.forOffsetMillis(rawOffset); } public Set getAvailableIDs() { return AVAILABLE_IDS; } }
我已经testing了这个,它似乎可以在Android SDK 2.1+与卓达版本1.6.2。 它当然可以进一步优化,但在分析我的应用程序( mogwee )时,这将DateTimeZone初始化时间从〜500ms减less到〜18ms 。
如果您使用proguard来构build您的应用程序,则必须将此行添加到proguard.cfg,因为Joda希望类名完全按照您的指定:
-keep class com.your.package.FastDateTimeZoneProvider
我强烈怀疑这是因为它必须build立默认时区的ISO年代表,这可能涉及读取所有的时区信息。
你可以通过首先调用ISOChronology.getInstance()
来validation,然后再调用new DateTime()
。 我怀疑它会很快。
你知道哪个时区将在你的应用程序中相关吗? 你可能会发现你可以通过重buildJoda Time来缩短时区数据库的速度。 或者,调用DateTimeZone.setProvider()
与你自己的Provider
的实现,这并没有做太多的工作。
当然,这是值得检查的是,这实际上是否是第一个问题。您可能还想尝试显式传递UTC时区,这将不需要在时区数据库中读取…虽然您永远不知道什么时候会不小心触发了一个需要默认时区的呼叫,在这一点上,您将承担相同的费用。
我只需要在我的应用程序的UTC。 所以,遵循unchek的build议,我用了
System.setProperty("org.joda.time.DateTimeZone.Provider", "org.joda.time.tz.UTCProvider");
org.joda.time.tz.UTCProvider
实际上被JodaTime用作辅助备份,所以我想为什么不使用它作为主要用途? 到现在为止还挺好。 它加载速度很快。
如果您的date必须有精确的时区计算,那么耕耘者提供的最佳答案是不可靠的。 这是一个可能发生的问题的例子:
假设您的DateTime
对象设置为凌晨4:00,即当天夏令时开始后的一个小时。 当Joda在凌晨3点之前检查FastDateTimeZoneProvider
提供程序(即夏令时之前)时,它将得到一个带有错误偏移量的DateTimeZone
对象,因为tz.inDaylightTime(new Date())
检查将返回false。
我的解决scheme是采用最近出版的joda-time-android库 。 它使用Joda的核心,但确保只从原始文件夹根据需要加载时区。 用gradlebuild立起来很简单。 在你的项目中,扩展Application
类并在其onCreate()
添加以下内容:
public class MyApp extends Application { @Override public void onCreate() { super.onCreate(); JodaTimeAndroid.init(this); } }
作者去年写了一篇博文 。
我可以用joda的版本1,1.5和1.62来证实这个问题。 Date4J对我来说是很好的select。
我只是在几个设备上进行了@“Name is carl”的testing。 我必须注意到,testing不是完全有效的,结果是误导性的(因为它只反映了一个DateTime实例)。
-
在他的testing中,当比较DateTime到Date时,DateTime被迫parsingString ts,其中Date不parsing任何东西。
-
虽然DateTime的初始创build是准确的,但它只需要花费很多时间在第一次创build之后…每个实例之后是0ms(或非常接近0ms)
为了validation这一点,我使用了下面的代码,并在OLD Android 2.3设备上创build了1000个新的DateTime实例
int iterations = 1000; long totalTime = 0; // Test Joda Date for (int i = 0; i < iterations; i++) { long d1 = System.currentTimeMillis(); DateTime d = new DateTime(); long d2 = System.currentTimeMillis(); long duration = (d2 - d1); totalTime += duration; log.i(TAG, "datetime : " + duration); } log.i(TAG, "Average datetime : " + ((double) totalTime/ (double) iterations));
我的结果显示:
datetime:264 datetime:0 datetime:0 datetime:0 datetime:0 datetime:0 datetime:0 ... datetime:0 datetime:0 datetime:1 datetime:0 ... datetime:0 datetime:0 datetime:0
所以结果是第一次是264ms,超过95%以下是0ms(我偶尔有一个1ms,但从来没有超过1ms的值)。
希望能够更清楚地了解使用乔达的成本。
注:我正在使用乔达时间版本2.1
我find了解决scheme。 我加载UTC和默认时区。 所以加载非常快 我想在这种情况下,我需要捕获广播时区更改和重新加载默认时区。
public class FastDateTimeZoneProvider implements Provider { public static final Set<String> AVAILABLE_IDS = new HashSet<String>(); static { AVAILABLE_IDS.add("UTC"); AVAILABLE_IDS.add(TimeZone.getDefault().getID()); } public DateTimeZone getZone(String id) { int rawOffset = 0; if (id == null) { return DateTimeZone.getDefault(); } TimeZone tz = TimeZone.getTimeZone(id); if (tz == null) { return DateTimeZone.getDefault(); } rawOffset = tz.getRawOffset(); //sub-optimal. could be improved to only create a new Date every few minutes if (tz.inDaylightTime(new Date())) { rawOffset += tz.getDSTSavings(); } return DateTimeZone.forOffsetMillis(rawOffset); } public Set getAvailableIDs() { return AVAILABLE_IDS; } }
使用dlew / joda-time-android gradle依赖性,它只需要22.82毫秒(毫秒)。 所以我build议你使用它而不是重写任何东西。
这个快速的笔记来完成@Steven关于date4j的答案
我跑了一个快速和肮脏的基准比较java.util.Date
, jodatime
和date4j
我最弱的Android设备(macros达梦/蓝gem2.3.5)。
详细信息:正常生成(没有proguard),为FastDateTimeZoneProvider
实现FastDateTimeZoneProvider。
代码如下:
String ts = "2010-01-19T23:59:59.123456789"; long d1 = System.currentTimeMillis(); DateTime d = new DateTime(ts); long d2 = System.currentTimeMillis(); System.err.println("datetime : " + dateUtils.durationtoString(d2 - d1)); d1 = System.currentTimeMillis(); Date dd = new Date(); d2 = System.currentTimeMillis(); System.err.println("date : " + dateUtils.durationtoString(d2 - d1)); d1 = System.currentTimeMillis(); hirondelle.date4j.DateTime ddd = new hirondelle.date4j.DateTime(ts); d2 = System.currentTimeMillis(); System.err.println("date4j : " + dateUtils.durationtoString(d2 - d1));
结果如下:
debug | normal joda : 3s (3577ms) | 0s (284ms) date : 0s (0) | 0s (0s) date4j : 0s (55ms) | 0s (2ms)
最后一件事,jar子大小:
jodatime 2.1 : 558 kb date4j : 35 kb
我想我会给date4j一个尝试。