Android - 在UI线程上执行SQLite ContentResolver的插入/删除/更新操作?

10
我已经查看了许多关于在Android中使用SQLite的示例/教程。假设您有一个使用SQLite的应用程序,其中包括ContentProvider、CursorLoader和自定义CursorAdapter。
现在我找到的所有主要示例都依赖于CursorLoader从ContentProvider中获取数据并将其传递给CursorAdapter,这是由于CursorLoader的特性使得它能够在异步-UI线程安全的方式下进行。然而,这些示例在主线程上进行插入/删除/更新操作(例如来自onClick、onResume、onPause等),通过ContentResolver进行调用。(例如:示例)。他们没有将这些调用封装在AsyncTask中或启动单独的线程或使用AsyncQueryHandler。
为什么会这样呢?这么多写得很好的博客/示例会犯这种明显的错误吗?还是说简单的单行插入/删除/更新调用如此之快,以至于可以从主/UI线程中安全地启动它们?这些快速调用的正确方式是什么呢?

1
你的最后一句话可能是正确的:查询操作肯定比单个插入/更新/删除要复杂得多。 - pskink
3个回答

5
我也对样例在主线程上进行调用感到困惑。我猜这些样例只是简化了演示,避免使用额外的线程和回调,因为单个插入/更新/删除调用可能会很快返回。
除了查询的Loader模式之外,自API级别1起,Android还提供了一个名为AsyncQueryHandler的辅助类,用于支持具有完整CRUD回调的异步CRUD操作。 AsyncQueryHandler在内部使用HandlerThread进行异步操作,并将结果传递回主线程。
因此,我认为ContentProvider查询应在工作线程中运行,而这些样例可能不符合官方设计的最佳实践。
===编辑
在官方框架文档中找到了一个注释,请参见thisthis,第255行:
In practice, this should be done in an asynchronous thread instead of
on the main thread. For more discussion, see Loaders. If you are not
just reading data but modifying it, see {@link android.content.AsyncQueryHandler}.

=== 编辑 2 链接 到包含上述引用的实际 Android 开发指南


我在我的问题中也提到了AsyncQueryHandler...但仍然不太令人信服的是,我遇到的所有教程和示例都这样做,只是因为它更简单,并且甚至没有提到这不是你应该这样做的方式。对于一个正确的答案,我想要来自Google(团队成员、文档、教程)的官方信息,或者类似的东西(也许是一个链接到一个体面的GitHub项目,它使用主线程上的这些轻量级调用,或者使用类似AsyncQueryHandler的东西...)。 - Leo K
否则,其他所有内容都只是我们的观点... :) - Leo K
@LeoK,我有一份官方文档提到了这个问题。请查看我的编辑。 - HC Zhang
1
很酷,谢谢。虽然间接,但基本上确认了我想知道的事情。谢谢。(附注:我添加了实际文档的链接) - Leo K

2
这个问题长期以来一直困扰着我。我想这取决于我们正在尝试插入、更新或删除的文件的复杂性。如果我们的应用程序要插入或更新大型文件,异步执行是明智的选择;而如果文件不会很大,可以在UI线程上运行。然而,通常建议在单独的线程上继续进行数据库操作。

谢谢回复。这并没有完全解释为什么教程在介绍最佳实践和其他方面的安全操作时,在插入/删除/更新的情况下从未涉及单独的线程。显然,我还没有阅读所有好的教程,所以一些好的反例将不胜感激。(特别是来自Google或其他值得信赖的来源的反例) - Leo K

0

我认为你已经回答了自己的问题。我相信CursorLoader扩展了AsyncTaskLoader。仅从UI线程发出的调用仅处理对CusorLoader的调用(它使用AsyncTask)。但是,调用所执行的操作仍不会在UI线程上发生。调用一个然后在单独的线程上运行事物的方法/函数仍然是在UI线程之外进行工作。

你认为UI线程正在进行哪些工作?

如果可能,请显示调试日志或示例,以说明您认为在UI上完成了工作。

这不应该发生。

我不是在争论,只是想知道您是如何得出UI工作的结论的。


你关于Cursor Loader是异步的说法是正确的。这也是我在问题中自己写的。然而,插入/删除/更新调用是通过ContentResolver而不是CursorLoader进行的。如果在UI线程上调用ContentResolver,则所有经过ContentResolver的操作都将在UI线程上执行。这很容易验证,只需在ContentProvider的插入覆盖方法的开头添加Thread.sleep(5000)即可。如果这样做,您会看到您的UI会冻结5秒钟... - Leo K

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接