App 帶預設資料(如字典、地圖、配置)常放在 assets/ 內的 .db 檔。首次啟動需複製至 /data/data/pkg/databases/ 供 SQLiteDatabase 開啟;後續直接讀寫該副本,避免重複複製且保留使用者修改。AssetsDatabaseManager 以單例模式封裝此流程,提供線程安全的快取與生命週期管理。
getDatabase(name) 首次呼叫才複製,不拖慢 Application 啟動。Map<String, SQLiteDatabase> 避免重複開啟同一資料庫。SharedPreferences 記錄「已複製」旗標,配合 File.exists() 雙重驗證,防止檔案被誤刪後不再複製。NO_LOCALIZED_COLLATORS 避免舊版相容問題。closeDatabase(name) / closeAllDatabase() 釋放連線,防洩漏。// 1. Application.onCreate() 初始化一次
AssetsDatabaseManager.initManager(getApplication());
// 2. 任意處取得管理器
AssetsDatabaseManager mg = AssetsDatabaseManager.getManager();
// 3. 以 assets 檔名取得 SQLiteDatabase 直接操作
SQLiteDatabase db = mg.getDatabase("dict.db");
db.execSQL("INSERT INTO words(word) VALUES('hello')");
Cursor c = db.rawQuery("SELECT * FROM words", null);
// ... 業務邏輯
// 4. 退出時釋放(可選,系統回收進程會自動關閉)
AssetsDatabaseManager.closeAllDatabase();
// 複製 assets -> 資料目錄
private boolean copyAssetsToFilesystem(String assetsSrc, String des) {
InputStream is = context.getAssets().open(assetsSrc);
OutputStream os = new FileOutputStream(des);
byte[] buffer = new byte[1024];
int len;
while ((len = is.read(buffer)) > 0) os.write(buffer, 0, len);
is.close(); os.close();
return true;
}
// 取得資料庫(雙重檢查鎖簡化版)
public SQLiteDatabase getDatabase(String dbfile) {
if (databases.containsKey(dbfile)) return databases.get(dbfile);
// ... 複製邏輯 ...
SQLiteDatabase db = SQLiteDatabase.openDatabase(path, null, SQLiteDatabase.NO_LOCALIZED_COLLATORS);
databases.put(dbfile, db);
return db;
}
新專案建議直接用 Room 的 createFromAsset("db/file.db")(Room 2.2+)或自訂 SQLiteOpenHelper 在 onCreate 複製 assets。本 Manager 適合舊專案快速整合或輕量級專案無 Room 依賴時使用。