SQLiteReadOnlyDatabaseException:试图写一个只读数据库(代码1032)

因此,在极less数情况下,我看到“试图写一个只读数据库”的信息,我不知道问题出在哪里。 我将从logcat中的堆栈跟踪开始……从时间戳中可以看到,我在检查db.isReadOnly()之前只检查了1ms。 (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. 数据库真的被closures/设置为“只读”在1ms之内的检查和尝试的批量插入。
  2. isReadOnly对我说谎,不准确地报告数据库的状态。
  3. 数据库正在通过我的插入中途被删除! 看到上面日志的最后一行。 我打开SQLite的严格日志logging,注意到以上。 我有一个怀疑,第三方库可能会删除我所有的数据库。

虽然在这一点上的想法,但我愿意尝试任何build议。

Solutions Collecting From Web of "SQLiteReadOnlyDatabaseException:试图写一个只读数据库(代码1032)"

所以这个乍一看就是第三方库。 除非我错了,否则Mobeix的Tagit会在应用程序启动时删除数据库。 我添加了一些详细的SQLite日志logging,包括这些策略:

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

我注意到在日志中,我创build并打开后,我的数据库已被取消链接。 更详细的日志logging表明它在Mobeix库被初始化时发生。 有问题的线路:

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

所以我的数据库文件是不链接的。 奇怪的。 下一次调用getWritableDatabase()重新创build它,然后直到应用程序被终止并重新启动,在此时它被删除并重新创build。

我会更新这个,如果我知道究竟是什么导致取消链接。

我卡住了或多或less完全相同的问题,我发现一个开放的缺陷,在这个问题上是有道理的…

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

我的解决方法 – 尽pipe不是最好的解决scheme – 是永远不要步骤数据库修订和自己跟踪这个,因此永远不会调用onUpgrade() ,并在更新应用程序时手动进行升级。

或者,如果您有一个只读的小型数据库,您可以在DBHelper类中的每个onCreate()资产上触发数据库的副本,但如果文件系统已满,则可能会出现不必要的问题,因此只能在查找解决scheme更好

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

我的应用程序现在升级,因为它应该与我的解决方法,并通过判断多久的时间,因为这个缺陷提出了原来它可能永远不会被修复…

对不起,这不是一个完整的解决scheme,但至less是一个前进的方向。

我有类似的问题。 但是,我有意删除当前的数据库作为恢复的一部分。

我认为正在发生的事情是,SQLite将数据库标记为只读,以便file unlinked while open:提供对file unlinked while open:保护file unlinked while open:

还原后,任何更新将attempt to write a readonly database (code 1032)失败。

我的解决scheme是重新实例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方法的调用是onUpgrade / versions的等价/ getaround。 它根据与实际数据库进行比较的伪模式添加表和列。

完整的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); } } 

我有一个类似的问题,这实际上令人讨厌的是,它发生在任何时候,很难复制的确切条件,使之发生。 我只closuresMainActivity类的ondestroy方法中的DDBB。 我所做的就是在每次使用db时添加一个try / catch,并添加下面的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(); } 

到目前为止,我仍然有一些这个错误,我还无法找出原因,但至less我的应用程序不会崩溃,它恢复了它必须做的事情。 我看不到文件是否被删除,但这个解决scheme正在为我工​​作