Android SQLiteReadOnlyDatabaseException:尝试写入只读数据库(代码1032)
因此,在一些罕见的情况下,我看到了“尝试编写只读数据库”的消息,我无法找出问题所在。我将从日志中的stacktrace开始。。。从时间戳中可以看出,我在尝试写入之前仅1毫秒检查db.isReadOnly()。(等参=真,只读=假) 我的消息来源: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
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);
}
}
}
所以我认为可能有两件事正在发生
虽然目前还没有什么想法,但我愿意尝试任何建议。因此,乍一看,造成这种情况的根本原因似乎是第三方图书馆。除非我弄错了,否则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();
}