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?
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.