Google Play pre-launch report found an issue with my app. This issue comes up only for a few devices. The problem occurs when I try to open the app for the first time, and the SQLite database is being opened for the first time (after it was copied to the device). The app opens fine after this initial incident (for the second time).
The crash happens at line:
mDatabase = SQLiteDatabase.openDatabase(mypath, null, SQLiteDatabase.OPEN_READWRITE);
The code for the database creation:
/**
* constructor should be private to prevent direct instantiation.
* make call to static factory method "getInstance()" instead.
*/
private DbHelper(Context context) throws SQLException, IOException {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
DB_PATH = Environment.getDataDirectory() + "/data/" + context.getPackageName() + "/databases/";
helperContext = context;
boolean dbexist = checkdatabase();
if (dbexist) {
open();
} else {
createdatabase();
}
}
public void createdatabase() throws IOException {
boolean dbexist = checkdatabase();
if(!dbexist) {
// By calling this method and empty database will be created into
// the default system path
// of your application so we are gonna be able to overwrite that
// database with our database.
this.getReadableDatabase();
this.close();
try {
copydatabase();
this.getReadableDatabase();
} catch(IOException e) {
throw new Error("Error copying database");
}
}
}
private boolean checkdatabase() {
boolean checkdb = false;
try {
String myPath = DB_PATH + DATABASE_NAME;
File dbfile = new File(myPath);
checkdb = dbfile.exists();
} catch (SQLiteException e) {
System.out.println("Database doesn't exist");
}
return checkdb;
}
private void copydatabase() throws IOException {
//Open your local db as the input stream
InputStream myinput = helperContext.getAssets().open(DATABASE_NAME);
// Path to the just created empty db
String outfilename = DB_PATH + DATABASE_NAME;
//Open the empty db as the output stream
OutputStream myoutput = new FileOutputStream(outfilename);
// transfer byte to inputfile to outputfile
byte[] buffer = new byte[1024];
int length;
while ((length = myinput.read(buffer)) > 0) {
myoutput.write(buffer, 0, length);
}
//Close the streams
myoutput.flush();
myoutput.close();
myinput.close();
}
public void open() throws SQLException {
//Open the database
String mypath = DB_PATH + DATABASE_NAME;
mDatabase = SQLiteDatabase.openDatabase(mypath, null, SQLiteDatabase.OPEN_READWRITE);
}
public synchronized void close() {
if (mDatabase != null) {
mDatabase.close();
}
super.close();
}
@Override
public void onCreate(SQLiteDatabase db) {
mDatabase = db;
try {
this.createdatabase();
open();
}catch(Exception ex){
}
}
I was able to reproduce this issue on a simulator, the detailed error log is below:
E/SQLiteLog: (5) database is locked in "PRAGMA journal_mode"
E/SQLiteDatabase: Failed to open database '/data/data/com.ch.mo/databases/moDB.db'.
android.database.sqlite.SQLiteDatabaseLockedException: database is locked (code 5 SQLITE_BUSY): , while compiling: PRAGMA journal_mode
at android.database.sqlite.SQLiteConnection.nativePrepareStatement(Native Method)
at android.database.sqlite.SQLiteConnection.acquirePreparedStatement(SQLiteConnection.java:1045)
at android.database.sqlite.SQLiteConnection.executeForString(SQLiteConnection.java:788)
at android.database.sqlite.SQLiteConnection.setJournalMode(SQLiteConnection.java:405)
at android.database.sqlite.SQLiteConnection.setWalModeFromConfiguration(SQLiteConnection.java:345)
at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:258)
at android.database.sqlite.SQLiteConnection.open(SQLiteConnection.java:205)
at android.database.sqlite.SQLiteConnectionPool.openConnectionLocked(SQLiteConnectionPool.java:505)
at android.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java:206)
at android.database.sqlite.SQLiteConnectionPool.open(SQLiteConnectionPool.java:198)
at android.database.sqlite.SQLiteDatabase.openInner(SQLiteDatabase.java:918)
at android.database.sqlite.SQLiteDatabase.open(SQLiteDatabase.java:898)
at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:789)
at android.database.sqlite.SQLiteDatabase.openDatabase(SQLiteDatabase.java:736)
at com.ch.mo.DbHelper.open(DbHelper.java:128)
at com.ch.mo.DbHelper.onCreate(DbHelper.java:143)
at android.database.sqlite.SQLiteOpenHelper.getDatabaseLocked(SQLiteOpenHelper.java:411)
at android.database.sqlite.SQLiteOpenHelper.getReadableDatabase(SQLiteOpenHelper.java:340)
at com.ch.mo.DbHelper.createdatabase(DbHelper.java:79)
at com.ch.mo.DbHelper.<init>(DbHelper.java:67)
at com.ch.mo.DbHelper.getInstance(DbHelper.java:46)
at com.ch.mo.MainActivity.onCreate(MainActivity.java:573)
at android.app.Activity.performCreate(Activity.java:7994)
at android.app.Activity.performCreate(Activity.java:7978)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1309)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3422)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3601)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:85)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2066)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:223)
at android.app.ActivityThread.main(ActivityThread.java:7656)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
D/CompatibilityChangeReporter: Compat change id reported: 147798919; UID 10121; state: ENABLED
D/HostConnection: HostConnection::get() New Host Connection established 0xf4be3390, tid 3464
D/HostConnection: HostComposition ext ANDROID_EMU_CHECKSUM_HELPER_v1 ANDROID_EMU_native_sync_v2 ANDROID_EMU_native_sync_v3 ANDROID_EMU_native_sync_v4 ANDROID_EMU_dma_v1 ANDROID_EMU_direct_mem ANDROID_EMU_host_composition_v1 ANDROID_EMU_host_composition_v2 ANDROID_EMU_vulkan ANDROID_EMU_deferred_vulkan_commands ANDROID_EMU_vulkan_null_optional_strings ANDROID_EMU_vulkan_create_resources_with_requirements ANDROID_EMU_YUV_Cache ANDROID_EMU_vulkan_ignored_handles ANDROID_EMU_has_shared_slots_host_memory_allocator ANDROID_EMU_vulkan_free_memory_sync ANDROID_EMU_vulkan_shader_float16_int8 ANDROID_EMU_vulkan_async_queue_submit ANDROID_EMU_sync_buffer_data ANDROID_EMU_read_color_buffer_dma GL_OES_vertex_array_object GL_KHR_texture_compression_astc_ldr ANDROID_EMU_host_side_tracing ANDROID_EMU_gles_max_version_2
W/OpenGLRenderer: Failed to choose config with EGL_SWAP_BEHAVIOR_PRESERVED, retrying without...
D/EGL_emulation: eglCreateContext: 0xf4bcd4b0: maj 2 min 0 rcv 2
D/EGL_emulation: eglMakeCurrent: 0xf4bcd4b0: ver 2 0 (tinfo 0xf4f2e4b0) (first time)
I/Gralloc4: mapper 4.x is not supported
D/HostConnection: createUnique: call
HostConnection::get() New Host Connection established 0xf4bd52b0, tid 3464
D/goldfish-address-space: allocate: Ask for block of size 0x100
D/goldfish-address-space: allocate: ioctl allocate returned offset 0x3f3ffe000 size 0x2000
D/HostConnection: HostComposition ext ANDROID_EMU_CHECKSUM_HELPER_v1 ANDROID_EMU_native_sync_v2 ANDROID_EMU_native_sync_v3 ANDROID_EMU_native_sync_v4 ANDROID_EMU_dma_v1 ANDROID_EMU_direct_mem ANDROID_EMU_host_composition_v1 ANDROID_EMU_host_composition_v2 ANDROID_EMU_vulkan ANDROID_EMU_deferred_vulkan_commands ANDROID_EMU_vulkan_null_optional_strings ANDROID_EMU_vulkan_create_resources_with_requirements ANDROID_EMU_YUV_Cache ANDROID_EMU_vulkan_ignored_handles ANDROID_EMU_has_shared_slots_host_memory_allocator ANDROID_EMU_vulkan_free_memory_sync ANDROID_EMU_vulkan_shader_float16_int8 ANDROID_EMU_vulkan_async_queue_submit ANDROID_EMU_sync_buffer_data ANDROID_EMU_read_color_buffer_dma GL_OES_vertex_array_object GL_KHR_texture_compression_astc_ldr ANDROID_EMU_host_side_tracing ANDROID_EMU_gles_max_version_2
D/AndroidRuntime: Shutting down VM
--------- beginning of crash
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.ch.mo, PID: 3434
java.lang.IllegalStateException: Fragment already added: Songs{e0fd133} (8c0858e6-ae0b-4903-9f34-c790c433860b) id=0x7f08022b android:switcher:2131231275:0}
at androidx.fragment.app.FragmentStore.addFragment(FragmentStore.java:67)
at androidx.fragment.app.FragmentManager.addFragment(FragmentManager.java:1563)
at androidx.fragment.app.BackStackRecord.executeOps(BackStackRecord.java:405)
at androidx.fragment.app.FragmentManager.executeOps(FragmentManager.java:2167)
at androidx.fragment.app.FragmentManager.executeOpsTogether(FragmentManager.java:1990)
at androidx.fragment.app.FragmentManager.removeRedundantOperationsAndExecute(FragmentManager.java:1945)
at androidx.fragment.app.FragmentManager.execPendingActions(FragmentManager.java:1847)
at androidx.fragment.app.FragmentManager$4.run(FragmentManager.java:413)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:223)
at android.app.ActivityThread.main(ActivityThread.java:7656)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)
The error from the Google automatic test for Google Pixel 3 device:
FATAL EXCEPTION: main
Process: com.ch.mo, PID: 20970
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.ch.mo/com.ch.mo.MainActivity}: java.lang.IllegalStateException: attempt to re-open an already-closed object: SQLiteDatabase: /data/data/com.ch.mo/databases/moDB.db
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2951)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3086)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:78)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:108)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:68)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1816)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loop(Looper.java:193)
at android.app.ActivityThread.main(ActivityThread.java:6718)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:858)
Caused by: java.lang.IllegalStateException: attempt to re-open an already-closed object: SQLiteDatabase: /data/data/com.ch.mo/databases/moDB.db
at android.database.sqlite.SQLiteClosable.acquireReference(SQLiteClosable.java:55)
at android.database.sqlite.SQLiteDatabase.rawQueryWithFactory(SQLiteDatabase.java:1404)
at android.database.sqlite.SQLiteDatabase.rawQuery(SQLiteDatabase.java:1347)
at com.ch.mo.MainActivity.w(:1)
at com.ch.mo.MainActivity.onCreate(:2)
at android.app.Activity.performCreate(Activity.java:7144)
at android.app.Activity.performCreate(Activity.java:7135)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1271)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2931)
Thanks for your thoughts on this!
Checking if the database is closed, and opening it fixed the issue: