JakeWharton / ThreeTenABP

An adaptation of the JSR-310 backport for Android.
Apache License 2.0
3.55k stars 132 forks source link

IllegalStateException: Already initialized #119

Open ksc91u opened 4 years ago

ksc91u commented 4 years ago
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.

JakeWharton commented 4 years ago

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.

ksc91u commented 4 years ago

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.

JakeWharton commented 4 years ago

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

ksc91u commented 4 years ago

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

ghost commented 4 years ago

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?

ghost commented 4 years ago

And it just happened again, any idea? Thanks

JakeWharton commented 4 years ago

Initialize in application, not activity.

ghost commented 4 years ago

Silly me :)

DmitriyG1 commented 4 years ago

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();
   }
}
Androidjent commented 4 years ago

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());
        }
    }
vlad-kasprov commented 4 years ago

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 commented 4 years ago

Should be fine

On Wed, Aug 26, 2020, at 3:53 AM, Vladyslav Kasprov wrote:

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

@JakeWharton https://github.com/JakeWharton Is it ok to call AndroidThreeTen.init before calling super.onCreate() in Application? Here https://github.com/JakeWharton/ThreeTenABP#usage it is called after super.onCreate().

If not then we should either stop injecting dependencies into Application or we should rigorously check our dependency graph constructors for using org.threeten.abp classes.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/JakeWharton/ThreeTenABP/issues/119#issuecomment-680721122, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAAQIELGTED5AJVF4YJXVC3SCS5QJANCNFSM4KFDNVLA.

blainepwnz commented 3 years ago

@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
    }
}
MarvinDuane commented 2 years ago

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