用于在Android中写入SQLite数据库的推荐设计模式

前端之家收集整理的这篇文章主要介绍了用于在Android中写入SQLite数据库的推荐设计模式前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
伙计们,

我正在寻找一种设计模式,使UI线程能够与客户端sqlite数据库交互,该数据库可能具有批量插入(需要10秒),快速插入和读取,并且不会阻止UI线程.

我想知道我是否正在使用最佳设计模式,因为我最近调试了死锁和同步问题,而且我对我的最终产品没有100%的信心.

现在,所有数据库访问都通过单例类进行瓶颈.这是伪代码,显示我如何在我的单例DataManager中接近写入:

  1. public class DataManager {
  2.  
  3. private sqliteDatabase mDb;
  4. private ArrayList<Message> mCachedMessages;
  5.  
  6. public ArrayList<Message> readMessages() {
  7. return mCachedMessages;
  8. }
  9.  
  10. public void writeMessage(Message m) {
  11. new WriteMessageAsyncTask().execute(m);
  12. }
  13.  
  14. protected synchronized void dbWriteMessage(Message m) {
  15. this.mDb.replace(MESSAGE_TABLE_NAME,null,m.toContentValues());
  16. }
  17.  
  18. protected ArrayList<Message> dbReadMessages() {
  19. // sqlite query for messages
  20. }
  21.  
  22. private class WriteMessageAsyncTask extends AsyncTask<Message,Void,ArrayList<Messages>> {
  23. protected Void doInBackground(Message... args) {
  24. DataManager.this.mDb.execsql("BEGIN TRANSACTION;");
  25. DataManager.this.dbWriteMessage(args[0]);
  26. // More possibly expensive DB writes
  27. DataManager.this.mDb.execsql("COMMIT TRANSACTION;");
  28. ArrayList<Messages> newMessages = DataManager.this.dbReadMessages();
  29. return newMessages;
  30. }
  31. protected void onPostExecute(ArrayList<Message> newMessages) {
  32. DataManager.this.mCachedMessages = newMessages;
  33. }
  34. }
  35. }

强调:

>首先:所有公共写操作(writeMessage)都是通过AsyncTask发生的,而不是主要的
线
>下一步:所有写操作都被同步并包装
开始交易
>下一步:读操作是
非同步,因为它们在写入期间不需要阻塞
>最后:读取操作的结果缓存在main上
onPostExecute中的线程

这是否代表了将大量数据写入sqlite数据库的Android最佳实践,同时最大限度地减少了对UI线程的影响?您在上面看到的伪代码是否存在明显的同步问题?

更新

上面的代码中存在一个重要的错误,如下所示:

  1. DataManager.this.mDb.execsql("BEGIN TRANSACTION;");

该行获取数据库的锁定.但是,它是一个DEFERRED锁,所以直到写入发生,other clients can both read and write.

  1. DataManager.this.dbWriteMessage(args[0]);

该行实际上修改数据库.此时,锁是一个RESERVED锁,因此没有其他客户端可以写.

请注意,在第一次dbWriteMessage调用之后,可能会有更多昂贵的数据库写入.假设每个写操作都发生在受保护的同步方法中.这意味着在DataManager上获取锁定,发生写入,并释放锁定.如果WriteAsyncMessageTask是唯一的编写器,这很好.

现在让我们假设还有一些其他任务也执行写操作,但不使用事务(因为它是一个快速写入).这是它的样子:

  1. private class WriteSingleMessageAsyncTask extends AsyncTask<Message,Message> {
  2. protected Message doInBackground(Message... args) {
  3. DataManager.this.dbWriteMessage(args[0]);
  4. return args[0];
  5. }
  6. protected void onPostExecute(Message newMessages) {
  7. if (DataManager.this.mCachedMessages != null)
  8. DataManager.this.mCachedMessages.add(newMessages);
  9. }
  10. }

在这种情况下,如果WriteSingleMessageAsyncTask与WriteMessageAsyncTask同时执行,并且WriteMessageAsyncTask已经执行了至少一次写入,则WriteSingleMessageAsyncTask可以调用dbWriteMessage,获取对DataManager的锁定,但随后被阻止完成其写入,因为保留锁. WriteMessageAsyncTask正在重复获取并放弃对DataManager的锁定,这是一个问题.

结论:组合事务和单例对象级锁定可能导致死锁.确保在开始事务之前有对象级锁定.

我原来的WriteMessageAsyncTask类的修复:

  1. synchronized(DataManager.this) {
  2. DataManager.this.mDb.execsql("BEGIN TRANSACTION;");
  3. DataManager.this.dbWriteMessage(args[0]);
  4. // More possibly expensive DB writes
  5. DataManager.this.mDb.execsql("COMMIT TRANSACTION;");
  6. }

更新2

观看来自Google I / O 2012的视频:
http://youtu.be/gbQb1PVjfqM?t=19m13s

它建议使用内置独占事务的设计模式,然后使用yieldIfContendedSafely

解决方法

我真的不能说同步/死锁部分,这将极大地依赖于你的其余代码.由于DataManager类并未真正与UI交互,因此您可能希望使用服务(IntentService)而不是AsyncTask.完成同步后,您可以显示通知.如果不调用UI代码,则不需要onPostExecute().

猜你在找的Android相关文章