这段时间公司项目,涉及到数据缓存,由于需要缓冲的数据太多、太大,通过网络请求,再缓存到本地sqlite数据库,太费时间,消耗流量。所以准备先在本地保存一个标准版sqlite数据库(包含数据),打包到apk文件里,以后需要的操作就是更新数据,这样一来,请求和操作的数据就很小了。
那么问题来了,如何把标准版的sqlite数据库文件(db格式)从内部存储空间里面导出,然后放到项目中assets文件夹下?
想从内部存储空间里拷贝东西,首先要root,手机要root,APP也要获得root权限。这篇博客不讲如何通过root拷贝内容,因为这种办法真的很蠢,root手机会给手机带来不可逆的改变,如果手机很贵的话,尽量不要root;即便手机root后,app也好获得root权限,很麻烦,而且国内手机厂商rom不同,很多手机即便按照步骤一步步root了,也不能查看和赋值内部存储空间里的文件。
那么怎么实现呢?一个简单到可笑的办法(获取这是android系统安全性上的漏洞),在代码中,我们可以访问、读取内部存储空间里的东西,也可以读写SD卡等外部存储空间(要添加相应权限),那么我们就可以通过IO的方法,将内部存储空间中的文件,拷贝到外部存储空间,然后再从外部存储空间里拷贝我们需要的东西就OK了。
实现方式:
1、添加系统权限:
在AndroidManifest.xml中添加读写SD卡等操作权限:
<!-- 写入扩展存储,向扩展卡写入数据--> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
2、从内部存储空间通过IO的方式拷贝到SD卡和外部存储空间:
这个时候,已经通过网络请求,将拿到的JSON数据写入到sqlite数据库中了。直接拷贝就好:
/** * 拷贝内部存储空间的数据库到外部 * * @throws FileNotFoundException */ public void copyDBFile() throws FileNotFoundException { File toDir = new File(Field.DB_PATH_SD); //外部存储文件夹 if (!toDir.exists()) { toDir.mkdirs(); } File toDb = new File(Field.DB_PATH_SD + App.BaseDB.dbName); //外部存储数据库 File fromDir = new File(Field.DB_PATH + App.BaseDB.dbName); //内部存储数据库 InputStream is; OutputStream os; is = new FileInputStream(fromDir); os = new FileOutputStream(toDb); byte[] buffer = new byte[1024]; int length; try { /** * 拷贝过程 */ while ((length = is.read(buffer, 0, buffer.length)) > 0) { os.write(buffer, 0, length); } os.flush(); os.close(); is.close(); } catch (IOException e) { e.printStackTrace(); } }
相关常量:
/** * 内部数据库路径 */ public static final String DB_PATH = File.separator + "data" + Environment.getDataDirectory().getAbsolutePath() + File.separator + MyApplication.getInstance().getPackageName() + File.separator + "databases" + File.separator; /** * 外部数据库路径 */ public static final String DB_PATH_SD = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "tdr" + File.separator; public static class BaseDB{ public static final int version=1; public static final String dbName="base.db"; }
3、在需要的地方,调用copyDBFile()
try { //拷贝工具类通过单例获取 BaseDBHelper.getInstance().copyDBFile(); } catch (FileNotFoundException e) { Log.e(TAG, "fileNotFount"); Log.e(TAG, e.getMessage()); Log.e(TAG, e.toString()); e.printStackTrace(); }
执行完成以后,就可以直接在手机文件夹里看到需要的db文件了:
如此,就可以在非root的前提下,拷贝出内部存储空间里的文件了。不得不说,这也许是android无意的一个安全漏洞吧!因为这个真的很蠢,就好比:一家人有一个保险柜,里面放着家里的财产,贼要是想从保险柜里直接拿走财物是不可以的,但是如果贼先把钱从保险柜里拿到客厅,再从客厅拿走,就完全可以,这样拿走,不仅没有约束,家里主人还会主动把保险柜密码告诉你,把家里的大门为你敞开。。。。。。
4、后记
我之前介绍公司项目的时候,提到:我会把db文件通过离线的方式拷贝到项目的assets文件夹下,然后需要操作数据库的时候,就操作assets文件夹里的数据库。但是这样做会有一个很大的性能上的问题:
sqlite数据库的读写,只能读写/data/...中的数据库文件,如此一来,每次读写或修改assets中的数据库的时候,就要先把assets的数据库拷贝到data对应文件夹下,然后再进行读取,这样一来,效率极低,并且对数据库做完改动后,改动也没有办法同步到源数据库中(也就是数据库会自动还原)。那么这个问题该输入解决呢?以后有时间,再整理一篇博客,说一些这个问题。