Android 2.2 設備偶發 SQLiteException: database locked,API 文檔警告:getReadableDatabase() 可能耗時,不應在主線程調用。
「此方法可能需要很長時間返回,不應從應用主線程調用,包括 ContentProvider.onCreate()」
原因:首次調用時會檢查數據庫完整性、執行 onCreate/onUpgrade、獲取文件鎖、WAL 模式檢查點等,可能阻塞主線程導致 ANR。
並非主線程調用直接導致,而是並發訪問衝突:
getWritableDatabase),另一線程嘗試讀 (getReadableDatabase)close()) 導致鎖未釋放| 方案 | 優點 | 缺點 | 適用場景 |
|---|---|---|---|
| 單例 SQLiteOpenHelper + 同步鎖 | 簡單、兼容舊代碼 | 主線程仍可能阻塞、需手動管理事務 | 輕量舊專案 |
| 子線程異步獲取 (AsyncTask/Executor) | 不阻塞主線程 | 回調處理複雜、生命周期管理難 | 過渡方案 |
| Room 資料庫 (強烈推薦) | 編譯期驗證、主線程禁止 IO、自動遷移、RxJava/Flow/LiveData 整合 | 需遷移成本 | 所有新/重構專案 |
// Room 自動在背景線程執行查詢
@Dao
interface UserDao {
@Query("SELECT * FROM users")
LiveData> getAllUsers(); // 返回 LiveData,自動在 IO 線程查詢
@Insert
suspend fun insertUsers(users: List); // 協程掛起函數,自動切換 IO 線程
}
// 主線程直接觀察,零風險
viewModel.users.observe(this) { users ->
adapter.submitList(users)
}
synchronized(helper) { db = helper.getWritableDatabase(); ... }db.enableWriteAheadLogging() 提升並發讀寫性能try-with-resources (API 19+)AsyncQueryHandler 或自定義線程池文檔警告是為防 ANR,database locked 是並發衝突。徹底解決請遷移 Room,舊專案加鎖+WAL+單例可緩解。