SQLiteReadOnlyDatabaseException: попытка написать базу данных только для чтения (код 1032)

Поэтому в некоторых редких случаях я вижу сообщение «попытаться написать сообщение для базы данных», и я не могу понять, в чем проблема. Я начну с stacktrace в моем logcat … как вы можете видеть из timestamp, я проверяю db.isReadOnly () всего 1 мс, прежде чем пытаюсь записать. (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 

Из моего источника:

 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); } } } 

Поэтому я думаю, что может произойти одна из двух вещей.

  1. БД действительно закрывается / устанавливается на «readonly» в течение 1 мс между проверкой и попыткой вставки пакета.
  2. IsReadOnly лжет мне и не точно сообщает состояние базы данных.
  3. База данных удаляется частично через мою вставку! См. Последнюю строку журнала выше. Я включил строгую регистрацию для SQLite и заметил выше. У меня есть подозрение, что библиотека сторонних разработчиков может удалить все мои базы данных.

Из идей на данный момент, хотя, но я готов попробовать что-нибудь предлагаемое.

Solutions Collecting From Web of "SQLiteReadOnlyDatabaseException: попытка написать базу данных только для чтения (код 1032)"

Я застрял в более или менее точно такой же проблеме, и я обнаружил открытый дефект по этому вопросу, который имеет смысл …

https://code.google.com/p/android/issues/detail?id=174566

Мое обходное решение – хотя и не самое лучшее решение – это никогда не onUpgrade() параметры базы данных и отслеживать это самостоятельно, поэтому никогда не onUpgrade() и вручную выполняйте обновление при обновлении приложения.

Альтернативно, если у вас есть небольшая БД, которая доступна только для чтения, вы можете инициировать копирование db в активах на каждом onCreate() в классе DBHelper , но это может привести к нежелательным проблемам, если файловая система заполнена, поэтому делайте это только при поиске Лучшее решение.

 @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()); } } 

Мое приложение теперь обновляется, как и должно быть с моим обходным путем, и, судя, сколько времени прошло с тех пор, как этот дефект был поднят изначально, он, возможно, никогда не будет исправлен вообще …

Мне жаль, что это не полное решение проблемы, но это, по крайней мере, путь вперед.

Таким образом, первопричина этого, на первый взгляд, появляется в сторонней библиотеке. Если я не ошибаюсь, Tagit от Mobeix удаляет базу данных при запуске приложения. Я добавил несколько подробных журналов SQLite, включая эти политики:

  StrictMode.setVmPolicy(new StrictMode.VmPolicy.Builder() .detectLeakedSqlLiteObjects() .detectLeakedClosableObjects() .penaltyLog() .penaltyDeath() .build()); 

Я заметил в журнале, что моя база данных отключается после создания и открытия. Более подробное ведение журнала указывает, что это происходит, когда библиотека Mobeix инициализируется. Оскорбительная линия, о которой идет речь:

 01-29 13:47:49.118: W/SQLiteLog(12120): (28) file unlinked while open: /data/user/0/com.company.app/databases/MyDatabase.db 

Таким образом, мой файл базы данных отсоединен. Weird. Следующий вызов getWritableDatabase () воссоздает его снова, и тогда все нормально, пока приложение не будет убито и перезапущено, после чего оно будет удалено и восстановлено.

Я обновлю это, если я когда-нибудь выясню , что вызывает unlink.

У меня была аналогичная проблема. Однако я намеренно удалял текущую базу данных как часть восстановления.

Я думаю, что это происходит, так как SQLite флаги базы данных только для чтения, чтобы обеспечить защиту от file unlinked while open:

После восстановления все обновления будут сбой при attempt to write a readonly database (code 1032) .

Мое решение состояло бы в том, чтобы восстановить DBHelper. Я сделал это, добавив метод reopen который я вызываю.

например

  public static void reopen(Context context) { instance = new DBHelper(context); } 

Затем я вызываю / вызываю это, используя

  if(copytaken && origdeleted && restoredone) { DBHelper.reopen(context); DBHelper.getHelper(context).expand(null,true); } 

Вызов метода expand – это мой эквивалент / getaround для onUpgrade / версий. Он добавляет таблицы и столбцы в соответствии с псевдо-схемой, сравниваемой с фактической базой данных.

Полный DBHelper: –

 /** * DBHelper */ @SuppressWarnings("WeakerAccess") class DBHelper extends SQLiteOpenHelper { private static final String LOGTAG = "SW-DBHelper"; private static final String DBNAME = DBConstants.DATABASE_NAME; private static final String dbcreated = "001I Database " + DBNAME + " created."; private static final String dbunusable = "002E Database " + DBNAME + " has been set as unusable (according to schema)."; private static final String dbexpanded = "003I Database " + DBNAME + " expanded."; private static final String dbexpandskipped = "004I Database " + DBNAME + " expand skipped - nothing to alter."; private static final String dbbuildskipped = "005I Database" + DBNAME + " build skipped - no tables to add"; public static final String THISCLASS = DBHelper.class.getSimpleName(); /** * Consrtuctor * * @param context activity context * @param name database name * @param factory cursorfactory * @param version database version */ DBHelper(Context context, @SuppressWarnings("SameParameterValue") String name, @SuppressWarnings("SameParameterValue") SQLiteDatabase.CursorFactory factory, @SuppressWarnings("SameParameterValue") int version) { super(context, name, factory, version); } /** * Instantiates a new Db helper. * * @param context the context */ DBHelper(Context context) { super(context, DBConstants.DATABASE_NAME, null, 1); } private static DBHelper instance; /** * Gets helper. * * @param context the context * @return the helper */ static synchronized DBHelper getHelper(Context context) { if(instance == null) { instance = new DBHelper(context); } return instance; } @Override public void onCreate(SQLiteDatabase db) { expand(db, false); } @Override public void onUpgrade(SQLiteDatabase db, int oldversion, int newversion) { } /** * expand create database tables * * @param db SQLIte Database, if null then instance is used * @param buildandexpand to attempt both create and expand */ void expand(SQLiteDatabase db, boolean buildandexpand) { String mode = "Create Mode."; if (buildandexpand) { mode = "Expand Mode."; } String msg = mode; String methodname = new Object(){}.getClass().getEnclosingMethod().getName(); LogMsg.LogMsg(LogMsg.LOGTYPE_INFORMATIONAL,LOGTAG,msg,THISCLASS,methodname); // if no database has been passed then get the database if(db == null) { db = instance.getWritableDatabase(); } // Build Tables to reflect schema (SHOPWISE) only if schema is usable if(DBConstants.SHOPWISE.isDBDatabaseUsable()) { // Check to see if any tables need to be added ArrayList<String> buildsql = DBConstants.SHOPWISE.generateDBBuildSQL(db); if (!buildsql.isEmpty()) { DBConstants.SHOPWISE.actionDBBuildSQL(db); msg = dbcreated + buildsql.size() + " tables added."; LogMsg.LogMsg(LogMsg.LOGTYPE_INFORMATIONAL,LOGTAG,msg,THISCLASS,methodname); } else { msg = dbbuildskipped; LogMsg.LogMsg(LogMsg.LOGTYPE_INFORMATIONAL,LOGTAG,msg,THISCLASS,methodname); } if(buildandexpand) { ArrayList<String> altersql = DBConstants.SHOPWISE.generateDBAlterSQL(db); if(!altersql.isEmpty()) { msg = dbexpanded + altersql.size() + " columns added."; LogMsg.LogMsg(LogMsg.LOGTYPE_INFORMATIONAL,LOGTAG,msg,THISCLASS,methodname); DBConstants.SHOPWISE.actionDBAlterSQL(db); } else { msg = dbexpandskipped; LogMsg.LogMsg(LogMsg.LOGTYPE_INFORMATIONAL,LOGTAG,msg,THISCLASS,methodname); } } } else { msg = dbunusable + "\n" + DBConstants.SHOPWISE.getAllDBDatabaseProblemMsgs(); LogMsg.LogMsg(LogMsg.LOGTYPE_ERROR,LOGTAG,msg,THISCLASS,methodname); } } public static void reopen(Context context) { instance = new DBHelper(context); } } 

У меня есть аналогичная проблема, которая действительно раздражает, так это то, что это происходит в любое время, трудно воспроизвести точные условия, которые делают это возможным. Я только закрываю DDBB в методе ondestroy класса MainActivity. То, что я сделал, это добавить try / catch при каждом использовании db и добавить следующий catch, в этом случае он находится в середине цикла while, в других функциях я вызываю функцию снова один раз:

 catch (SQLException e) { e.printStackTrace(); Log.d(TAG, "MainService: error in AccSaveToDB with "+mainDB.getPath()+" in iteration "+j+". Closing and re-opening DB"); DBHelper.close(); mainDB.close(); j--; } 

И это в начале каждой функции, доступ к базе данных:

 if (mainDB==null || !mainDB.isOpen()) { DBHelper = DefSQLiteHelper.getInstance(getApplicationContext(), "Data.db", null, 1); mainDB = DBHelper.getWritableDatabase(); } 

До сих пор я все еще имел эту ошибку, я еще не мог понять причину, но, по крайней мере, мое приложение не разбивается и возобновляет то, что ему нужно делать. Я не вижу, удаляется ли файл, но эти решения работают для меня