First是判断文件(而不是数据库)header中的魔法头字符串.i、 e.它是一个有效的数据库吗.
只需打开文件并读取前16个字节,它必须是SQLite format 3\000
或53 51 4c 69 74 65 20 66 6f 72 6d 61 74 20 33 00
十六进制.
Second,然后可以判断用户_版本(偏移量60表示4字节),该版本应与数据库版本匹配(从而防止恢复过时的版本).如果使用SQLiteOpenHelper访问数据库,则会根据编译和生成发行版时使用的版本号维护此值.
添加一些无法查看或编辑的"隐藏"元数据.(不确定这是否可行).
Third,您可以再次使用头,但这次是偏移量68(4字节)处的应用程序ID,这将是未使用的.这可以以与版本号类似的方式使用,但您必须实现它的维护(设置/更新).
前两种方法几乎不需要什么,而且可以防止大多数意外情况.
第三个是应用程序ID,它提供了更多的保护,防止使用具有有效版本号的有效SQLite数据库.
没有人能防止故意虐待(为什么这样的意图值得怀疑).然而,这可能会导致一个例外.
如果前3项不足,那么可以打开数据库,询问sqlite_master,查看模式是否符合预期.
也许考虑一下房间使用的元数据.
Room根据@Entity注释的类和存储在Room_master_表中的数据库中的哈希值,根据预期的模式哈希值进行模式判断.这相当于你的元数据方法.
e、 g.当一个Room项目被编译时,在生成的java中,它会有代码,在createAllTables
方法中,比如:-
_db.execSQL("CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)");
_db.execSQL("INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'c9583474ce546ff5ead43c63fe049bc8')");
现在,如果数据库不存在,则存储哈希.如果数据库确实存在,那么它会判断存储在room_master_表中的哈希是否匹配.如果没有,那么,如果对适当的迁移进行了编码,并且版本号已更改,则会调用适当的迁移,如果架构匹配,则会调用存储,否则会引发异常.如果哈希不匹配且没有适当的迁移,则会引发异常(需要迁移),或者如果对FallbackToDetrictiveMigration进行了编码,则会从头开始创建数据库.
因此,房间数据库(如上所述)将包括:-
First的替代方法是使用/覆盖DatabaseErrorHandler's onCorruption方法.
这里有一个方法(尽管相当冗长),它使用这种技术,并额外判断是否有任何表(但不是全部):-
/**************************************************************************
*
* @return false if the backup file is invalid.
*
* determine by creating a differently name database (prefixed with IC),
* openeing it with it's own helper (does nothing) and then trying to
* check if there are tables in the database.
* No tables reflects that file is invalid type.
*
* Note! if an attempt to open an invalid database file then SQLite deletes the file.
*/
private boolean dataBaseIntegrityCheck() {
String methodname = new Object() {
}.getClass().getEnclosingMethod().getName();
LogMsg.LogMsg(LogMsg.LOGTYPE_INFORMATIONAL, LOGTAG, "Invoked", this, methodname);
@SuppressWarnings("UnusedAssignment") final String THIS_METHOD = "dataBaseIntegrityCheck";
//String sqlstr_mstr = "SELECT name FROM sqlite_master WHERE type = 'table' AND name!='android_metadata' ORDER by name;";
Cursor iccsr;
boolean rv = true;
//Note no use having the handler as it actually introduces problems as SQLite assumes that
// the handler will restore the database.
// No need to comment out as handler can be disabled by not not passing it as a parameter
// of the DBHelper
@SuppressWarnings("UnusedAssignment") DatabaseErrorHandler myerrorhandler = new DatabaseErrorHandler() {
@Override
public void onCorruption(SQLiteDatabase sqLiteDatabase) {
}
};
try {
FileInputStream bkp = new FileInputStream(backupfilename);
OutputStream ic = new FileOutputStream(icdbfilename);
while ((copylength = bkp.read(buffer)) > 0) {
ic.write(buffer, 0, copylength);
}
ic.close();
bkp.close();
icfile = new File(icdbfilename);
//Note SQLite will actually check for corruption and if so delete the file
//
IntegrityCheckDBHelper icdbh = new IntegrityCheckDBHelper(this, null, null, 1, null);
SQLiteDatabase icdb = icdbh.getReadableDatabase();
iccsr = icdb.query("sqlite_master",
new String[]{"name"},
"type=? AND name!=?",
new String[]{"table", "android_metadata"},
null, null,
"name"
);
//Check to see if there are any tables, if wrong file type shouldn't be any
//iccsr = icdb.rawQuery(sqlstr_mstr,null);
if (iccsr.getCount() < 1) {
errlist.add("Integrity Check extract from sqlite_master returned nothing - Propsoed file is corrupt or not a database file.");
rv = false;
}
iccsr.close();
icdb.close();
} catch (IOException e) {
e.printStackTrace();
errlist.add("Integrity Check Failed Error Message was " + e.getMessage());
}
if (!rv) {
AlertDialog.Builder notokdialog = new AlertDialog.Builder(this);
notokdialog.setTitle("Invalid Restore File.");
notokdialog.setCancelable(true);
String msg = "File " + backupfilename + " is an invalid file." +
"\n\nThe Restore cannot continue and will be canclled. " +
"\n\nPlease Use a Valid Database Backup File!";
notokdialog.setMessage(msg);
notokdialog.setPositiveButton("OK", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialogInterface, int i) {
}
}).show();
}
// Delete the Integrity Check File (Database copy)
//noinspection ResultOfMethodCallIgnored
icfile.delete();
return rv;
}
- 注意:这包括打开日志(log)记录时的日志(log)记录和消息存储/检索,因此,如果遇到许多消息,可以检索它们.这就是长风的一部分.