3

I recently added JobScheduler to my app via a JobService. I use the JobService to sync with my database periodically in the background and update a local Room DB instance.

However, I am seeing arbitrary crashes with the error:

Process: com.application.name, PID: 27229 java.lang.RuntimeException: Unable to instantiate service com.application.name.Services.SyncService: java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.Context android.content.Context.getApplicationContext()' on a null object reference

The full stack trace is as follows:

Caused by java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.Context android.content.Context.getApplicationContext()' on a null object reference at com.application.name.Database.AppDatabase.getDatabase(AppDatabase.java:38) at com.application.name.Services.SyncService.(SyncService.java:48) at java.lang.Class.newInstance(Class.java) at android.app.ActivityThread.handleCreateService(ActivityThread.java:3551) at android.app.ActivityThread.-wrap4(Unknown Source) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1778) at android.os.Handler.dispatchMessage(Handler.java:105) at android.os.Looper.loop(Looper.java:164) at android.app.ActivityThread.main(ActivityThread.java:6798) at java.lang.reflect.Method.invoke(Method.java) at com.android.internal.os.Zygote$MethodAndArgsCaller.run(Zygote.java:240) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:767)

The offending line highighted by the crash report is the instantiation of the Room DB instance in the JobService constructor.

 public SyncService() {
        super();
        database = AppDatabase.getDatabase(getApplication());
    }

and in the Room DAO I have the following code:

private static AppDatabase INSTANCE;
public static AppDatabase getDatabase(final Context context){
        if(INSTANCE == null) {
            synchronized (AppDatabase.class){
                if(INSTANCE == null){

                    // Migration definitions go here
                    ..
                    INSTANCE = Room.databaseBuilder(context.getApplicationContext(),
                            AppDatabase.class, context.getResources().getString(R.string.database))
                            .allowMainThreadQueries()
                            .addMigrations(FROM_2_TO_3)
                            .build();
                }
            }
        }
        return INSTANCE;
    }

So I think I understand why this is happening - when the app isn't running, the Application Context is Null, so Room's database builder runs into issues setting up.

What I would like to know is if there is some way to get access to my Room DB when the app isn't running. Passing Context to the initialisation from the JobService and writing a secondary constructor seems like an ugly hack that may produce unpredictable conditions, so I'm wondering what other options I have.

One thing I could do is write the data retreived from the JobService to SharedPreferences and then sync that to the DB when the app is started. Is there any other way?

kilokahn
  • 1,136
  • 2
  • 18
  • 38
  • 2
    Small favor: in the future, rather than post just the error message, please post the entire stack trace. – CommonsWare Dec 22 '18 at 14:12
  • @CommonsWare : I've added the full stack trace. Sorry about the slip up. Big fan, by the way. You've helped me more times than you know with your answers and insights on other questions. :) – kilokahn Dec 22 '18 at 14:24
  • 1
    "I've added the full stack trace" -- thanks! It's pretty much as I expected from your description. However, every now and then, the stack trace shows something that the question author did not expect, so having the full trace helps in those cases. Your error is rather strange, as it implies that `getApplication()` on a `JobService` can return `null` on occasion, which is disturbing. Still, your solution is a good first step, and I was halfway through writing that up when you posted your own. "Big fan, by the way" -- thanks for the kind words! – CommonsWare Dec 22 '18 at 14:28
  • " Still, your solution is a good first step " -- Thank you for your encouragement and support. I will keep poking at the app over the next few days, and if I see anything new or curious, I'll add an update here. :) – kilokahn Dec 22 '18 at 14:39
  • Hi, I've been working on my app quite a bit and I see a fair bit of recurrence with the NPE on getApplicationContext() or getApplication() or 'this'. Any pointers on how to avoid this would be great! – kilokahn Jan 20 '19 at 18:38
  • I recommend that you ask a fresh question, with the up-to-date code and stack trace, perhaps with a link back to this question to point out the previous problem and resolution. – CommonsWare Jan 20 '19 at 18:42
  • Will do! Thanks much! – kilokahn Jan 20 '19 at 18:43

2 Answers2

3

I think I missed one simple fact - a Service IS a Context.

Instead of passing :

 database = AppDatabase.getDatabase(getApplication());

all I needed to do was this:

 database = AppDatabase.getDatabase(this);

So far, I've not had any recurring crashes ... yet. I am still open to other answers, so do chip in with your views, if you think I can do this in a better way.

kilokahn
  • 1,136
  • 2
  • 18
  • 38
1

The applicationContext should be invoked after onCrate() method called. Therefore, you need to run your code inside onCreate() method.

Ramin Ar
  • 1,213
  • 13
  • 10