10

我有一个应用程序(Android 2.2 Google API Level 8),它有多个活动从内容提供者那里提取数据(仅选择数据库访问)。它还有一个带有中央阻塞任务队列的服务,可以接受任何数据库写入任务;活动可以触发服务请求(作为意图),该请求将任务放在阻塞队列中,以便由单个线程顺序检索并执行。数据库大约 4mb。

服务使用一个数据库助手来调用与数据库交互的方法,包括写入数据库;所有 SQL 写入都在数据库助手中执行。

  • 所有数据库写入都被事务包围。
  • 所有数据库读取都在方法结束时关闭游标。
  • 所有活动都没有数据库对象的句柄,它们只能通过内容提供者或服务进行通信。
  • 任何 AlarmManager 触发的任务(如活动)仅使用该服务将适当的任务弹出到队列中。
  • 该服务是唯一拥有数据库助手句柄的类。
  • 所有数据库写入仅通过放置在队列中的任务执行;我已经彻底检查了任务执行是按顺序执行的,我很清楚避免并发写入 SQLite 数据库是必不可少的。

在一系列任务执行过程中,我在尝试写入由“开始事务”的任务执行触发的数据库时总是遇到一个或两个“数据库已锁定”错误。

在试图追踪锁定的来源时,我发现使用 dbhelper.inTransaction()、dbhelper.isLockedByThisThread()、dbhelper.isLockedByOtherThread() 并没有帮助,因为它们不会指示意外的数据库锁定。

我确实发现在早期检测锁定方面起作用的是使用 beginTransaction() 和 setTransactionSuccessful 创建一个方法,而无需任何实际的 SQL 编写代码,在将记录问题的 try catch 块中 - 始终由 beginTransaction() 触发。

我将这个数据库锁定陷阱放置在每个阻塞队列任务方法的任一侧,期望/希望我会找到一个在完成后使数据库处于锁定状态的单一罪魁祸首。我找不到一致的罪魁祸首。在从任务调用开始一直深入到数据库写入之后,我发现数据库锁似乎突然发生而没有被先前运行的任务锁定(所有这些任务在同一个单一线程下按顺序运行)。

在查看了许多其他人在数据库锁定问题方面的经验之后,我尝试在所有任务的事务完成后直接关闭数据库连接,但这并没有帮助,如果有任何事情似乎会导致更多的数据库锁定事件发生。尝试在每个任务执行之间添加一个睡眠;没有经过详尽的测试,但通常发现延迟 3 秒或以上似乎可以阻止数据库锁的出现。尝试禁用警报管理器触发的任务 - 没有任何区别。

我的印象是,我的应用程序外部的某种形式的维护任务正在定期插入并锁定数据库 - 可能会延迟写入日志。显然我不太热衷于设置任务处理延迟,所以我正在考虑设置一个数据库锁重试任务队列,以便在必要时重新尝试数据库写入;更喜欢解决,但我的想法已经不多了。

谁能想到我错过的一些原则或问题?

在 Android 和更大的 SQLite 数据库中,您偶尔会遇到数据库锁,这实际上是正常的吗?

谢谢

4

3 回答 3

7

只要您使用单个数据库连接,SQLite 就可以保证从多个线程进行顺序访问。您如何以及在哪里打开和关闭数据库连接?

我通常建议在启动时打开数据库一次,永远不要关闭它。关闭没有任何好处,因为 SQLite 的事务性质意味着无论如何都会尽快将写入刷新到持久存储。

于 2012-01-14T23:18:37.670 回答
0

关于

在 Android 和更大的 SQLite 数据库中,您偶尔会遇到数据库锁,这实际上是正常的吗?

不,偶尔获得数据库锁绝对不正常。通过阅读您的故事,您说您同时拥有从数据库中提取的服务和内容提供者,因此您可能在两次访问之间锁定了数据库。

我通常做的是确保我通过内容提供者处理我的所有数据库访问。通过单一的数据库入口点,您可以确保每个软件组件都使用相同的逻辑来访问数据库。是否可以让您的服务通过内容提供商访问数据库?

记住,通过将数据库放在内容提供者之后,它仍然可以同时被多个线程访问。为确保您一次只访问一个线程,您可以在内容提供程序内的数据库上放置同步构造。显然,如果您对数据库进行大量长时间的写入/读取,以这种方式锁定绝对会破坏您的应用程序。将所有数据库代码放在内容提供程序中还将为您提供单点调试,这将帮助您确定是否有多个线程正在访问数据库。

于 2012-01-19T19:59:11.433 回答
0

我会检查一些调用数据库的活动或调用其他调用数据库的活动是否只有一个实例。否则它可以在某种意义上锁定自己。

于 2012-01-14T22:52:08.740 回答