【问题标题】:Async Task locks Database异步任务锁定数据库
【发布时间】:2012-11-19 16:37:21
【问题描述】:

我正在尝试在我的应用中使用与 WebService 进行事件同步的自定义日历。

一切都很好(嗯,不是真的)。​​

Calendar Activity 启动 AsyncTask 以获取当前月份事件并将它们存储在数据库中,onPostExecute 方法更新视图以正确标记每一天。

我的问题是当用户进入日历活动然后多次快速返回主要活动时,比如说

Main Activity > Calendar Activity(按返回按​​钮)> Main Activity > Calendar Activity(按返回按​​钮)> ...

此时 Activity 因database lock has not been available for 30 sec. 而冻结或应用因SQLiteConstraintException: error code 19 而崩溃

编辑SQLiteConstraintException 行为不再发生

因为更新在我的应用程序中非常重要,所以每次我重新进入日历活动时,我都会删除数据库并用服务器新数据重新填充它,即使它是相同的,并且此时就是锁定的位置。

编辑

我已将 Delete > Insert 样式更改为: Update所有数据(设置flag列=0)>Insert新数据(设置flag列=1)>Delete(其中flag列=0)

我已经尝试过this 解决方案来取消活动结束时的AsyncTask,但没有成功

我确定问题是我没有完全了解我正在做的事情的过程,所以我错过了一些非常重要(或基本)的东西。

任何帮助改进我的代码或只是指出我正确的方向,将不胜感激。

提前致谢!

编辑

我的应用程序有一个 Aplication,它负责创建单个数据库连接 (getWriteableDatabase();),这个独特的连接可以通过 MyApp.getDatabase(); 方法访问。

【问题讨论】:

  • SQLiteConstraintException 应该与数据库锁问题无关。例外是代码中的错误,例如插入带有null 的内容,其中定义了sqlite 表NOT NULLstackoverflow.com/questions/9599809/… 可能是锁定问题的解决方案
  • @zapl 你说得对,SQLiteConstraintException 被抛出是因为另一个问题,与这个问题无关。现在已经解决了。谢谢!

标签: android sqlite android-asynctask


【解决方案1】:

尝试序列化对您的数据库的访问。 SQLiteDatabase 的每个实例都代表一个到 SQLite DB 的单独链接。在单个 SQLiteDatabase 实例上执行的操作是序列化的,但是当您在多个线程中处理多个 SQLiteDatabase 实例时,就会出现您描述的错误。

您没有发布任何代码,所以我只是假设您每次访问 AsyncTasks 中的数据库时都会获得一个新的 SQLiteDatabase 实例(使用 .getWriteableDatabase() / .getReadableDatabase())。尝试使用单个 SQLiteDatabase 实例并将其传递给任务。这样,您将序列化数据库访问并避免 AsyncTask 线程争用数据库锁。

【讨论】:

  • 我已经编辑了我的问题,添加了更多信息,也许我现在对我如何处理SQLiteDatabase更加清楚了@谢谢你的回答!
  • 查看此线程:stackoverflow.com/questions/8200509/… 会不会是您手动管理事务,而某项事务可能无法自行完成?
【解决方案2】:

主活动 > 日历活动(按返回按​​钮)> 主活动 > 日历活动(按返回按​​钮)> ...

这可能会在您每次打开日历时创建并开始一个新的AsyncTask

如果您不明确取消,每个AsyncTask 仍将运行。

这取决于您用来执行AsyncTask 的Android 版本/执行器,它们是并行运行还是序列化运行。 >= Honeycomb 的默认行为是串行执行。在AsyncTask documentation执行顺序部分中进行了解释。

如果您使用串行 AsyncTask,您可以将几个(旧)任务排队,并且可能需要相当长的时间才能执行最近的任务。离开 Activity 时取消任务可能是个好主意。

您收到的database lock has not been available for 30 sec. 警告表明您在一个线程中写入并尝试同时从另一个线程访问数据库。不确定这是否需要两个并行写入线程。

问题在于没有使用不同的SQLiteDatabase 实例。我认为如果你这样做,你会得到错误而不是警告,因为 Android 无法检查某些随机 SQLiteDatabase 对象是否在数据库上有写锁定。

那么你能做什么: 我假设您使用事务,因为在没有事务的情况下长时间锁定数据库几乎是不可能的。

  • 将事务的大小限制在一定的范围内,这样可以保证在其他线程之间使用数据库。
  • 使用db.yieldIfContendedSafely() 动态停止事务。这将检查另一个线程是否想要访问数据库并暂时结束事务。缺点:它将提交更改,您无法回滚整个事务。
  • 如预写日志文档中所述,使用SQLiteDatabase#enableWriteAheadLogging()beginTransactionNonExclusive() 进行实验。看起来它可以允许多线程访问而不会出现锁定问题。我还没有进一步调查这是否有效以及它有什么副作用。

【讨论】:

  • 这是解决问题的正确思维方式。我有几个旧队列AsyncTask,因此阻塞了我的数据库,直到全部完成,这可能需要很长时间。
猜你喜欢
  • 2014-11-04
  • 1970-01-01
  • 1970-01-01
  • 2014-12-09
  • 2018-02-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多