a亚洲精品_精品国产91乱码一区二区三区_亚洲精品在线免费观看视频_欧美日韩亚洲国产综合_久久久久久久久久久成人_在线区

首頁 > 系統 > Android > 正文

Android SQLite3多線程操作問題研究總結

2020-04-11 11:38:46
字體:
來源:轉載
供稿:網友

最近做項目時在多線程讀寫數據庫時拋出了異常,這自然是我對SQlite3有理解不到位的地方,所以事后仔細探究了一番。

1.關于getWriteableDataBase()和getReadableDatabase()的真正作用
getWriteableDataBase()其實是相當于getReadableDatabase()的一個子方法,getWriteableDataBase()是只能返回一個以讀寫方式打開的SQLiteDatabase的引用,如果此時數據庫不可寫時就會拋出異常,比如數據庫的磁盤空間滿了的情況。而getReadableDatabase()一般默認是調用getWriteableDataBase()方法,如果數據庫不可寫時就會返回一個以只讀方式打開的SQLiteDatabase的引用,這就是二者最明顯的區別。

關鍵源碼如下:

public synchronized SQLiteDatabase getWritableDatabase() {  if (mDatabase != null) {    if (!mDatabase.isOpen()) {    // darn! the user closed the database by calling mDatabase.close()    mDatabase = null;    } else if (!mDatabase.isReadOnly()) {    return mDatabase; // The database is already open for business    }  }... ...public synchronized SQLiteDatabase getReadableDatabase() {  if (mDatabase != null) {    if (!mDatabase.isOpen()) {    // darn! the user closed the database by calling mDatabase.close()    mDatabase = null;    } else {    return mDatabase; // The database is already open for business    }  } ... ...  try {    return getWritableDatabase();  }... ...

2.SQLiteDatabase的同步鎖

其實在只使用一個SQLiteDatabase的引用時,SQLiteDatabase對CRUD操作都會加上一個鎖(因為是db文件,所以精確至數據庫級),這就保證了在同一時間你只能進行一項操作,無論是不是在同一個線程中,這就導致了如果你在程序中對SQLiteOpenHelper使用了單例模式,那么你對數據庫讀寫進行任何的優化操作都是"徒勞"。

3.多線程讀數據庫

仔細看源碼你會發現,在數據庫操作中只有add,delete,update會調用lock(),而query()是不會調用的,但是在加載數據時,調用了SQLiteQuery的fillWindow方法,而該方法依然會調用SQLiteDatabase.lock(),所以要想真正的實現多線程讀數據庫,只能每個線程使用各自的SQLiteOpenHelper對象進行讀操作,這樣就可避開同步鎖。關鍵源碼如下:

/* package */ int fillWindow(CursorWindow window,  int maxRead, int lastPos) {  long timeStart = SystemClock.uptimeMillis();  mDatabase.lock();  mDatabase.logTimeStat(mSql, timeStart, SQLiteDatabase.GET_LOCK_LOG_PREFIX);  try {... ...

4.多線程讀寫

實現多線程讀寫的關鍵是enableWriteAheadLogging屬性,這個方法 API Level 11添加的,也就是所3.0以上的版本就基本不可能實現真正的多線程讀寫了。簡單的說通過調用enableWriteAheadLogging()和disableWriteAheadLogging()可以控制該數據是否被運行多線程讀寫,如果允許,它將允許一個寫線程與多個讀線程同時在一個SQLiteDatabase上起作用。實現原理是寫操作其實是在一個單獨的log文件,讀操作讀的是原數據文件,是寫操作開始之前的內容,從而互不影響。當寫操作結束后讀操作將察覺到新數據庫的狀態。當然這樣做的弊端是將消耗更多的內存空間。

5.多線程寫

這個就不用多想了,SQLite壓根不支持,如果實在有需求可以使用多個數據庫文件。

6.備注

(1)你有沒有想SQLite最多支持多少個數據庫連接,其實在官方API文檔(enableWriteAheadLogging ()方法)中給出了最精確的答案:The maximum number of connections used to execute queries in parallel is dependent upon the device memory and possibly other properties.就是看你有多少內存,但是我感覺這話說的有點大,是不?哈哈。

(2)當你在多線程中只使用一個SQLiteDatabase的引用時,需要格外注意你SQLiteDataBase.close()調用的時機,因為你是使用的同一個引用,比如在一個線程中當一個Add操作結束后立刻關閉了數據庫連接,而另一個現場中正準備執行查詢操作,但此時db已經被關閉了,然后就會報異常錯誤。此時一般有三種解決方案,①簡單粗暴給所有的CRUD添加一個 synchronized關鍵字;②永遠不關閉數據庫連接,只在最后退出是關閉連接。其實每次執行getWriteableDataBase()或getReadableDatabase()方法時,如果有已經建立的數據庫連接則直接返回(例外:如果舊的連接是以只讀方式打開的,則會在新建連接成功的前提下,關閉舊連接),所以程序中將始終保持有且只有一個數據庫連接(前提是單例),資源消耗的很少。③可以自己進行引用計數,簡單示例代碼如下:

//打開數據庫方法public synchronized SQLiteDatabase openDatabase() {if (mOpenCounter.incrementAndGet() == 1) { // Opening new database try { mDatabase = sInstance.getWritableDatabase(); } catch (Exception e) { mDatabase = sInstance.getReadableDatabase(); } }return mDatabase;}//關閉數據庫方法public synchronized void closeDatabase() { if (mOpenCounter.decrementAndGet() == 0) { // Closing database mDatabase.close(); } }

 (3)還有一些比較好的習慣和常識,例如關閉Cursor,使用Transaction,SQLite存儲數據時其實不區分類型,以及SQLite支持大部分標準SQL語句,增刪改查語句都是通用的等等。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 精品一区二区在线播放 | 亚洲第1页 | 一区二区三区在线 | 九色一区二区 | 亚洲三级电影 | 在线观看中文字幕 | 日韩大尺度在线观看 | 欧美日韩在线免费观看 | 国产精品视频999 | 99re国产视频 | 免费二区 | 韩国av一区二区 | 国产免费观看一区二区三区 | 亚洲一区二区三区免费在线观看 | 天天做天天看 | 日本色视频| 成人亚洲精品久久久久软件 | 美女黄网站视频免费 | 亚洲天堂久 | 狠狠色丁香婷婷综合 | 噜噜噜天天躁狠狠躁夜夜精品 | 日本a天堂 | 中文字幕亚洲一区 | 亚洲视频一区二区三区四区 | 久久久久久久久久久蜜桃 | 成人精品一区二区三区中文字幕 | 在线视频日本 | 日韩免费| 国产黄色在线播放 | 中文字幕精品一区 | 一区二区三区在线免费观看 | 国产在线一区二区 | 久久99精品久久久久久琪琪 | 国产中文在线播放 | 亚洲九九| 91九色最新 | 日韩aaaa | 精品96久久久久久中文字幕无 | 午夜影院在线观看免费 | 国产激情视频在线观看 | 亚洲色图偷拍自拍 |