1. 程式人生 > >小白到大神,你需要了解的 sqlite 最佳實踐

小白到大神,你需要了解的 sqlite 最佳實踐

本文微信公眾號「AndroidTraveler」首發。

背景

本文是對一篇英文文件的翻譯,原文請見文末連結。


併發資料庫訪問

假設你實現了自己的 SQLiteOpenHelper。

public class DatabaseHelper extends SQLiteOpenHelper { ... }

現在你想要在多個執行緒中對資料庫寫入資料。

 // Thread 1
 Context context = getApplicationContext();
 DatabaseHelper helper = new DatabaseHelper(context);
 SQLiteDatabase database = helper.getWritableDatabase();
 database.insert(…);
 database.close();

 // Thread 2
 Context context = getApplicationContext();
 DatabaseHelper helper = new DatabaseHelper(context);
 SQLiteDatabase database = helper.getWritableDatabase();
 database.insert(…);
 database.close();

你將會在你的 logcat 中發現下面資訊,並且你的其中一個改變不會寫入資料庫:

android.database.sqlite.SQLiteDatabaseLockedException: database is locked (code 5)

產生這個錯誤的原因是因為,每次你建立新的 SQLiteOpenHelper 物件,實際上你建立了新的資料庫連線。如果你嘗試從不同的連線同時對資料庫寫入資料,其中一個會失敗。

為了在多執行緒使用資料庫,我們要確保只使用一個數據庫連線。

讓我們構造單例類 DatabaseManager,它會持有並返回單個 SQLiteOpenHelper 物件。

public class DatabaseManager {

    private static DatabaseManager instance;
    private static SQLiteOpenHelper mDatabaseHelper;

    public static synchronized void initializeInstance(SQLiteOpenHelper helper) {
        if (instance == null) {
            instance = new DatabaseManager();
            mDatabaseHelper = helper;
        }
    }

    public static synchronized DatabaseManager getInstance() {
        if (instance == null) {
            throw new IllegalStateException(DatabaseManager.class.getSimpleName() +
                    " is not initialized, call initialize(..) method first.");
        }

        return instance;
    }

    public synchronized SQLiteDatabase getDatabase() {
        return mDatabaseHelper.getWritableDatabase();
    }

}

在多個執行緒中對資料庫寫入資料,修改後的程式碼如下所示。

// In your application class
DatabaseManager.initializeInstance(new DatabaseHelper());

// Thread 1
DatabaseManager manager = DatabaseManager.getInstance();
SQLiteDatabase database = manager.getDatabase()
database.insert(…);
database.close();

// Thread 2
DatabaseManager manager = DatabaseManager.getInstance();
SQLiteDatabase database = manager.getDatabase()
database.insert(…);
database.close();

這會帶來另一個奔潰。

java.lang.IllegalStateException: attempt to re-open an already-closed object: SQLiteDatabase

由於我們只使用了一個數據庫連線,Thread1Thread2getDatabase() 方法都會返回同一個 SQLiteDatabase 物件例項。可能發生的場景是 Thread1 關閉了資料庫,然而 Thread2 還在使用它。這也就是為什麼我們會有 IllegalStateException 的奔潰的原因。

我們需要確保沒有人正在使用資料庫,這個時候我們才可以關閉它。stackoveflow 上有人推薦永遠不要關閉你的 SQLiteDatabase。這會讓你看到下面的 logcat 資訊。所以我一點也不認為這是一個好的想法。

Leak found
Caused by: java.lang.IllegalStateException: SQLiteDatabase created and never closed

實戰例子

一種可能的解決方案是使用計數器跟蹤開啟/關閉的資料庫連線。

public class DatabaseManager {

    private AtomicInteger mOpenCounter = new AtomicInteger();

    private static DatabaseManager instance;
    private static SQLiteOpenHelper mDatabaseHelper;
    private SQLiteDatabase mDatabase;

    public static synchronized void initializeInstance(SQLiteOpenHelper helper) {
        if (instance == null) {
            instance = new DatabaseManager();
            mDatabaseHelper = helper;
        }
    }

    public static synchronized DatabaseManager getInstance() {
        if (instance == null) {
            throw new IllegalStateException(DatabaseManager.class.getSimpleName() +
                    " is not initialized, call initializeInstance(..) method first.");
        }

        return instance;
    }

    public synchronized SQLiteDatabase openDatabase() {
        if(mOpenCounter.incrementAndGet() == 1) {
            // Opening new database
            mDatabase = mDatabaseHelper.getWritableDatabase();
        }
        return mDatabase;
    }

    public synchronized void closeDatabase() {
        if(mOpenCounter.decrementAndGet() == 0) {
            // Closing database
            mDatabase.close();

        }
    }
}

然後如下所示來使用。

SQLiteDatabase database = DatabaseManager.getInstance().openDatabase();
database.insert(...);
// database.close(); Don't close it directly!
DatabaseManager.getInstance().closeDatabase(); // correct way

每當你需要使用資料庫的時候你應該呼叫 DatabaseManager 類的 openDatabase() 方法。在這個方法裡面,我們有一個計數器,用來表明資料庫開啟的次數。如果計數為 1,意味著我們需要建立新的資料庫連線,否則,資料庫連線已經建立。

對於 closeDatabase() 方法來說也是一樣的。每次我們呼叫這個方法的時候,計數器在減少,當減為 0 的時候,我們關閉資料庫連線。

現在你能夠使用你的資料庫並且確保是執行緒安全的。


原文:Concurrent database access

由於本人翻譯水平有限,如果你有更好的翻譯文案,歡迎在 GitHub 提 PR。
這邊建了一個倉庫,歡迎提 PR 投稿一些好的文章翻譯。
併發資料庫訪問

相關推薦

需要sqlite 最佳實踐

本文微信公眾號「AndroidTraveler」首發。 背景 本文是對一篇英文文件的翻譯,原文請見文末連結。 併發資料庫訪問 假設你實現了自己的 SQLiteOpenHelper。 public class DatabaseHelper extends SQLiteOpenHelper { ... } 現在

從計算機視覺的變為需要經歷這七個階段

如果想要機器能夠進行思考,我們需要先教會它們去看。  李飛飛——Director of Stanford AI Lab and Stanford Vision Lab 計算機視覺(Computer vision)是一門研究如何使機器“看”的科學,更進一步的說,就

【廣州服務器回收】服務器維護過程中需要的5個常識

windows ron 就是 圖片 渲染 天都 驚人的 領域 其他人 大多數人認為,服務器僅僅是升級後的臺式機。但任何在數據中心工作過的人都知道,它們的差別挺大的。 盡管web服務器每天都要承擔數百萬訪問者的負載,但對於普通用戶來說,它們仍然神秘莫測。以下是關於服務器你可能

資料分析師:資料建模需要形式

“沒有免費的午餐”理論已經應用於機器學習領域,無偏的狀態好於(如一個具體的演算法)任何其他可能的問題(資料集)出現的平均狀態。沒有一個演算法適合每一個問題。但是經 過資料探勘處理的問題或資料集絕不是隨機的,也不是所有可能問題的均勻分佈,他們代表的是一個有偏差的樣本,那麼為什麼要應用NFL的結論?答案涉及到上

關於驗證碼需要這些

使用場景 strong 兼容 logs nbsp 簽名 業務 swe yun (一)什麽是驗證碼業務id? captchaId, 驗證碼唯一標識,公開可見,用於區分不同的驗證碼使用場景,如登錄、投票、發帖等。可在易盾管理中心驗證碼業務ID管理處自行創建。2017年6月1

微服務架構盛行的時代需要點 Spring Boot

措辭 理由 直接 響應 con 可伸縮 角度 徹底 構建 隨著互聯網的高速發展,龐大的用戶群體和快速的需求變化已經成為了傳統架構的痛點。 在這種情況下,如何從系統架構的角度出發,構建出靈活、易擴展的系統來快速響應需求的變化,同時,隨著用戶量的增加,如何保證系統的穩定性、高可

身為前端開發工程師需要的搜尋引擎優化SEO.

網站url網站建立具有良好描述性、規範、簡單的url,有利於使用者更方便的記憶和判斷網頁的內容,也有利於搜尋引擎更有效的抓取您的網站。網站設計之初,就應該有合理的url規劃。 處理方式: 1.在系統中只使用正常形式url,不讓使用者接觸到非正常形式的url。 2.不把session id、統計程式碼等不必

身為前端開發工程師需要的搜索引擎優化SEO.

ide 收藏 htm des 頻道 最適 主題 開發工程師 用戶 網站url網站創建具有良好描述性、規範、簡單的url,有利於用戶更方便的記憶和判斷網頁的內容,也有利於搜索引擎更有效的抓取您的網站。網站設計之初,就應該有合理的url規劃。 處理方式: 1.在系統中只使用正

數字貨幣交易需要這幾所海外交易所!

現在數字貨幣非常的火熱,而數字貨幣交易所作為數字貨幣交易的地方也吸引了許多人的關注,不過大部分人只知道兩三個常用的交易所,下面小編就講一下世界上最大的八個加密貨幣交易所。 1.GDAX 傳說中的G網,GDAX是Coinbase旗下的全球數字資產交易所,是美國第一家持有正規牌照的比特幣

進入職場之前需要這個致命弱點

這裡其實隱含了一個資訊:你們已經從學生角色慢慢進入職場,或即將成為一個職場新人。 首先要明確思維本身無對錯高下,重要的是:不同的場合或不同的身份,應該用不同的思維去解決問題。正因如此,你才會被要求具備職場思維。 學生思維和職場思維最大的區別就是——職場從現實利益出發,看重最後結果

關於深度學習優化器 optimizer 的選擇需要這些

在很多機器學習和深度學習的應用中,我們發現用的最多的優化器是 Adam,為什麼呢? 在 keras 中也有 SGD,RMSprop,Adagrad,Adadelta,Adam 等: https://keras.io/optimizers/ 我們可以發

安裝 Linux 與 Windows 10 雙系統需要的一切

該選Windows 10還是Linux Mint?魚與熊掌當然可以兼得,但咱們得掌握點小技巧才能順利搞定。 Windows 10絕不是唯一一款值得我們安裝在自己計算機之上的免費作業系統。Linux只靠一塊U盤就能順利執行,而且完全無需對現有系統作出任何修改。當然,如果大家打

學習 webpack 前需要的那些概念

什麼是webpack 關於什麼是webpack,一般的教程裡面都會提到webpack是一個模組化打包工作,但是很多初學者沒有模組化這個概念,所以往往在第一步就被攔住了。所以在講什麼是webpack之前,我想先講一下和模組化相關的概念。 javascript的執行環境 因

入坑幣圈需要的數字貨幣錢包那些事兒

問題 strong fff 區塊 重置密碼 單詞 cto 而是 oss 如果你剛剛入坑幣圈,正欲入手數字貨幣,別著急,先靜下心來看一看下面的內容,我猜一定會對你有所幫助。 一、什麽是數字貨幣錢包 很多人說,數字貨幣錢包就是用來裝數字貨幣的,通俗來講這樣理解沒有問題,但實際上

Spring Cloud Config 配置中心實踐過程中需要這些細節!

本文導讀: Spring Cloud Config 基本概念 Spring Cloud Config 客戶端載入流程 Spring Cloud Config 基於訊息匯流排配置 Spring Cloud Config 中的佔位符 Spring Cloud Config 倉庫最佳實踐 Spring Cloud

關於redis需要的幾點!

一、關於 redis key: 1、是二進位制安全的,也就是說,你可以使用任何形式的二進位制序列來作為key,比如一個string,或者一個jpg圖片的資料,需要說明的是,空字串也是一個有效的key。 2、不建議使用過長的key,影響記憶體佔用及資料查效能,對於過長的key,可以通過hash(例如SHA1)處

這些 Drawable 的技巧嗎?

嵌套 工作 足夠 學習 狀態 C4D 日常 anim xxd 一、前言 在 Android 的開發過程中,Drawable 經常會被用到,一般會用 Drawable 為 View 設置一個顯示的效果。而在 Android 下,也提供了很多 Drawable 的默認實現,它

Spring Boot 2.1.0 已釋出7 個重大更新需要

Spring Boot 2.1.0 在 10 月底就釋出了,我們來看下 Spring Boot 2.1.0 都更新了什麼,每一個 Java 技術人都值得關注。 棧長其實早就看到了更新了,現在才有時間來更新下。 1、第三方類庫升級 Hibernate 5.3 Micrometer 1.1 Reacto

Spring Boot 2.1.0 已發布7 個重大更新需要

pool for rep ctu err 自動配置 表示 req spring Spring Boot 2.1.0 在 10 月底就發布了,我們來看下 Spring Boot 2.1.0 都更新了什麽,每一個 Java 技術人都值得關註。 棧長其實早就看到了更新了,現在才有

工業4.0圍觀炒作問題差距發現嗎?

工業4.0這個議題被提出已長達5年之久,如今仍是行業裡炙手可熱的話題。隨著工業 4.0的熱議持續升溫,人們估計工業 4.0 產生的影響將極其巨大。但是要想工業 4.0 能像熱議中那樣全部實現,必須採取相應的行動。 工業4.0的概念為何誕生於德國,絕非偶然。根據德國的經驗,工業4.0的未來實現,基於兩個前提條