关于可写数据库上的数据库更新的SQLiteReadOnlyDatabaseException

时间:2016-07-17 07:57:19

标签: android android-sqlite sqliteopenhelper

每当我更新我的数据库时,我都会收到此错误。但是,当我重新运行应用程序时,数据库会更新。

android.database.sqlite.SQLiteReadOnlyDatabaseException: attempt to write a readonly database (code 1032)[

代码:

 public DBAdapter(Context context) {
    super(context, DATABASE_NAME, null, DATABASE_VERSION);
    ctx = context;
    db = getWritableDatabase();
}

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    if (oldVersion != newVersion) {
        ctx.deleteDatabase(DATABASE_NAME);
        new DBAdapter(ctx);
    } else {
        super.onUpgrade(db, oldVersion, newVersion);
    }
}

作为建议的答案之一,我也添加了这个:

    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

BTW:我正在使用SQLiteAssetHelper来创建预建数据库

2 个答案:

答案 0 :(得分:1)

这不是解决此问题的解决方案,而是一种解决方法。

 public DBAdapter(Context context) {
    super(context, DATABASE_NAME, null, DATABASE_VERSION);
    ctx = context;
    try {
        db = getWritableDatabase();
    } catch (SQLiteReadOnlyDatabaseException e){
        ctx.startActivity(new Intent(ctx, MainActivity.class));
    }
}

@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
    if (oldVersion != newVersion) {
        ctx.deleteDatabase(DATABASE_NAME);
        new DBAdapter(ctx);
    } else {
        super.onUpgrade(db, oldVersion, newVersion);
    }
}

首次初始化适配器时,会创建一个可写的数据库。然后调用onUpgrade。删除数据库时,适配器将重新初始化。但是,db的连接不会被删除并且因此持续存在,第二次执行db = getWritableDatabase(); SQLiteReadOnlyDatabaseException时。重新启动初始化DBAdapter的原始活动。现在,适配器已重新初始化,并且未调用onUpgrade方法,因此SQLiteReadOnlyDatabaseException不会发生。

所有这些过程发生得非常快,用户体验在我的情况下也不会变坏。

注意:new DBAdapter(ctx);似乎没有必要,deleteDatabase似乎重新创建了适配器。但是为了谨慎起见,我写了这行代码。

我很想获得有关此错误的原因和解决方案的一些信息。

答案 1 :(得分:1)

我在Android SQLite数据库中遇到过类似的问题。我很久以前在https://code.google.com/p/android/issues/detail?id=174566提交了一份关于它的错误报告。本报告更详细地讨论了我对原因的调查结果。我不确定它是否与您的问题有关,但似乎有一些共同点。

总结一下,我的调试表明Android打开数据库文件,调用onUpgrade(),如果在onUpgrade()调用期间替换数据库文件,则Android端文件句柄指向旧文件,从而导致从onUpgrade()返回并且Android尝试访问旧文件时崩溃的应用程序。

以下是我用来解决这个问题的一些代码:

当应用程序启动时,我在onCreate()中执行了此操作:

Thread t = new Thread(new Runnable() {
  @Override
  public void run() {
    Context context = getApplicationContext();
    DBReader.copyDB(MainActivity.this);
    DBReader.initialize(context);
  }
});
t.start();

这会导致数据库文件的更新在应用程序启动时在后台发生,并且用户对敬畏的应用程序感到敬畏。因为我的文件相当大,需要一段时间才能复制。请注意,我完全避免在onUpgrade()中执行任何操作。

DBReader是我自己的类,其主要代码是:

  SharedPreferences prefs = context.getSharedPreferences(Const.KEY_PREFERENCES, Context.MODE_PRIVATE);
  //here we have stored the latest version of DB copied
  String dbVersion = prefs.getString(Const.KEY_DB_VERSION, "0");
  int dbv = Integer.parseInt(dbVersion);
  if (checkIfInitialized(context) && dbv == DBHelper.DB_VERSION) {
    return;
  }
  File target = context.getDatabasePath(DBHelper.DB_NAME);
  String path = target.getAbsolutePath();
  //Log.d("Awesome APP", "Copying database to " + path);

  path = path.substring(0, path.lastIndexOf("/"));
  File targetDir = new File(path);
  targetDir.mkdirs();
  //Copy the database from assets
  InputStream mInput = context.getAssets().open(DBHelper.DB_NAME);
  OutputStream mOutput = new FileOutputStream(target.getAbsolutePath());
  byte[] mBuffer = new byte[1024];
  int mLength;
  while ((mLength = mInput.read(mBuffer)) > 0) {
    mOutput.write(mBuffer, 0, mLength);
  }
  mOutput.flush();
  mOutput.close();
  mInput.close();
  SharedPreferences.Editor edit = prefs.edit();
  edit.putString(Const.KEY_DB_VERSION, "" + DBHelper.DB_VERSION);
  edit.apply();

和checkIfInitialized()的代码:

public static synchronized boolean checkIfInitialized(Context context) {
  File dbFile = context.getDatabasePath(DBHelper.DB_NAME);
  return dbFile.exists();
}

因此,为了简化故事,我完全避免使用onUpgrade()并实现了我自己的自定义升级功能。这样可以避免Android操作系统因onUpgrade()更改数据库导致的旧文件和无效文件句柄崩溃的问题。

有点奇怪,但是,对于onUpgrade()导致操作系统崩溃你的应用程序,如果你实际上最终升级数据库文件在一个旨在让你升级数据库的函数。谷歌对错误报告的评论是在几年之后发布的,因此我不再使用原始的崩溃代码来进行简单的概念验证。

您的问题可能略有不同,因为您没有复制数据库文件,但您似乎仍在修改它,因此根本原因可能类似。