So in some rare instances, I'm seeing the "attempt to write a readonly database" message, and I can't figure out where the problem lies. I'll start with the stacktrace in my logcat... as you can see from the timestamp I'm checking db.isReadOnly() only 1ms before I attempt the write. (isOpen=true, readOnly=false)
01-29 13:47:49.115: D/AWT(11055): #479.Got writable database (230537815): isOpen: (true) isReadOnly: (false) inTransaction: (false)
01-29 13:47:49.116: D/AWT(11055): #479.in transaction: Got writable database (230537815): isOpen: (true) isReadOnly: (false) inTransaction: (true)
01-29 13:47:49.116: E/SQLiteLog(11055): (1032) statement aborts at 15: [INSERT INTO Events(col1,col2,col3,col4) VALUES (?,?,?,?)]
01-29 13:47:49.117: E/SQLiteDatabase(11055): Error inserting data="scrubbed"
01-29 13:47:49.117: E/SQLiteDatabase(11055): android.database.sqlite.SQLiteReadOnlyDatabaseException: attempt to write a readonly database (code 1032)
01-29 13:47:49.117: E/SQLiteDatabase(11055): at android.database.sqlite.SQLiteConnection.nativeExecuteForLastInsertedRowId(Native Method)
01-29 13:47:49.117: E/SQLiteDatabase(11055): at android.database.sqlite.SQLiteConnection.executeForLastInsertedRowId(SQLiteConnection.java:780)
01-29 13:47:49.117: E/SQLiteDatabase(11055): at android.database.sqlite.SQLiteSession.executeForLastInsertedRowId(SQLiteSession.java:788)
01-29 13:47:49.117: E/SQLiteDatabase(11055): at android.database.sqlite.SQLiteStatement.executeInsert(SQLiteStatement.java:86)
01-29 13:47:49.117: E/SQLiteDatabase(11055): at android.database.sqlite.SQLiteDatabase.insertWithOnConflict(SQLiteDatabase.java:1471)
01-29 13:47:49.117: E/SQLiteDatabase(11055): at android.database.sqlite.SQLiteDatabase.insert(SQLiteDatabase.java:1341)
01-29 13:47:49.117: E/SQLiteDatabase(11055): at com.company.DbHelper.insertBatch(EventsDbHelper.java:174)
01-29 13:47:49.117: D/AWT(11055): #479.finalizing transaction: Got writable database (230537815): isOpen: (true) isReadOnly: (false) inTransaction: (true)
01-29 13:47:49.118: W/SQLiteLog(12120): (28) file unlinked while open: /data/user/0/com.company.app/databases/MyDatabase.db
From my source:
public void insertBatch(LinkedList<WriteQueue.DatabaseRecord> writeQueue) throws Exception {
Log.d("AWT", "EventsDbHelper->insertBatch()");
if (writeQueue == null) {
return;
}
Iterator<DatabaseRecord> it = writeQueue.iterator();
SQLiteDatabase db = this.getWritableDatabase();
Log.d("AWT", String.format("Got writable database (%s): isOpen: (%s) isReadOnly: (%s) inTransaction: (%s)",
db.hashCode(), db.isOpen(), db.isReadOnly(), db.inTransaction()));
try {
db.beginTransaction();
while (it.hasNext()) {
DatabaseRecord record = it.next();
ContentValues initialValues = new ContentValues();
initialValues.put(col1, val1);
initialValues.put(col2, val2);
initialValues.put(col3, val3);
initialValues.put(col4, val4);
Log.d("AWT", String.format("in transaction: Got writable database (%s): isOpen: (%s) isReadOnly: (%s) inTransaction: (%s)",
db.hashCode(), db.isOpen(), db.isReadOnly(), db.inTransaction()));
db.insert(DBTBL, null, initialValues);
}
Log.d("AWT", String.format("finalizing transaction: Got writable database (%s): isOpen: (%s) isReadOnly: (%s) inTransaction: (%s)",
db.hashCode(), db.isOpen(), db.isReadOnly(), db.inTransaction()));
db.setTransactionSuccessful();
} catch (Exception e) {
Log.e(TAG, "Error inserting batch record into database.", e);
} finally {
try {
db.endTransaction();
db.close();
} catch (Exception e) {
Log.e(TAG, Global.DB_ERROR, e);
}
}
}
So I think that maybe one of two things is happening.
Out of ideas at this point though but I'm willing to try anything suggested.
I'm stuck with more or less the exact same issue and I found a an open defect on the matter that makes sense...
https://code.google.com/p/android/issues/detail?id=174566
My workaround - albeit not the best solution - is to never step the database revision and track this myself, thus never calling onUpgrade()
, and manually do an upgrade when updating the app.
Alternatively if you have a small DB which is read only, you can trigger the copy of the db in assets on every onCreate()
in DBHelper
class, but this might give unwanted problems if the filesystem is full so only do this whilst looking for a better solution.
@Override
public void onCreate(SQLiteDatabase db) {
// Workaround for Issue 174566
myContext.deleteDatabase(DB_NAME);
try {
copyDataBase();
}
catch(IOException e) {
System.out.println("IOException " + e.getLocalizedMessage());
}
}
My app now upgrades as it should with my workaround and by judging how long time it is since this defect was raised originally it might never be fixed at all...
I'm sorry this isn't a full solution to the problem, but it's a way forward at least.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With