I have 2 types of jobs which runs at application's onCreate in the following sequence:
EventJobUploader.schedulePeriodicJob();
EventJobUploader.runJobImmediately();
FavoriteJob.schedulePeriodicJob();
FavoriteJob.runJobImmediately();
Implementation of jobs methods a similar:
public static void runJobImmediately(){
new JobRequest.Builder(EventJobUploader.TAG)
.setExecutionWindow(TimeUnit.SECONDS.toMillis(10), TimeUnit.SECONDS.toMillis(20))
.build()
.schedule();
}
public static void schedulePeriodicJob(){
new JobRequest.Builder(EventJobUploader.TAG)
.setPeriodic(TimeUnit.MINUTES.toMillis(15), TimeUnit.MINUTES.toMillis(5))
.setUpdateCurrent(true)
.setRequiredNetworkType(JobRequest.NetworkType.CONNECTED)
.build()
.schedule();
}
And on several devices (unknown, sentry did not provide any info about them, sorry) I've got crash with the following stacktrace:
android.database.sqlite.SQLiteException: table jobs already exists (code 1)
at dalvik.system.NativeStart.main
at com.android.internal.os.ZygoteInit.main
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run
at java.lang.reflect.Method.invoke
at java.lang.reflect.Method.invokeNative
at android.app.ActivityThread.main
at android.os.Looper.loop
at android.os.Handler.dispatchMessage
at android.app.ActivityThread$H.handleMessage
at android.app.ActivityThread.access$1300
at android.app.ActivityThread.handleBindApplication
at android.app.Instrumentation.callApplicationOnCreate
at xxx.Application.onCreate
at xxx.EventJobUploader.schedulePeriodicJob
at com.evernote.android.job.JobRequest.schedule
at com.evernote.android.job.JobManager.schedule
at com.evernote.android.job.JobStorage.put
at com.evernote.android.job.JobStorage.store
at com.evernote.android.job.JobStorage.getDatabase
at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase
at android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked
at com.evernote.android.job.JobStorage$JobOpenHelper.onCreate
at com.evernote.android.job.JobStorage$JobOpenHelper.createJobTable
at android.database.sqlite.SQLiteDatabase.execSQL
at android.database.sqlite.SQLiteDatabase.executeSql
at android.database.sqlite.SQLiteStatement.executeUpdateDelete
at android.database.sqlite.SQLiteSession.executeForChangedRowCount
at android.database.sqlite.SQLiteConnection.executeForChangedRowCount
at android.database.sqlite.SQLiteConnection.nativeExecuteForChangedRowCount
Also sometimes app crashed with another kind of sqlite related error:
android.database.sqlite.SQLiteDatabaseLockedException: database is locked(Sqlite code 5): , while compiling: PRAGMA journal_mode,(OS error - 11:Try again)
at com.android.internal.os.ZygoteInit.main
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run
at java.lang.reflect.Method.invoke
at java.lang.reflect.Method.invoke
at android.app.ActivityThread.main
at android.os.Looper.loop
at android.os.Handler.dispatchMessage
at android.app.ActivityThread$H.handleMessage
at android.app.ActivityThread.access$1900
at android.app.ActivityThread.handleBindApplication
at android.app.Instrumentation.callApplicationOnCreate
at xxx.Application.onCreate
at xxx.FavoriteJob.runJobDelay
at com.evernote.android.job.JobRequest.schedule
at com.evernote.android.job.JobManager.schedule
at com.evernote.android.job.JobStorage.put
at com.evernote.android.job.JobStorage.store
at com.evernote.android.job.JobStorage.getDatabase
at android.database.sqlite.SQLiteOpenHelper.getWritableDatabase
at android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked
at android.content.ContextWrapper.openOrCreateDatabase
at android.app.ContextImpl.openOrCreateDatabase
at android.database.sqlite.SQLiteDatabase.openDatabase
at android.database.sqlite.SQLiteDatabase.open
at android.database.sqlite.SQLiteDatabase.openInner
at android.database.sqlite.SQLiteConnectionPool.open
at android.database.sqlite.SQLiteConnectionPool.open
at android.database.sqlite.SQLiteConnectionPool.openConnectionLocked
at android.database.sqlite.SQLiteConnection.open
at android.database.sqlite.SQLiteConnection.open
at android.database.sqlite.SQLiteConnection.setWalModeFromConfiguration
at android.database.sqlite.SQLiteConnection.setJournalMode
at android.database.sqlite.SQLiteConnection.executeForString
at android.database.sqlite.SQLiteConnection.acquirePreparedStatement
at android.database.sqlite.SQLiteConnection.nativePrepareStatement
Source code shows that db access is not synchronized at all - maybe any ReadWriteLock can be added to resolve concurrent access to database between database access methods inside JobStorage class?
All the methods inside of JobStorage are synchronized. Honestly, I don't know what I should do here. Any recommendation?
Well, I was talking about synchronized access to database object, i.e. use readLock to access for queries and writeLock for database modify operations.
But that's what the synchronized keyword is also achieving. Or am I missing something?
synchronized keyword for the method just guarantee that just a method is
thread-safe. But calling several methods from several threads do not
guarantee thread-safety for database object.
On Thu, Jan 11, 2018 at 4:15 PM, Ralf Wondratschek <[email protected]
wrote:
But that's what the synchronized keyword is also achieving. Or am I
missing something?—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
https://github.com/evernote/android-job/issues/344#issuecomment-356873158,
or mute the thread
https://github.com/notifications/unsubscribe-auth/ABGgmlX3PXJCPtXk0TDhTX63yNowLPHmks5tJdFIgaJpZM4RYBWw
.
Dang, I thought I also synchronized the whole object when accessing it. Good catch! Thanks
Sorry for the confusion, but what you said is wrong. Synchronized methods prevent that calls on ALL synchronized methods of the same object don't interleave, see here. But I agree that a ReadWriteLock provides a better performance. Nonetheless, it won't fix your problem.
Sorry for the confusion, but what you said is wrong.
Yep, sorry, my fault.
Version 1.2.2 has been released. It now uses a ReadWriteLock.
A much simpler fix would be to just open the database connection once, and reuse it everywhere. You don't need to open and close the database connection every time you read or write. SQLite itself takes care of thread safety.
I'll make this feature optional through the JobConfig.
In android's training its said we should leave our database connection open for as long as we possibly need to access it
Since getWritableDatabase() and getReadableDatabase() are expensive to call when the database is closed, you should leave your database connection open for as long as you possibly need to access it. Typically, it is optimal to close the database in the onDestroy() of the calling Activity.
Ref: https://developer.android.com/training/data-storage/sqlite.html
Also post post by a Google engineer (Dianne Hackborn)
Android made a deliberate design decision that is can seem surprising, to just give up on the whole idea of applications cleanly exiting and instead let the kernel clean up their resources. After all, the kernel needs to be able to do this anyway. Given that design, keeping anything open for the entire duration of a process's life and never closing it is simply not a leak. It will be cleaned up when the process is cleaned up.
Ref: https://groups.google.com/forum/#!msg/android-developers/nopkaw4UZ9U/cPfPL3uW7nQJ
In version 1.2.3 I don't close the database anymore. Please let me know if that works for you all better now.
Most helpful comment
In android's training its said we should leave our database connection open for as long as we possibly need to access it
Ref: https://developer.android.com/training/data-storage/sqlite.html
Also post post by a Google engineer (Dianne Hackborn)
Ref: https://groups.google.com/forum/#!msg/android-developers/nopkaw4UZ9U/cPfPL3uW7nQJ