Android SQLiteReadOnlyDatabaseException:尝试写入只读数据库(代码1032)

Android SQLiteReadOnlyDatabaseException:尝试写入只读数据库(代码1032),android,sqlite,android-sqlite,Android,Sqlite,Android Sqlite,因此,在一些罕见的情况下,我看到了“尝试编写只读数据库”的消息,我无法找出问题所在。我将从日志中的stacktrace开始。。。从时间戳中可以看出,我在尝试写入之前仅1毫秒检查db.isReadOnly()。(等参=真,只读=假) 我的消息来源: public void insertBatch(LinkedList<WriteQueue.DatabaseRecord> writeQueue) throws Exception { Log.d("AWT", "EventsDbH

因此,在一些罕见的情况下,我看到了“尝试编写只读数据库”的消息,我无法找出问题所在。我将从日志中的stacktrace开始。。。从时间戳中可以看出,我在尝试写入之前仅1毫秒检查db.isReadOnly()。(等参=真,只读=假)

我的消息来源:

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);
        }
    }
}
public void insertBatch(LinkedList writeQueue)引发异常{
Log.d(“AWT”、“EventsDbHelper->insertBatch()”;
if(writeQueue==null){
返回;
}
Iterator it=writeQueue.Iterator();
SQLiteDatabase db=this.getWritableDatabase();
Log.d(“AWT”,String.format(“获取可写数据库(%s):isOpen:(%s)isReadOnly:(%s)内部事务:(%s)”,
db.hashCode()、db.isOpen()、db.isReadOnly()、db.inTransaction());
试一试{
db.beginTransaction();
while(it.hasNext()){
DatabaseRecord=it.next();
ContentValues initialValues=新的ContentValues();
initialValues.put(col1,val1);
initialValues.put(col2,val2);
initialValues.put(col3,val3);
initialValues.put(col4,val4);
Log.d(“AWT”,String.format(“在事务中:获取可写数据库(%s):isOpen:(%s)isReadOnly:(%s)内部事务:(%s)”,
db.hashCode()、db.isOpen()、db.isReadOnly()、db.inTransaction());
插入(DBTBL,null,initialvalue);
}
Log.d(“AWT”,String.format(“正在完成事务:获取可写数据库(%s):isOpen:(%s)isReadOnly:(%s)内部事务:(%s)”,
db.hashCode()、db.isOpen()、db.isReadOnly()、db.inTransaction());
db.setTransactionSuccessful();
}捕获(例外e){
Log.e(标记“将批处理记录插入数据库时出错”,e);
}最后{
试一试{
db.endTransaction();
db.close();
}捕获(例外e){
Log.e(标记,Global.DB_ERROR,e);
}
}
}
所以我认为可能有两件事正在发生

  • 在检查和尝试批量插入之间的1ms内,DB确实被关闭/设置为“只读”
  • isReadOnly对我撒谎,没有准确地报告数据库的状态
  • 数据库在我的插入过程中被删除!请参阅上面日志的最后一行。我为SQLite打开了严格的日志记录,并注意到了上述情况。我怀疑第三方库可能会删除我所有的数据库

  • 虽然目前还没有什么想法,但我愿意尝试任何建议。

    因此,乍一看,造成这种情况的根本原因似乎是第三方图书馆。除非我弄错了,否则Tagit by 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
    
    因此,我的数据库文件被取消链接。奇怪的对getWritableDatabase()的下一次调用会再次创建它,然后就可以了,直到该应用被终止并重新启动,此时它会被删除并重新创建


    如果我能找出导致断开链接的确切原因,我会更新此信息。

    我或多或少遇到了相同的问题,我发现了一个有意义的公开缺陷

    我的解决方法——虽然不是最好的解决方案——是永远不要单步执行数据库修订并自己跟踪,因此永远不要调用
    onUpgrade()
    ,并在更新应用程序时手动执行升级

    或者,如果您有一个只读的小DB,您可以在
    DBHelper
    类中的每个
    onCreate()
    上触发资产中DB的副本,但如果文件系统已满,这可能会产生不必要的问题,因此只需在寻找更好的解决方案时执行此操作

    @覆盖
    public void onCreate(SQLiteDatabase db){
    //问题174566的解决方法
    myContext.deleteDatabase(数据库名称);
    试一试{
    copyDataBase();
    }
    捕获(IOE异常){
    System.out.println(“IOException”+e.getLocalizedMessage());
    }
    }
    
    我的应用程序现在升级,因为它应该与我的解决方案,并判断多久,因为这个缺陷是最初提出,它可能永远不会得到修复


    很抱歉,这不是问题的完整解决方案,但至少是一个前进的方向。

    我也遇到过类似的问题。但是,作为恢复的一部分,我故意删除了当前数据库

    我认为正在发生的是,SQLite将数据库标记为只读,以便提供保护,防止打开时
    文件被解除链接:

    还原后,任何更新都将失败,并尝试写入只读数据库(代码1032)

    我的解决方案是重新实例化DBHelper。为此,我添加了一个调用的
    重新打开
    方法

    e、 g

    然后我使用

                    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);
        }
    }
    
    /**
    *数据库助手
    */
    @抑制警告(“弱化访问”)
    类DBHelper扩展了SQLiteOpenHelper{
    私有静态最终字符串LOGTAG=“SW DBHelper”;
    私有静态最终字符串DBNAME=DBConstants.DATABASE_NAME;
    已创建私有静态最终字符串dbc=
    “001I数据库”+DBNAME+“已创建。”;
    私有静态最终字符串dbunusable=
    “002E数据库”+DBNAME+
    “已设置为不可用(根据架构)。”;
    私有静态最终字符串dbexpanded=
    “003I数据库”+DBNAME+“已扩展。”;
    已跳过私有静态最终字符串dbexpander=
    
                    if(copytaken && origdeleted && restoredone) {
                        DBHelper.reopen(context);
                        DBHelper.getHelper(context).expand(null,true);
                    }
    
    /**
     * 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);
        }
    }
    
    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();
    }