app默认的数据库目录是在 data/data/your package/databases目录下,为什么我们需求将数据库移植到sdcard目录下呢,主要是为了解决 用户卸载在安装app数据丢失的问题 如果能移植到sdcard目录下 那么增加了数据的安全性,其实数据库移植无非就是文件的拷贝过程 直接上代码
private static void copy(File currentDb, File backUpDb) {
try {
File sd = Environment.getExternalStorageDirectory();
if (sd.canWrite()) {
if (currentDb.exists()) {
FileChannel src = new FileInputStream(currentDb).getChannel();
FileChannel dst = new FileOutputStream(backUpDb).getChannel();
dst.transferFrom(src, 0, src.size());
src.close();
dst.close();
DBLog.p("dataFile:" + currentDb.length());
DBLog.p("sdcardFile:" + backUpDb.length());
} else {
DBLog.p("currentDb not exists");
}
} else {
DBLog.p("sdcard not write");
}
} catch (Exception e) {
DBLog.p("copy-e:" + e.getMessage());
}
}
下面以greendao为例子,将数据库拷贝之后 需要把默认的读取路径进行更换
需要自己定义一个 DaoContext 实现getDatabasePath方法即可
public class DaoContext extends ContextWrapper
通常情况下这一系列操作是没有什么问题 但是sqlite-v3.7.0之后引入了新的事务设计WAL(Write-Ahead Logging)
修改数据库时会在数据库目录下生成”-wal”和”-shm”两个文件,而且数据库文件并不会实时修改(文件时间戳不变),如果修改数据库之后立刻将数据库文件拷贝到其他目录,查看数据库会发现仍然是过期的旧数据,原因是sqlite把对数据库的修改缓存在”-wal”和”-shm”两个文件中了。
总结下 也就是
v3.7.0之前的数据文件方案,通过一个Journal文件来保证事务即使在断电情况下保持原子性,每次开启一个事务时,首先向Journal文件写入需要修改page的原内容,并且flush到硬盘,然后向数据库文件写入新的内容,最后删掉Journal文件中的内容完成一次事务。
但要注意SQLite的Journal设计与ext3的Journal设计不一样,ext3的Journal保存的是全量可重做日志,SQLite恢复是备份恢复,ext3恢复时是重做流水日志
v3.7.0之后的数据文件方案,首先把修改内容写入日志(-wal),为了提高性能创建一个内存索引(-shm),映射每一个page是否dirty,读取时先看需要的page是否在WAL日志中,然后再读取。当达到一定条件后SQLite会自动将数据flush到数据库文件,也可以通过命令或接口手动flush到数据库文件。
那如何解决呢 大概的思路是 在拷贝数据库之前 先将wal的数据推入到主数据库中
直接上代码 execSQL("PRAGMA wal_checkpoint(FULL)")