SQLite 在打開DB文件時(shí)铣鹏,如果遇到打不開的情況讽膏,會(huì)刪除DB 文件镐侯,有點(diǎn)兇殘欠窒。
我們來查看源碼
- 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 出錯(cuò)時(shí)候的處理 弯汰,打開 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
的處理方式与纽,如果這個(gè)數(shù)據(jù)庫打不開就會(huì)刪除這個(gè)DB文件堰怨,如果當(dāng)前DB 有 attach 其他數(shù)據(jù)庫的話芥玉,也有可能會(huì)被刪除,所以開發(fā)者應(yīng)該提供自己的DatabaseErrorHandler
- 3备图、SQLiteOpenHelper
我們通過繼承SQLiteOpenHelper 來使用SQLite 數(shù)據(jù)庫 【詳見:SQLite學(xué)習(xí)一灿巧、基礎(chǔ)使用】;
我們提供自己的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 時(shí)的修改
- 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());
}
同樣的,如果這個(gè)數(shù)據(jù)庫打不開就會(huì)刪除這個(gè)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 數(shù)據(jù)庫 【詳見:SQLite學(xué)習(xí)一家破、基礎(chǔ)使用】颜说;
我們提供自己的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 時(shí)的修改
獲取 *Database 的實(shí)例時(shí)
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;
}