Android Java-Joda Date가 느립니다.
Android에서 Joda 1.6.2 사용
다음 코드는 약 15 초 동안 멈 춥니 다.
DateTime dt = new DateTime();
원래이 게시물을 게시했습니다 Android Java-Joda Date is slow in 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;
}
}
나는 이것을 테스트했으며 joda 버전 1.6.2와 함께 Android SDK 2.1 이상에서 작동하는 것으로 보입니다. 물론 더 최적화 할 수 있지만 내 앱 ( mogwee ) 을 프로파일 링하는 동안 DateTimeZone 초기화 시간이 ~ 500ms에서 ~ 18ms로 감소했습니다.
proguard를 사용하여 앱을 빌드하는 경우 Joda는 클래스 이름이 지정한 것과 정확히 일치 할 것으로 예상하므로 proguard.cfg에이 줄을 추가해야합니다.
-keep class com.your.package.FastDateTimeZoneProvider
기본 시간대에 대한 ISO 연대기를 작성해야하므로 모든 시간대 정보를 읽어야하기 때문이라고 강력히 의심 합니다.
ISOChronology.getInstance()
처음 으로 호출 한 다음에 대한 후속 호출 시간을 측정하여이를 확인할 수 new DateTime()
있습니다. 나는 그것이 빠를 것이라고 생각 한다.
귀하의 애플리케이션에 어떤 시간대가 관련 될지 알고 있습니까? 당신은 할 수 있습니다 당신이 아주 많이 감소 시간대 데이터베이스와 Joda 시간을 재 구축하여 모든 것을 더 빨리 할 수 있습니다 찾을 수 있습니다. 또는 많은 작업을 수행하지 않는 DateTimeZone.setProvider()
자체 구현으로 호출 하십시오 Provider
.
물론 그것이 실제로 문제인지 먼저 확인하는 것이 좋습니다. :) UTC 시간대를 명시 적으로 전달해 볼 수도 있습니다. 시간대 데이터베이스에서 읽을 필요가 없습니다. 실수로 기본 시간대 가 필요한 통화를 트리거 하면 동일한 비용이 발생합니다.
내 응용 프로그램에는 UTC 만 필요합니다. 그래서 unchek의 조언에 따라
System.setProperty("org.joda.time.DateTimeZone.Provider", "org.joda.time.tz.UTCProvider");
org.joda.time.tz.UTCProvider
실제로 JodaTime에서 보조 백업으로 사용하므로 기본 용도로 사용하지 않는 이유는 무엇입니까? 여태까지는 그런대로 잘됐다. 빠르게로드됩니다.
날짜에 대한 정확한 시간대 계산이 필요한 경우 plowman이 제공하는 최고 답변은 신뢰할 수 없습니다. 다음은 발생할 수있는 문제의 예입니다.
DateTime
그날 일광 절약이 시작된 지 한 시간 후인 오전 4시에 개체가 설정되어 있다고 가정합니다 . Joda가 FastDateTimeZoneProvider
오전 3시 이전에 (즉, 일광 절약 시간 전에) 공급자를 확인하면 검사가 false를 반환 DateTimeZone
하므로 오프셋이 잘못된 개체를 가져 tz.inDaylightTime(new Date())
옵니다.
내 해결책은 최근에 게시 된 joda-time-android 라이브러리 를 채택하는 것이 었습니다 . Joda의 핵심을 사용하지만 원시 폴더에서 필요한 경우에만 시간대를로드합니다. Gradle을 사용하면 설정이 쉽습니다. 프로젝트에서 Application
클래스를 확장 하고 다음을 추가하십시오 onCreate()
.
public class MyApp extends Application {
@Override
public void onCreate() {
super.onCreate();
JodaTimeAndroid.init(this);
}
}
저자는 작년에 그것에 대한 블로그 게시물을 썼습니다 .
이 문제는 joda의 버전 1, 1.5 및 1.62에서 확인할 수 있습니다. Date4J는 대안으로 나를 위해 잘 작동합니다.
방금 여러 장치에서 @ "Name is carl"이 게시 한 테스트를 수행했습니다. 테스트가 완전히 유효하지 않으며 결과가 오해의 소지가 있다는 점에 유의해야합니다 (DateTime의 단일 인스턴스 만 반영한다는 점에서).
그의 테스트에서 DateTime을 Date와 비교할 때 DateTime은 String ts를 구문 분석해야하는데, 여기서 Date는 아무것도 구문 분석하지 않습니다.
DateTime의 초기 생성은 정확했지만 첫 번째 생성에만 그 정도의 시간이 걸립니다. 그 이후의 모든 인스턴스는 0ms (또는 거의 0ms)였습니다.
이를 확인하기 위해 다음 코드를 사용하고 OLD Android 2.3 기기에서 DateTime의 새 인스턴스 1000 개를 만들었습니다.
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));
내 결과는 다음과 같습니다.
날짜 시간 : 264 날짜 시간 : 0 날짜 시간 : 0 날짜 시간 : 0 날짜 시간 : 0 날짜 시간 : 0 날짜 시간 : 0 ... 날짜 시간 : 0 날짜 시간 : 0 날짜 시간 : 1 날짜 시간 : 0 ... 날짜 시간 : 0 날짜 시간 : 0 날짜 시간 : 0
따라서 결과는 첫 번째 인스턴스가 264ms이고 다음 중 95 % 이상이 0ms였습니다 (때때로 1ms가 있었지만 1ms보다 큰 값은 없었습니다).
이것이 Joda 사용 비용에 대한 명확한 그림을 제공하기를 바랍니다.
참고 : joda-time 버전 2.1을 사용하고있었습니다.
dlew / joda-time-android gradle 종속성을 사용하면 22.82ms (밀리 초) 만 걸립니다. 그래서 나는 그것을 재정의하는 대신 사용하는 것이 좋습니다.
나는 나를 위해 해결책을 찾았다. UTC와 기본 시간대를로드합니다. 따라서로드가 매우 빠릅니다. 이 경우 방송 TIME ZONE CHANGE를 잡아서 기본 시간대를 다시로드해야한다고 생각합니다.
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;
}
}
@Steven의 date4j에 대한 답변을 완성하는이 빠른 메모
나는 비교하는 신속하고 더러운 벤치 마크를 실행 java.util.Date
, jodatime
그리고 date4j
가장 약한 안드로이드 장치에 나는 (HTC 드림 / 사파이어 2.3.5)를 가지고있다.
세부 정보 : 일반 빌드 ( FastDateTimeZoneProvider
프로 가드 없음), jodatime 에 대한 구현 .
코드는 다음과 같습니다.
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)
마지막으로, 항아리 크기 :
jodatime 2.1 : 558 kb
date4j : 35 kb
나는 date4j를 시도해 볼 것이라고 생각합니다.
또한 체크 아웃 할 수 제이크 와튼의 JSR-310 백 포트 java.time의를. * 패키지를.
이 라이브러리는 시간대 정보를 표준 Android 애셋으로 배치하고이를 효율적으로 파싱하기위한 커스텀 로더를 제공합니다. [It]은 바이너리 크기와 메서드 수뿐만 아니라 API 크기에서도 훨씬 작은 패키지로 Java 8의 표준 API를 제공합니다.
따라서이 솔루션은 Timezone 데이터를위한 효율적인 로더와 결합 된 더 작은 메서드 수 풋 프린트로 더 작은 바이너리 크기 라이브러리를 제공합니다.
- 이미 언급했듯이 joda-time-android 라이브러리를 사용할 수 있습니다 .
FastDateTimeZoneProvider
@ElijahSh 및 @plowman이 제안한 것을 사용하지 마십시오 . DST 오프셋을 선택한 시간대에 대한 표준 오프셋으로 취급하기 때문입니다. 다음 DST 전환이 발생하기 전 오늘과 나머지 반년 동안 "올바른"결과를 제공합니다. 그러나 DST 전환 전날과 다음 DST 전환 다음날에 잘못된 결과를 제공합니다.JodaTime으로 시스템의 시간대를 활용하는 올바른 방법 :
공용 클래스 AndroidDateTimeZoneProvider는 org.joda.time.tz.Provider {를 구현합니다.
@Override public Set<String> getAvailableIDs() { return new HashSet<>(Arrays.asList(TimeZone.getAvailableIDs())); } @Override public DateTimeZone getZone(String id) { return id == null ? null : id.equals("UTC") ? DateTimeZone.UTC : Build.VERSION.SDK_INT >= Build.VERSION_CODES.N ? new AndroidNewDateTimeZone(id) : new AndroidOldDateTimeZone(id); }
}
AndroidOldDateTimeZone :
public class AndroidOldDateTimeZone extends DateTimeZone {
private final TimeZone mTz;
private final Calendar mCalendar;
private long[] mTransition;
public AndroidOldDateTimeZone(final String id) {
super(id);
mTz = TimeZone.getTimeZone(id);
mCalendar = GregorianCalendar.getInstance(mTz);
mTransition = new long[0];
try {
final Class tzClass = mTz.getClass();
final Field field = tzClass.getDeclaredField("mTransitions");
field.setAccessible(true);
final Object transitions = field.get(mTz);
if (transitions instanceof long[]) {
mTransition = (long[]) transitions;
} else if (transitions instanceof int[]) {
final int[] intArray = (int[]) transitions;
final int size = intArray.length;
mTransition = new long[size];
for (int i = 0; i < size; i++) {
mTransition[i] = intArray[i];
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public TimeZone getTz() {
return mTz;
}
@Override
public long previousTransition(final long instant) {
if (mTransition.length == 0) {
return instant;
}
final int index = findTransitionIndex(instant, false);
if (index <= 0) {
return instant;
}
return mTransition[index - 1] * 1000;
}
@Override
public long nextTransition(final long instant) {
if (mTransition.length == 0) {
return instant;
}
final int index = findTransitionIndex(instant, true);
if (index > mTransition.length - 2) {
return instant;
}
return mTransition[index + 1] * 1000;
}
@Override
public boolean isFixed() {
return mTransition.length > 0 &&
mCalendar.getMinimum(Calendar.DST_OFFSET) == mCalendar.getMaximum(Calendar.DST_OFFSET) &&
mCalendar.getMinimum(Calendar.ZONE_OFFSET) == mCalendar.getMaximum(Calendar.ZONE_OFFSET);
}
@Override
public boolean isStandardOffset(final long instant) {
mCalendar.setTimeInMillis(instant);
return mCalendar.get(Calendar.DST_OFFSET) == 0;
}
@Override
public int getStandardOffset(final long instant) {
mCalendar.setTimeInMillis(instant);
return mCalendar.get(Calendar.ZONE_OFFSET);
}
@Override
public int getOffset(final long instant) {
return mTz.getOffset(instant);
}
@Override
public String getShortName(final long instant, final Locale locale) {
return getName(instant, locale, true);
}
@Override
public String getName(final long instant, final Locale locale) {
return getName(instant, locale, false);
}
private String getName(final long instant, final Locale locale, final boolean isShort) {
return mTz.getDisplayName(!isStandardOffset(instant),
isShort ? TimeZone.SHORT : TimeZone.LONG,
locale == null ? Locale.getDefault() : locale);
}
@Override
public String getNameKey(final long instant) {
return null;
}
@Override
public TimeZone toTimeZone() {
return (TimeZone) mTz.clone();
}
@Override
public String toString() {
return mTz.getClass().getSimpleName();
}
@Override
public boolean equals(final Object o) {
return (o instanceof AndroidOldDateTimeZone) && mTz == ((AndroidOldDateTimeZone) o).getTz();
}
@Override
public int hashCode() {
return 31 * super.hashCode() + mTz.hashCode();
}
private long roundDownMillisToSeconds(final long millis) {
return millis < 0 ? (millis - 999) / 1000 : millis / 1000;
}
private int findTransitionIndex(final long millis, final boolean isNext) {
final long seconds = roundDownMillisToSeconds(millis);
int index = isNext ? mTransition.length : -1;
for (int i = 0; i < mTransition.length; i++) {
if (mTransition[i] == seconds) {
index = i;
}
}
return index;
}
}
AndroidNewDateTimeZone.java는 "Old"와 동일하지만 android.icu.util.TimeZone
대신에 기반 합니다.
- I have created a fork of Joda Time especially for this. It loads for only ~29 ms in debug mode and ~2ms in release mode. Also it has less weight as it doesn't include timezone database.
ReferenceURL : https://stackoverflow.com/questions/5059663/android-java-joda-date-is-slow
'Development Tip' 카테고리의 다른 글
DataGridView 컨트롤에서 선택한 모든 행을 선택 취소하는 방법은 무엇입니까? (0) | 2021.01.06 |
---|---|
면도기로 다른 경우 속기 (0) | 2021.01.06 |
Rails : Engine.root가 있습니까? (0) | 2021.01.06 |
가장 효율적인 node.js 프로세스 간 통신 라이브러리 / 방법은 무엇입니까? (0) | 2021.01.06 |
삽입 모드에서 vim을 어떻게 시작합니까? (0) | 2021.01.06 |