22

We converted our main Application class to Kotlin recently.
Since then we are experiencing crashes, especially during the night (when our application was probably killed by the system), when our JobService is startet.

We are accessing the Application Context in a static way for some dependencies which worked pretty well before we converted the class to Kotlin. Since then the static getter is a lateinit var which is initialised in the applications onCreate function.

After the release Google Play reported these crashes:

Caused by: kotlin.UninitializedPropertyAccessException: 
  at x.y.z.application.App.access$getAppContext$cp 
  [...]
  at x.y.z.jobs.JobSchedulerService.onCreate (JobSchedulerService.java:27)  

Which leads to the question, is our Application.onCreate() not executed yet?

We refactored the JobService a little bit to reduce the amount of static context access until a major refactoring would be necessary. After that, we received these crashes from our users in Google Play Console:

Caused by: kotlin.UninitializedPropertyAccessException: 
  at org.koin.standalone.StandAloneContext.getKoinContext (StandAloneContext.java:45)
  at org.koin.java.standalone.KoinJavaComponent.get (KoinJavaComponent.java:66)
  at org.koin.java.standalone.KoinJavaComponent.get$default (KoinJavaComponent.java:64)
  at org.koin.java.standalone.KoinJavaComponent.get (KoinJavaComponent.java)
  at x.y.z.SearchState.<init> (SearchState.java:21)
  [...]
  at x.y.z.jobs.JobSchedulerService.onStartJob (JobSchedulerService.java:54)

These crashed tell us the same thing: Application.onCreate() was not executed yet because Koin is not initialised.

So my question? Why would the execution time of Application.onCreate() change when converting to Kotlin or why is our Application not created anymore before the JobService is startet?

I mean, sure, we could refactored the whole application dependencies to use the context provided by the JobService itself, but what if the application is created afterwards and we still want to use Koin? Our app will probably crash again with an AlreadyStartetException. And if our Application is not "there" yet, what context would the service have?

Sources (simplified):

Application

abstract class App : MultiDexApplication() {

    companion object {
        @JvmStatic
        lateinit var appContext: Context
        @JvmStatic
        val isDevelopment: Boolean = BuildConfig.DEBUG
    //  @JvmStatic
    //  val isDevelopment: Boolean by lazy { 
    //      appContext.resources.getBoolean(R.bool.isDevelopment) 
    //  }
    }

    override fun onCreate() {
        super.onCreate()

        appContext = applicationContext
        startKoin(
                applicationContext,
                listOf(
                        coreModule,
                        sharedPrefsModule
                )
        )

    }
}

JobService

public class JobSchedulerService extends JobService implements OnFinishListener {

    @Override
    public boolean onStartJob(JobParameters params) {

        if (App.isDevelopment()) { //First crash cause `isDevelopment` relied on App.appContext
            ...
        }
        this.mJobParameters = params;

        this.mStateMachine = StateContext.getInstance(getApplication());
        mStateMachine.setOnFinishListener(this);
        mStateMachine.execute("" + params.getJobId()); //Second crash is in the first executed state auf this state Machine

        return true;
    }
}

Manifest Registration

    <service
        android:name="x.y.z.jobs.JobSchedulerService"
        android:enabled="true"
        android:exported="true"
        android:permission="android.permission.BIND_JOB_SERVICE">
    </service>

SearchState

public class SearchState extends State {

    //Koin Crash in SearchState.<init>
    private PlacemarkRepository placemarkRepository = get(PlacemarkRepository.class);

    ...
}
marilion91
  • 2,094
  • 1
  • 19
  • 28
  • 1
    why is your App class abstract? Is there anything else which needs to be looked upon? – Rahul Sep 28 '18 at 12:49
  • also, please double-check if the class extending `App` is specified in AndroidManifest. – Rahul Sep 28 '18 at 12:51
  • @RahulKumar yes the subclass of `App.kt` is registered in the Manifest. The Class is abstract because there are functions which need to be overwritten in the Application Class in the `app`-Module – marilion91 Oct 01 '18 at 09:43
  • could you please add your manifest JobService declaration? I think that the crash may be related to a googlePlayServices update, most of update are done by night... – Anis BEN NSIR Oct 02 '18 at 16:36
  • Be sure your Service is declared in your manifest. Also can you post your build.gradle to ensure your referencing the proper plugin – Edward DiGirolamo Oct 03 '18 at 13:38
  • @AnisBENNSIR i added the manifest declaration – marilion91 Oct 05 '18 at 12:24
  • @EdwardDiGirolamo which Plugin(s) are you referring to? Our gradle file(s) are huge ;-) – marilion91 Oct 05 '18 at 12:29
  • the KoinJavaComponent is it your Application implementation? could you please add implementation, or check that super is called at first? – Anis BEN NSIR Oct 05 '18 at 12:44
  • @AnisBENNSIR the code is working smoothly with normal app usage, everything is called as expected. The KoinJavaComponent is only uses in the java parts of the code executed in JobService. The Issue is not related to Koin cause I can't geht the application context via static access as well (see first crash description) – marilion91 Oct 05 '18 at 12:48
  • to help you, i will need the source code for StandAloneContext, could you please post the implementation for the class implementing your abstract App class. Is there any try/catch bloc? The Service will be started only if there is an Application object, so i think that your super method where you are initializing the context of App is not called for some reason... – Anis BEN NSIR Oct 05 '18 at 13:47
  • @marilion91 How many classes are extending the abstract App class? Are you extending it on many modules? That abstract declaration looks very suspicious. If you are creating a single application, keeping all your code in a single class will improve readability and bugs will be easier to find out. Publishing your extending class code will be helpful too. Are your service and your app on the same module? – Raymond Arteaga Nov 25 '18 at 05:08
  • well you should have stayed with java – has19 Jan 07 '19 at 11:19
  • Did you ever solve this?? – sudokai Mar 18 '20 at 20:29
  • @sudokai unfortunately not. We are now switching to WorkManager. I will give an update if this is a solution. – marilion91 Apr 08 '20 at 14:55

1 Answers1

1

Job-service must have its own separate context, but i think you should try upgrading to WorkManager, as Job-service will not work on Android O and Later devices. Work manager is a new thing, but remember, minimum interval it will allow to execute job is 15 minutes.

Sandip Savaliya
  • 784
  • 6
  • 19
  • we migrated to AndroidX a few weeks ago so yeah, worth the try. But in the end WorkManager is just using JobScheduler on API 23+ which might lead to the same behaviour. – marilion91 Feb 28 '19 at 15:24
  • Nope, i have tested both. JobSchedular stops when app is killed in android O or above, but i found work-manager was still working. Give it a try once and check it out. – Sandip Savaliya Mar 01 '19 at 03:09