gpt4 book ai didi

android - TimeoutException : net. sqlcipher.database.SQLiteCompiledSql.finalize() 在 10 秒后超时(Android)

转载 作者:行者123 更新时间:2023-12-04 09:11:48 24 4
gpt4 key购买 nike

在我的 android 应用程序中,我使用带有 sqlcipher 库的 Room 进行加密/解密。我经常在 Crashlytic 中看到以下崩溃:

java.util.concurrent.TimeoutException:net.sqlcipher.database.SQLiteCompiledSql.finalize() timed out after 10seconds at sun.misc.Unsafe.park(Native Method) atjava.util.concurrent.locks.LockSupport.park(LockSupport.java:190) atjava.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:868)atjava.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:902)atjava.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1227)atjava.util.concurrent.locks.ReentrantLock$FairSync.lock(ReentrantLock.java:231)atjava.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:294)atnet.sqlcipher.database.SQLiteDatabase.lock(SQLiteDatabase.java:567)atnet.sqlcipher.database.SQLiteCompiledSql.releaseSqlStatement(SQLiteCompiledSql.java:104)atnet.sqlcipher.database.SQLiteCompiledSql.finalize(SQLiteCompiledSql.java:146)at java.lang.Daemons$FinalizerDaemon.doFinalize(Daemons.java:289) atjava.lang.Daemons$FinalizerDaemon.runInternal(Daemons.java:276) atjava.lang.Daemons$Daemon.run(Daemons.java:137) atjava.lang.Thread.run(Thread.java:929)


崩溃的线路是 SQLiteDatabase.lock() line 567以前它是第 566 行,但在该方法中我插入了一个检查:如果数据库未打开 -> 返回并且不继续锁定,但它没有帮助,崩溃再次出现。
我认为这次崩溃可能是因为垃圾收集发生在应用程序在后台时(我们的应用程序有一个一直运行的前台服务)。但不确定如何修复它。
至于Room:我没有关闭它,它一直是打开的,因为我的应用程序一直在运行,所以经常需要它。在每次查询数据库后关闭它是不好的做法。
我问过 sqlcipher 的开发人员,但他们不知道是什么导致了这次崩溃。也许有人知道?

最佳答案

我在 Crashlytics 中看到这个问题已经有一段时间了,似乎最终能够完全解决这个问题,我将在这里发布完整的研究。
问题
对于这样的查询:

@Query("DELETE FROM table WHERE id NOT IN (:ids)")
abstract fun deleteNotInList(ids: List<String>): Int
Room 生成的代码不清理光标和生成的语句(在下图中,比较方法一不使用光标,不调用 release() 方法,与调用 cursor.close() 方法的底部相比); 方法名称和查询在那里略有不同,因为我简化了 fragment ):
enter image description here
在这种情况下,语句保持在未释放的内存中,释放转移到 GC 阶段,在 finalize() 方法中。而SqlCipher(SQLiteCompiledSql)中的finalize()反过来需要锁定Database才能释放语句:
enter image description here
问题是数据库可能被锁定超过 10 秒的长事务(或一批较短的事务,使用此类锁的唤醒顺序无法保证且不公平)。
GC 看门狗在达到 10/20 秒时使线程崩溃,具体取决于 OS/JVM 的确切版本。
解决方案
是使用 IN 运算符和手动原始查询重写所有 DELETE/UPDATE,如下所示:
@RawQuery
abstract fun deleteNotInListRaw(query: SimpleSQLiteQuery): Int

fun deleteNotInList(
ids: List<String>
) {
deleteNotInListRaw(
SimpleSQLiteQuery(
"DELETE FROM table WHERE id NOT IN (${ids.joinToString(prefix = "'", postfix = "'", separator = "','")})"
)
)
}
在这种情况下,查询使用游标并在删除完成后关闭它,仍然锁定数据库,但不在 GC 阶段和专用线程上。
附言
可能有更稳定的长期解决方案,但它们需要在 Room/SqlCipher 端实现。
在给定的状态下,SqlCipher 可以重构为在 GC 阶段不锁定数据库。有一个悬而未决的问题: https://github.com/sqlcipher/android-database-sqlcipher/issues/537
Room 可能应该修复代码生成器并使用查询构建器并生成语句关闭行,那里没有 Unresolved 问题,但我稍后会仔细检查这个想法,并将其作为问题提出来。
对我们来说,这似乎完全解决了这个问题。
注意所有 删除/更新 使用 的查询在/不在运算符和其他一些阻止 Room 预编译查询的运算符(由于运行时参数)会导致这种情况。您可以检查代码生成器以验证生成的代码是否调用了 cursor.close()statement.release()

关于android - TimeoutException : net. sqlcipher.database.SQLiteCompiledSql.finalize() 在 10 秒后超时(Android),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63336501/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com