SQLite 数据库打开异常时删除DB文件
2019-07-23 本文已影响0人
沐左
SQLite 在打开DB文件时,如果遇到打不开的情况,会删除DB 文件,有点凶残。
我们来查看源码
- 1、
android.database.sqlite.SQLiteDatabase
private SQLiteDatabase(String path, int openFlags, CursorFactory cursorFactory,
DatabaseErrorHandler errorHandler) {
mCursorFactory = cursorFactory;
mErrorHandler = errorHandler != null ? errorHandler : new DefaultDatabaseErrorHandler();
mConfigurationLocked = new SQLiteDatabaseConfiguration(path, openFlags);
}
我们需要看一下SQLite 在Database 出错时候的处理 ,打开 DefaultDatabaseErrorHandler
- 2、
android.database.DefaultDatabaseErrorHandler
public final class DefaultDatabaseErrorHandler implements DatabaseErrorHandler {
private static final String TAG = "DefaultDatabaseErrorHandler";
/**
* defines the default method to be invoked when database corruption is detected.
* @param dbObj the {@link SQLiteDatabase} object representing the database on which corruption
* is detected.
*/
public void onCorruption(SQLiteDatabase dbObj) {
Log.e(TAG, "Corruption reported by sqlite on database: " + dbObj.getPath());
// is the corruption detected even before database could be 'opened'?
if (!dbObj.isOpen()) {
// database files are not even openable. delete this database file.
// NOTE if the database has attached databases, then any of them could be corrupt.
// and not deleting all of them could cause corrupted database file to remain and
// make the application crash on database open operation. To avoid this problem,
// the application should provide its own {@link DatabaseErrorHandler} impl class
// to delete ALL files of the database (including the attached databases).
deleteDatabaseFile(dbObj.getPath());
return;
}
List<Pair<String, String>> attachedDbs = null;
try {
// Close the database, which will cause subsequent operations to fail.
// before that, get the attached database list first.
try {
attachedDbs = dbObj.getAttachedDbs();
} catch (SQLiteException e) {
/* ignore */
}
try {
dbObj.close();
} catch (SQLiteException e) {
/* ignore */
}
} finally {
// Delete all files of this corrupt database and/or attached databases
if (attachedDbs != null) {
for (Pair<String, String> p : attachedDbs) {
deleteDatabaseFile(p.second);
}
} else {
// attachedDbs = null is possible when the database is so corrupt that even
// "PRAGMA database_list;" also fails. delete the main database file
deleteDatabaseFile(dbObj.getPath());
}
}
}
private void deleteDatabaseFile(String fileName) {
if (fileName.equalsIgnoreCase(":memory:") || fileName.trim().length() == 0) {
return;
}
Log.e(TAG, "deleting the database file: " + fileName);
try {
SQLiteDatabase.deleteDatabase(new File(fileName));
} catch (Exception e) {
/* print warning and ignore exception */
Log.w(TAG, "delete failed: " + e.getMessage());
}
}
}
我们可以看到 DefaultDatabaseErrorHandler
的处理方式,如果这个数据库打不开就会删除这个DB文件,如果当前DB 有 attach 其他数据库的话,也有可能会被删除,所以开发者应该提供自己的DatabaseErrorHandler
- 3、SQLiteOpenHelper
我们通过继承SQLiteOpenHelper 来使用SQLite 数据库 【详见:SQLite学习一、基础使用】;
我们提供自己的DatabaseErrorHandler
public class WyhcjgOpenHelper extends SQLiteOpenHelper {
public WyhcjgOpenHelper(Context context, String path) {
super(context, path, null, VERSION, null, new DatabaseErrorHandler() {
@Override
public void onCorruption(SQLiteDatabase sqLiteDatabase) {
Logger.t(TAG).i("WyhcjgOpenHelper sqlite onCorruption " + sqLiteDatabase.getPath());
}
});
this.mContext = context;
}
}
使用sqlcipher 时的修改
- 1、
net.sqlcipher.database.SQLiteDatabase
...
public static SQLiteDatabase openOrCreateDatabase(File file, String password, SQLiteDatabase.CursorFactory factory, SQLiteDatabaseHook databaseHook, DatabaseErrorHandler errorHandler) {
return openOrCreateDatabase(file == null?null:file.getPath(), password, factory, databaseHook, errorHandler);
}
...
- 2、
net.sqlcipher.DefaultDatabaseErrorHandler
public void onCorruption(SQLiteDatabase dbObj) {
Log.e(this.TAG, "Corruption reported by sqlite on database, deleting: " + dbObj.getPath());
if(dbObj.isOpen()) {
Log.e(this.TAG, "Database object for corrupted database is already open, closing");
try {
dbObj.close();
} catch (Exception var3) {
Log.e(this.TAG, "Exception closing Database object for corrupted database, ignored", var3);
}
}
this.deleteDatabaseFile(dbObj.getPath());
}
同样的,如果这个数据库打不开就会删除这个DB文件。
- 3、
net.sqlcipher.database.SQLiteOpenHelper
public SQLiteOpenHelper(Context context, String name, CursorFactory factory, int version, SQLiteDatabaseHook hook, DatabaseErrorHandler errorHandler) {
this.mDatabase = null;
this.mIsInitializing = false;
if(version < 1) {
throw new IllegalArgumentException("Version must be >= 1, was " + version);
} else if(errorHandler == null) {
throw new IllegalArgumentException("DatabaseErrorHandler param value can't be null.");
} else {
this.mContext = context;
this.mName = name;
this.mFactory = factory;
this.mNewVersion = version;
this.mHook = hook;
this.mErrorHandler = errorHandler;
}
}
- 4、SQLiteOpenHelper
我们通过继承SQLiteOpenHelper 来使用SQLite 数据库 【详见:SQLite学习一、基础使用】;
我们提供自己的DatabaseErrorHandler
public class WyhcjgOpenHelper extends SQLiteOpenHelper {
public WyhcjgOpenHelper(Context context, String path) {
super(context, path, null, VERSION, null, new DatabaseErrorHandler() {
@Override
public void onCorruption(SQLiteDatabase sqLiteDatabase) {
Logger.t(TAG).i("WyhcjgOpenHelper sqlite onCorruption " + sqLiteDatabase.getPath());
}
});
this.mContext = context;
}
}
使用Room 时的修改
获取 *Database 的实例时
TaskDatabase extends RoomDatabase
...
public synchronized static TaskDatabase getInstance(byte[] passphrase) {
if (INSTANCE == null) {
synchronized (TaskDatabase.class) {
if (INSTANCE == null) {
INSTANCE = Room
.databaseBuilder(mContext.getApplicationContext(), TaskDatabase.class, sDbPath)
.openHelperFactory(new HelperFactory(passphrase))
.allowMainThreadQueries()
.addMigrations(migration_1_2)
.build();
}
}
}
return (INSTANCE);
}
new HelperFactory(passphrase)
public class HelperFactory implements SupportSQLiteOpenHelper.Factory {
private byte[] passphrase;
public HelperFactory(byte[] passphrase) {
this.passphrase = passphrase;
}
@Override
public SupportSQLiteOpenHelper create(SupportSQLiteOpenHelper.Configuration configuration) {
return (new Helper(configuration.context, configuration.name, configuration.callback, passphrase));
}
}
android.arch.persistence.db.framework.Helper
OpenHelper(Context context, String name, final Database[] dbRef,
final Callback callback, byte[] passphrase) {
super(context, name, passphrase, CIPHER_SPEC, null, callback.version,
new DatabaseErrorHandler() {
@Override
public void onCorruption(SQLiteDatabase dbObj) {
/*Database db = dbRef[0];
if (db != null) {
callback.onCorruption(db);
}*/
}
});
mCallback = callback;
mDbRef = dbRef;
}