|
1
614
从多个线程中插入、更新、删除和读取通常都是正常的,但是brad的 answer 不正确。您必须小心如何创建连接并使用它们。在某些情况下,即使数据库没有损坏,更新调用也会失败。 基本答案。 sqliteOpenHelper对象保留一个数据库连接。它似乎为您提供了一个读写连接,但实际上没有。调用只读,您将获得写数据库连接,而不管如何。 所以,一个助手实例,一个数据库连接。即使您从多个线程使用它,一次一个连接。SQLeDATABAASE对象使用Java锁来保持访问序列化。因此,如果100个线程有一个DB实例,那么对实际磁盘数据库的调用将被序列化。 因此,一个助手,一个DB连接,在Java代码中被序列化。一个线程,1000个线程,如果使用它们之间共享的一个助手实例,那么所有的DB访问代码都是串行的。生活是美好的。 如果您试图同时从实际的不同连接写入数据库,其中一个将失败。它不会等到第一次写完再写。它不会写你的零钱。更糟的是,如果您不在sqliteDatabase上调用正确版本的insert/update,则不会得到异常。您只需在日志cat中收到一条消息,就可以了。 那么,多线程?使用一个助手。时期。如果你知道只有一个线程在写,你可以使用多个连接,你的读取速度会更快,但买家要小心。我没有做过那么多的测试。 这里有一篇更详细的博客文章和一个示例应用程序。
我和格雷实际上正在包装一个基于他Ormlite的ORM工具,它与Android数据库实现本地工作,并遵循我在博客文章中描述的安全创建/调用结构。那应该很快就出来了。看一看。 同时,还有一篇后续博客文章: 同时检查叉子 2点0 前面提到的锁定示例: |
|
|
2
183
并发数据库访问Same article on my blog(I like formatting more) 我写了一篇小文章,描述了如何保证Android数据库线程的安全。 假设你有自己的 存取资料表 .
现在,您希望在单独的线程中将数据写入数据库。
您将在日志cat中收到以下消息,其中一个更改将不会被写入。
这是因为每次你创造新的 存取资料表 对象,您实际上正在建立新的数据库连接。如果您试图同时从实际的不同连接写入数据库,其中一个将失败。(来自上述答案) 要将数据库与多个线程一起使用,我们需要确保使用一个数据库连接。 让我们来做单件课程 数据库管理器 它将保持和返回单一 存取资料表 对象。
在不同线程中将数据写入数据库的更新代码将如下所示。
这会给你带来另一场车祸。
因为我们只使用一个数据库连接,方法 GETDATABASE() 返回的同一实例 数据操作 对象 螺纹1 和 线程2 . 发生了什么事, 螺纹1 可以关闭数据库,而 线程2 仍在使用。这就是为什么我们 非法状态例外 撞车。 我们需要确保没有人使用数据库,然后关闭它。StackoveFlow上的一些人建议永远不要关闭 数据操作 . 这将导致以下logcat消息。
工作样品
使用方法如下。
每次需要数据库时,都应该调用 OpenDATABASE() 方法 数据库管理器 班级。在这个方法中,我们有一个计数器,它指示打开数据库的次数。如果它等于1,则意味着我们需要创建新的数据库连接,如果不是,则数据库连接已经创建。 同样的情况也发生在 CuffEdababase[() 方法。每次调用此方法时,计数器都会减少,每当计数器变为零时,我们将关闭数据库连接。 现在您应该能够使用您的数据库并确保它是线程安全的。 |
|
|
3
16
在所有类之间共享一个静态字段。我过去常常为这个和其他需要分享的事情而保留一个单身汉。计数方案(通常使用atomicinteger)也应该用于确保永远不要过早关闭数据库或将其保持打开状态。
有关最新版本,请参见 https://github.com/JakarCo/databasemanager 但我也会尽量使代码保持最新。如果您想理解我的解决方案,请看代码并阅读我的注释。我的笔记通常很有用。
代码到 复制/粘贴 :
|
|
|
4
10
数据库具有非常灵活的多线程功能。我的应用程序同时从多个不同的线程命中了它们的DBS,而且效果很好。在某些情况下,我有多个进程同时命中数据库,这也很好。 您的异步任务-尽可能使用相同的连接,但如果必须,可以从不同的任务访问数据库。 |
|
|
5
6
德米特洛的回答对我的案件很有效。 我认为最好将函数声明为synchronized。至少在我的情况下,它会调用空指针异常,否则,例如getwritabledatabase尚未在一个线程中返回,而opendatabse同时在另一个线程中调用。
|
|
|
6
4
我对sqliteDatabase API的理解是,如果您有一个多线程的应用程序,那么您就不能拥有多个指向单个数据库的sqliteDatabase对象。 当然可以创建该对象,但是如果不同的线程/进程也开始使用不同的sqliteDatabase对象(比如我们在JDBC连接中的使用方式),则插入/更新将失败。 这里唯一的解决方案是坚持使用1个sqliteDatabase对象,并且每当在多个线程中使用startTransaction()时,Android会管理不同线程之间的锁定,并且一次只允许1个线程具有独占的更新访问权。 另外,您可以从数据库中执行“读取”,并在不同的线程中使用相同的sqliteDatabase对象(而另一个线程写入),并且不会出现数据库损坏,即“读取线程”在“写入线程”提交数据之前不会从数据库中读取数据,尽管两者都使用相同的sqliteDatabase对象。 这与JDBC中的连接对象不同,如果您在读写线程之间传递(使用相同的)连接对象,那么我们也可能打印未提交的数据。 在我的企业应用程序中,我尝试使用条件检查,以便UI线程不必等待,而bg线程持有sqliteDatabase对象(独占)。我尝试预测用户界面操作,并将bg线程的运行延迟“x”秒。还可以维护PriorityQueue来管理分发sqliteDatabase连接对象,以便UI线程首先获取它。 |
|
|
7
4
在为此挣扎了几个小时之后,我发现每个数据库执行只能使用一个db helper对象。例如,
如:
每次循环迭代时创建一个新的数据库适配器是我通过助手类将字符串获取到数据库中的唯一方法。 |
|
|
8
3
有过一些问题,我想我已经明白为什么我会出错。
我编写了一个数据库包装类,其中包括
我用的是单件的,在近距离的文档中有一些不祥的评论。
(我大胆)。 所以我有间歇性崩溃,我使用后台线程访问数据库,它们与前台同时运行。
所以我想
在阅读了其他解释sqliteDatabaseHelper代码实例计数的注释之后,您唯一想要关闭的时间就是您想要执行备份副本的情况,并且您想要强制关闭所有连接,并强制sqlite写下任何可能在徘徊的缓存内容-换句话说,st操作所有应用程序数据库活动,关闭以防帮助程序丢失跟踪,执行任何文件级活动(备份/还原),然后重新启动。 虽然尝试以受控方式关闭听起来是个不错的主意,但现实情况是,Android保留对虚拟机进行垃圾处理的权利,因此任何关闭都会降低缓存更新未被写入的风险,但如果设备受到压力,以及如果您正确释放了光标和对数据库的引用(这是不应是静态成员),则帮助程序仍将关闭数据库。 所以我的看法是: 使用GetWriteableDatabase从单个包装打开。(我使用派生的应用程序类从静态提供应用程序上下文以解决对上下文的需求)。 不要直接呼叫关闭。 不要将结果数据库存储在任何没有明显作用域的对象中,并依赖引用计数来触发隐式close()。 如果执行文件级处理,请停止所有数据库活动,然后调用close,以防出现失控线程,前提是您编写了正确的事务,这样失控线程将失败,关闭的数据库将至少具有正确的事务,而不是可能的部分事务的文件级副本。 |
|
|
9
3
您可以尝试应用新的体系结构方法 anounced 在2017年的谷歌I/O上。 它还包括新的ORM库 Room 它包含三个主要组件:@entity、@dao和@database Java语言
用户道.java
应用数据库.java
|
|
10
0
我知道响应很晚,但是在Android中执行sqlite查询的最佳方法是通过自定义内容提供程序。这样,用户界面就与数据库类(扩展sqliteOpenHelper类的类)分离了。查询也在后台线程(光标加载程序)中执行。 |
|
Murilo · Jetpack编写导航栏项目图标 1 年前 |
|
|
KolaYAndr · 活动RESULT_OK似乎从未发生过 1 年前 |
|
psycho_pat · Android应用程序中的权限 1 年前 |
|
|
FarazFiroz · 如何将argb转换为描述性文本颜色 1 年前 |
|
Daniel · Unity Android游戏支持的设备数量太少 1 年前 |