這段時間公司項目,涉及到數據緩存,由於需要緩沖的數據太多、太大,通過網絡請求,再緩存到本地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對應文件夾下,然后再進行讀取,這樣一來,效率極低,並且對數據庫做完改動后,改動也沒有辦法同步到源數據庫中(也就是數據庫會自動還原)。那么這個問題該輸入解決呢?以后有時間,再整理一篇博客,說一些這個問題。