JakeWharton/ThreeTenABP

IllegalStateException: Already initialized

ksc91u opened this issue ยท 14 comments

Caused by java.lang.IllegalStateException: Already initialized
       at org.threeten.bp.zone.ZoneRulesInitializer.setInitializer(ZoneRulesInitializer.java:74)
       at com.jakewharton.threetenabp.AndroidThreeTen.init(AndroidThreeTen.java:22)
       at com.jakewharton.threetenabp.AndroidThreeTen.init(AndroidThreeTen.java:17)
       at com.jakewharton.threetenabp.AndroidThreeTen.init(AndroidThreeTen.java:13)
       at com.wave.waveradio.WaveApplication.onCreate(WaveApplication.java:108)

We have seen this exception on some devices since last release.

//jsr310
    implementation 'com.jakewharton.threetenabp:threetenabp:1.2.1'
    testImplementation("org.threeten:threetenbp:1.4.0") {
        exclude module: "com.jakewharton.threetenabp:threetenabp:1.2.1"
    }
//previously added duplicate entry
//testImplementation("org.threeten:threetenbp:1.4.0")

We have tried remove that testImplementation duplicate entry,
try/catch that exception and continue. But new exception occurred.

Fatal Exception: org.threeten.bp.zone.ZoneRulesException: No time-zone data files registered
       at org.threeten.bp.zone.ZoneRulesProvider.getProvider(ZoneRulesProvider.java:165)
       at org.threeten.bp.zone.ZoneRulesProvider.getRules(ZoneRulesProvider.java:122)
       at org.threeten.bp.ZoneRegion.ofId(ZoneRegion.java:143)
       at org.threeten.bp.ZoneId.of(ZoneId.java:358)
       at org.threeten.bp.ZoneId.of(ZoneId.java:286)
       at org.threeten.bp.ZoneId.systemDefault(ZoneId.java:245)
       at org.threeten.bp.Clock.systemDefaultZone(Clock.java:137)
       at org.threeten.bp.LocalDateTime.now(LocalDateTime.java:152)

I have seen two library entry in Android Studio,

org.threeten:threetenbp:1.4.0:no-tzdb@jar
org.threeten:threetenbp:1.4.0@jar

But only 1.4.0@jar version when run ./gradlew app::dependencies 2>&1 1> log2

I have also clear AndroidStudio & gradle caches. Do you know what might cause this issue? Thank you.

Yeah that's not going to work as the regular threetenbp jar will initialize itself with the embedded tzdb and then the application will try to initialize it with the asset version. threetenabp is not a dependency of threetenbp (it's the other way around) so your exclude isn't actually doing anything. There's really not a good way to set this up, I don't think.

I do not understand why adding threetenbp to unit test would effect my app on only some devices.

now my configuration is

implementation 'com.jakewharton.threetenabp:threetenabp:1.2.1'

try {
            AndroidThreeTen.init(this)
        } catch (e: Exception) {
            Timber.e(e)
        }

I removed that testImplementation but ZoneRulesException: No time-zone data files registered still happen on some devices.

It should have no effect on the app. It will cause tests to always fail though.

Thank you, I will just try D8 java time then.

I got this too with 1.2.2

Caused by java.lang.IllegalStateException: Already initialized
       at org.threeten.bp.zone.ZoneRulesInitializer.setInitializer(ZoneRulesInitializer.java:74)
       at com.jakewharton.threetenabp.AndroidThreeTen.init(AndroidThreeTen.java:22)
       at com.jakewharton.threetenabp.AndroidThreeTen.init(AndroidThreeTen.java:17)
       at com.app.app.MainActivity.onCreate(MainActivity.java:161)

I have the following for release:

minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'

On the emulator it worked, the crash happened only for the first time I opened my app (after adding this lib). After that it seems to work. This is with my device, still waiting to see if it happens on more devices (a new release).

Did I do something wrong?

And it just happened again, any idea? Thanks

Initialize in application, not activity.

Silly me :)

Same error.

Library versions:

"com.jakewharton.threetenabp:threetenabp:1.2.1"
"org.threeten:threetenbp:1.4.0"

Usage:

class App : Application() {

override fun onCreate() {
  super.onCreate()
  if (LeakCanary.isInAnalyzerProcess(this)) {
    return
  }
  if (ProcessPhoenix.isPhoenixProcess(this)) {
    return
  }
 Fabric.with(this, crashlyticsKit)
 AndroidThreeTen.init(this)
}

}

Error:

org.threeten.bp.zone.ZoneRulesInitializer.setInitializer (ZoneRulesInitializer.java:74)
com.jakewharton.threetenabp.AndroidThreeTen.init (AndroidThreeTen.java:22)
com.jakewharton.threetenabp.AndroidThreeTen.init (AndroidThreeTen.java:17)
com.jakewharton.threetenabp.AndroidThreeTen.init (AndroidThreeTen.java:13)
\\ Error like: App.onCreate (App.java:109)

This error does not always occur. We can not reproduce when testing the application. Errors are logged in Crashlytics. Versions of Android 7, 8, 9, 10. Various device manufacturers Samsung, Xiaomi, HUAWEI and others.

Error investigation
It looks like the ZoneRulesProvider class is loading before calling the ZoneRulesInitializer.setInitializer() method.

Documentation for method setInitializer():

This can only be invoked before the {@link ZoneRulesProvider} class is loaded.

Class ZoneRulesProvider has a static initializer:

public abstract class ZoneRulesProvider {
    static {
        ZoneRulesInitializer.initialize();
   }
}

I also got that issue on abp 1.2.3, are there any known workarounds? 1.2.4 seems to only add a Japanese time zone.

The crash happens rather randomly when the app is in background for a long time. There is only an abp dependency in my gradle, no org.threeten:threetenbp. Is that mandatory?

Logcat:

05-09 00:45:48.285 10475 10475 E AndroidRuntime: Caused by: java.lang.IllegalStateException: Already initialized
05-09 00:45:48.285 10475 10475 E AndroidRuntime: 	at org.threeten.bp.zone.ZoneRulesInitializer.setInitializer(ZoneRulesInitializer.java:74)
05-09 00:45:48.285 10475 10475 E AndroidRuntime: 	at com.jakewharton.threetenabp.AndroidThreeTen.init(AndroidThreeTen.java:22)
05-09 00:45:48.285 10475 10475 E AndroidRuntime: 	at com.jakewharton.threetenabp.AndroidThreeTen.init(AndroidThreeTen.java:17)
05-09 00:45:48.285 10475 10475 E AndroidRuntime: 	at com.example.erik.myroom.Frontend.Activity_Main.onCreate(Activity_Main.java:49)
05-09 00:45:48.285 10475 10475 E AndroidRuntime: 	at android.app.Activity.performCreate(Activity.java:6684)
05-09 00:45:48.285 10475 10475 E AndroidRuntime: 	at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1119)
05-09 00:45:48.285 10475 10475 E AndroidRuntime: 	at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2637)

Code:

Public class Activity_Main extends AppCompatActivity
        implements
        Fragment_Main.OnCatButtonListener,
        Fragment_Main.OnGlobalLIstButtonListener,
        Fragment_Main.OnStatisticButtonListener,
        Fragment_Main.OnJobListButtonListener,
        OnBackToMainListener {

    private ActivityMainBinding oBinding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        //inflates Iconics
        LayoutInflaterCompat.setFactory2(getLayoutInflater(), new IconicsLayoutInflater2(getDelegate()));
        super.onCreate(savedInstanceState);
        //Init Date Framework
        AndroidThreeTen.init(this);
        //init job
        androidx.work.PeriodicWorkRequest periodicWorkRequest = new androidx.work.PeriodicWorkRequest.Builder(
                MonthlyWorker.class,
                MIN_PERIODIC_INTERVAL_MILLIS, TimeUnit.MILLISECONDS,
                MIN_PERIODIC_FLEX_MILLIS, TimeUnit.MILLISECONDS)
                .addTag(TAG)
                .build();
        WorkManager.getInstance(getApplicationContext()).enqueueUniquePeriodicWork(TAG, ExistingPeriodicWorkPolicy.KEEP, periodicWorkRequest);

        //init Databinding
        oBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);

        setSupportActionBar(oBinding.toolbar);

        if(oBinding.mainContainer != null){

            //are we restoring the instance state?
            if(savedInstanceState != null){
                return; //yes - do nothing 
            }

            this.CreateNewFragment(new Fragment_Main());
        }
    }

We had this crash too. We use Dagger.Hilt and it injects Application dependencies during super.onCreate() . We call AndroidThreeTen.init after super.onCreate(). However, recently we used OffsetDateTime in constructor of one of Application dependencies. Turns out that doing that triggers AndroidThreeTen initialization internally since we didn't call AndroidThreeTen.init yet. And after super.onCreate() we call AndroidThreeTen.init and get this crash.

@JakeWharton Here AndroidThreeTen.init is called after super.onCreate(). Is it ok to call it before super.onCreate()? If not then we should either stop injecting dependencies into Application or we should rigorously check constructors of all our dependencies for not using org.threeten.abp classes. Or maybe there is another way?

This is less error-prone with plain Dagger since you can't inject Application dependencies in super.onCreate(). So previously we called super.onCreate() then AndroidThreeTen.init and then injected our Application.

@JakeWharton
wdyt about adding fallback with force setting initializer to make sure, that we are not crashing?

try {
    AndroidThreeTen.init(application)
} catch (e: Exception) {
    forceSetInitializer(application)
}
/**
 * Copied logic from [com.jakewharton.threetenabp.AssetsZoneRulesInitializer]
 */
private fun forceSetInitializer(context: Context) {
    val assetPath = "org/threeten/bp/TZDB.dat"
    val provider: TzdbZoneRulesProvider
    var inputStream: InputStream? = null
    try {
        inputStream = context.assets.open(assetPath)
        provider = TzdbZoneRulesProvider(inputStream)
    } catch (e: IOException) {
        throw IllegalStateException("$assetPath missing from assets", e)
    } finally {
        if (inputStream != null) {
            try {
                inputStream.close()
            } catch (ignored: IOException) {
            }
        }
    }
    try {
        ZoneRulesProvider.registerProvider(provider)
    } catch (ignored : ZoneRulesException){
        // if this exception is thrown - means it is already initialized and we are good
    }
}

hi @JakeWharton, just checking if there still isn't a solution for this yet?