鄉親們寫的SQLite數據庫推薦的設計模式,在Android電子
我在尋找一個設計模式,使一個UI線程與客戶端的SQLite數據庫進行交互,可能有批量插入(服用10S秒),快速插入和讀取,並且不會阻塞UI線程。
我想諮詢一下我是否正在使用最佳設計模式,因爲我最近一直在調試死鎖和同步問題,並且我對最終產品並不是100%信心十足。
所有數據庫訪問現在都是通過一個單例類的瓶頸。下面是僞代碼顯示我怎麼在我的單身接近寫入,DataManager的:
public class DataManager {
private SQLiteDatabase mDb;
private ArrayList<Message> mCachedMessages;
public ArrayList<Message> readMessages() {
return mCachedMessages;
}
public void writeMessage(Message m) {
new WriteMessageAsyncTask().execute(m);
}
protected synchronized void dbWriteMessage(Message m) {
this.mDb.replace(MESSAGE_TABLE_NAME, null, m.toContentValues());
}
protected ArrayList<Message> dbReadMessages() {
// SQLite query for messages
}
private class WriteMessageAsyncTask extends AsyncTask<Message, Void, ArrayList<Messages>> {
protected Void doInBackground(Message... args) {
DataManager.this.mDb.execSQL("BEGIN TRANSACTION;");
DataManager.this.dbWriteMessage(args[0]);
// More possibly expensive DB writes
DataManager.this.mDb.execSQL("COMMIT TRANSACTION;");
ArrayList<Messages> newMessages = DataManager.this.dbReadMessages();
return newMessages;
}
protected void onPostExecute(ArrayList<Message> newMessages) {
DataManager.this.mCachedMessages = newMessages;
}
}
}
亮點:
- 第一:通過的AsyncTask所有公共寫操作(的WriteMessage)發生,從來沒有在主 螺紋
- 下一頁:所有的寫操作是同步的,幷包裹在 BEGIN交易
- 下一頁:閱讀操作 非同步的,因爲它們不需要寫
- 期間阻止最後:讀操作的結果顯示在onPostExecute緩存主 螺紋
這是否代表着Android的最佳實踐編寫潛在的大量的數據到SQLite數據庫,同時最大限度地減少對UI線程的影響?上面看到的僞代碼是否存在明顯的同步問題?
更新
有一個在我的代碼顯著錯誤之上,其計算方法如下:
DataManager.this.mDb.execSQL("BEGIN TRANSACTION;");
該行獲取數據庫鎖。但是,它是一個DEFERRED鎖,因此在寫入之前,other clients can both read and write。
DataManager.this.dbWriteMessage(args[0]);
該行實際上修改了數據庫。此時,該鎖是一個保留鎖,因此其他客戶端可能無法寫入。
請注意,在第一個dbWriteMessage調用之後,可能會有更昂貴的數據庫寫入操作。假定每個寫操作都發生在受保護的同步方法中。這意味着在DataManager上獲取鎖定,寫入發生,鎖定被釋放。如果WriteAsyncMessageTask是唯一的作者,這沒問題。
現在我們假設還有一些其他任務也可以執行寫操作,但不使用事務(因爲它是快速寫入)。下面是它可能看上去是這樣的:
private class WriteSingleMessageAsyncTask extends AsyncTask<Message, Void, Message> {
protected Message doInBackground(Message... args) {
DataManager.this.dbWriteMessage(args[0]);
return args[0];
}
protected void onPostExecute(Message newMessages) {
if (DataManager.this.mCachedMessages != null)
DataManager.this.mCachedMessages.add(newMessages);
}
}
在這種情況下,如果WriteSingleMessageAsyncTask在同一時間WriteMessageAsyncTask執行和WriteMessageAsyncTask至少執行一個寫則已,有可能WriteSingleMessageAsyncTask調用dbWriteMessage,獲得鎖定在DataManager上,但由於RESERVED鎖定而被阻止完成其寫入。 WriteMessageAsyncTask重複獲取並放棄DataManager上的鎖定,這是一個問題。
結論:結合事務和單例對象級鎖定可能導致死鎖。確保在開始事務之前擁有對象級鎖。
的修復到我原來的WriteMessageAsyncTask類:
synchronized(DataManager.this) {
DataManager.this.mDb.execSQL("BEGIN TRANSACTION;");
DataManager.this.dbWriteMessage(args[0]);
// More possibly expensive DB writes
DataManager.this.mDb.execSQL("COMMIT TRANSACTION;");
}
更新2
看看這個視頻從谷歌I/O 2012: http://youtu.be/gbQb1PVjfqM?t=19m13s
它提出了一種設計模式利用內置的專屬交易,然後使用yieldIfContendedSafely
如果我可以建議在性能部分的東西:有一兩件事我注意到的是,一旦你寫了新的消息,你似乎正在讀取db中的* entire *消息並設置mCachedMessages。除此之外,您可以簡單地將新添加的消息添加到mCachedMessages列表中?這將節省很多數據庫讀取。 – 2012-07-18 06:56:13
@AswinKumar這是真的;我的意思是這是一個簡單的例子來代表圍繞同步的最佳實踐。性能優化的確可以簡單地添加到已存在的緩存列表中。 – esilver 2012-07-18 18:12:16