Android:如何锁定可通过 ContentProvider(或其他执行原子条件操作的方式)访问的 SQLite 数据库
Android: how can I lock SQLite database accessible with ContentProvider (or other way of executing atomic conditional operations)
我的 SQLite 数据库中有两个表:entities
和 user_actions
。他们的大致方案:
程序的流程是这样的(所有数据库访问都由 ContentProvider 处理):
- 用户执行了一些修改实体之一的操作
- 相应的实体会立即在
entities
中更新。 locally_modified
此实体的值设置为 1
- 有关用户操作的信息存储在
user_actions
- 在未来的某个时候,将启动与服务器的同步会话(我使用 SyncAdapter 框架)
- 来自
user_actions
的用户操作被一个接一个地上传到服务器并在后台线程中从数据库中删除
- 上传完成后,我需要清除
entities
中的locally_modified
个标记
此时我遇到了原子性问题:与服务器的同步发生在后台线程中,因此用户可以使用该应用程序并执行其他操作。因此,在我清除实体的 locally_modified
标志之前,我必须检查 user_actions
中是否没有与该实体对应的记录。对于将 locally_modified
设置为 1
:
的每个实体,必须自动执行这三个步骤
- 查询
user_actions
对应于实体 _id
的条目
- 测试 #1 中的查询是否返回空集
- 清除
locally_modified
那个实体到 0
鉴于上述情况,我有三个问题:
Q1: 有没有办法在 Android 中锁定通过 ContentProvider 访问的 SQLite 数据库,使其只能由锁定线程访问?
Q2: 如果 Q1 的答案是肯定的,如果其他线程试图访问锁定的 DB,会发生什么情况?我应该采取哪些预防措施以确保可靠运行?
Q3: 可以使用 ContentProviderOperation? You can use "back-references" as described in this answer and this blog post 来执行带有条件逻辑的原子事务以引用先前操作的结果,但是有没有办法使用它导致某种 if-else
语句?
谢谢
Is there a way to lock SQLite DB in Android such that it can be accessed only by the locking thread?
是的,请查看 SQLiteDatabase.beginTransaction()
(source). I believe you need SQLite's exclusive transactions,但您需要进一步研究它以了解您的确切用法。
If the answer to Q1 is positive, what happens if some other thread tries to access a locked DB? What precautions should I take to ensure reliable operation?
有一个 SQLite.amIInTransaction()
方法你可以检查,或者只是捕获一个 SQLiteDatabaseLockedException
(more SQLite exceptions that you should look up)
It is possible to execute atomic transactions with conditional logic using ContentProviderOperation? You can use "back-references" as described in this answer and this blog post to reference the result of a previous operations, but is there a way to use that result in some kind of if-else statement?
从未这样做过,但覆盖 ContentProvider 的 applyBatch 并将其包装在事务中似乎应该可行:
Android: SQLite transactions when using ContentResolver
答案很简单,但有点像 "hack" - 只需在 ContentProvider
中添加额外的 Uri
。
例如:最初我的 ContentProvider
支持以下 URI:
Uri.withAppendedPath(MyContract.CONTENT_URI, "entities")
Uri.withAppendedPath(MyContract.CONTENT_URI, "user_actions")
为了支持问题中描述的原子操作,我额外添加了一个Uri
:
Uri.withAppendedPath(MyContract.CONTENT_URI, "clear_modified_flag")
当此 Uri
更新时:
getContentResolver().update(
MyContract.ClearModifiedFlag.CONTENT_URI,
new ContentValues(),
null,
null);
my ContentProvider
执行一个 SQLite transaction
,它在操作期间锁定数据库并在出现任何错误时将其回滚(如 this answer 中所述)。
就是这样。
P.S。我的 ContentProvider
没有导出(即其他应用程序无法访问和使用它),因此可以安全地将这个新的 Uri
添加到其中。但请记住,如果您确实导出 ContentProvider
,那么公开这样的功能可能会有问题。
我的 SQLite 数据库中有两个表:entities
和 user_actions
。他们的大致方案:
程序的流程是这样的(所有数据库访问都由 ContentProvider 处理):
- 用户执行了一些修改实体之一的操作
- 相应的实体会立即在
entities
中更新。locally_modified
此实体的值设置为1
- 有关用户操作的信息存储在
user_actions
- 在未来的某个时候,将启动与服务器的同步会话(我使用 SyncAdapter 框架)
- 来自
user_actions
的用户操作被一个接一个地上传到服务器并在后台线程中从数据库中删除 - 上传完成后,我需要清除
entities
中的
locally_modified
个标记
此时我遇到了原子性问题:与服务器的同步发生在后台线程中,因此用户可以使用该应用程序并执行其他操作。因此,在我清除实体的 locally_modified
标志之前,我必须检查 user_actions
中是否没有与该实体对应的记录。对于将 locally_modified
设置为 1
:
- 查询
user_actions
对应于实体_id
的条目
- 测试 #1 中的查询是否返回空集
- 清除
locally_modified
那个实体到0
鉴于上述情况,我有三个问题:
Q1: 有没有办法在 Android 中锁定通过 ContentProvider 访问的 SQLite 数据库,使其只能由锁定线程访问?
Q2: 如果 Q1 的答案是肯定的,如果其他线程试图访问锁定的 DB,会发生什么情况?我应该采取哪些预防措施以确保可靠运行?
Q3: 可以使用 ContentProviderOperation? You can use "back-references" as described in this answer and this blog post 来执行带有条件逻辑的原子事务以引用先前操作的结果,但是有没有办法使用它导致某种 if-else
语句?
谢谢
Is there a way to lock SQLite DB in Android such that it can be accessed only by the locking thread?
是的,请查看 SQLiteDatabase.beginTransaction()
(source). I believe you need SQLite's exclusive transactions,但您需要进一步研究它以了解您的确切用法。
If the answer to Q1 is positive, what happens if some other thread tries to access a locked DB? What precautions should I take to ensure reliable operation?
有一个 SQLite.amIInTransaction()
方法你可以检查,或者只是捕获一个 SQLiteDatabaseLockedException
(more SQLite exceptions that you should look up)
It is possible to execute atomic transactions with conditional logic using ContentProviderOperation? You can use "back-references" as described in this answer and this blog post to reference the result of a previous operations, but is there a way to use that result in some kind of if-else statement?
从未这样做过,但覆盖 ContentProvider 的 applyBatch 并将其包装在事务中似乎应该可行:
Android: SQLite transactions when using ContentResolver
答案很简单,但有点像 "hack" - 只需在 ContentProvider
中添加额外的 Uri
。
例如:最初我的 ContentProvider
支持以下 URI:
Uri.withAppendedPath(MyContract.CONTENT_URI, "entities")
Uri.withAppendedPath(MyContract.CONTENT_URI, "user_actions")
为了支持问题中描述的原子操作,我额外添加了一个Uri
:
Uri.withAppendedPath(MyContract.CONTENT_URI, "clear_modified_flag")
当此 Uri
更新时:
getContentResolver().update(
MyContract.ClearModifiedFlag.CONTENT_URI,
new ContentValues(),
null,
null);
my ContentProvider
执行一个 SQLite transaction
,它在操作期间锁定数据库并在出现任何错误时将其回滚(如 this answer 中所述)。
就是这样。
P.S。我的 ContentProvider
没有导出(即其他应用程序无法访问和使用它),因此可以安全地将这个新的 Uri
添加到其中。但请记住,如果您确实导出 ContentProvider
,那么公开这样的功能可能会有问题。