前言
有时候我们想把用户的一些数据存储在sdcard里面,因为sdcard里不会因为用户卸载数据被删除或用户用系统自带的清除,但是同样我们自己需要注意添加以下数据清除,这个不是属于现在要讨论的东西。文件什么保存都是很容易的了,但是数据库系统没有提供,默认是保存在当前应用下面的,但是我们还是可以通过一些手段把它保存到sdcard里面的。
查看源码,一切秘密都展现到你的面前
第一种 修改数据库保存根路径
通过查看 SQLiteOpenHelper 创建数据库的代码如下,发现两个关键的地方
- mContext.getDatabasePath(mName).getPath()
- db = mContext.openOrCreateDatabase(mName, ...
跟踪进去看看,一看是什么鬼,怎么都是空的实现。这里就不具体说了,有兴趣的可以看看Android Context的设计。
同时我们发现 ContextWrapper 继承 Context , 而 Application又继承 ContextWrapper , 而 Context 真正的实现在 ContextImpl 中,源码路径: /frameworks/base/core/java/android/app/ContextImpl.java
try {
if (DEBUG_STRICT_READONLY && !writable) {
final String path = mContext.getDatabasePath(mName).getPath();
db = SQLiteDatabase.openDatabase(path, mFactory,
SQLiteDatabase.OPEN_READONLY, mErrorHandler);
} else {
db = mContext.openOrCreateDatabase(mName, mEnableWriteAheadLogging ?
Context.MODE_ENABLE_WRITE_AHEAD_LOGGING : 0,
mFactory, mErrorHandler);
}
} catch (SQLiteException ex) {
if (writable) {
throw ex;
}
Log.e(TAG, "Couldn't open " + mName
+ " for writing (will try read-only):", ex);
final String path = mContext.getDatabasePath(mName).getPath();
db = SQLiteDatabase.openDatabase(path, mFactory,
SQLiteDatabase.OPEN_READONLY, mErrorHandler);
}
创建数据库文件,在我再重新看一遍源码的时候,我竟然发现系统是提供了自定义存储的方法的,注代码里面注释。为啥以前查出来那么多变种的答案呢。
private File validateFilePath(String name, boolean createDirectory) {
File dir;
File f;
//数据库名字如果以 / 开头,会自动截取到最后一个 /当作文件存储的文件夹
if (name.charAt(0) == File.separatorChar) {
String dirPath = name.substring(0, name.lastIndexOf(File.separatorChar));
dir = new File(dirPath);
name = name.substring(name.lastIndexOf(File.separatorChar));
f = new File(dir, name);
} else {
dir = getDatabasesDir();
f = makeFilename(dir, name);
}
if (createDirectory && !dir.isDirectory() && dir.mkdir()) {
FileUtils.setPermissions(dir.getPath(),
FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH,
-1, -1);
}
return f;
}
第二种 重写了SQLiteOpenHelper
项目地址: android-sdcard-helper
据就是重新 getWritableDatabase 和 getReadableDatabase ,创建数据库使用 SQLiteDatabase.openOrCreateDatabase, 默认打开数据库是打开读写的,但是有一点需要注意的。在sdcard存储满的时候,读写的数据库是打不开的,但是可以打开只读的数据库,所以我们默认打开的数据库都是读写,有两个原因:
- 如果打开一个只读的,文件可能会被锁住,再打开读写的会失败
- 只读的数据库转读写的是一个 hide 方法,这里无法调用
最后了,感觉自己需要一点反思。如果好好的看一下相关的代码可能就不需要再重新造一个轮子了,但是还是有一些应用场景的。
谢谢阅读到这里,觉得好的点个赞吧 _