Android 内容提供程序在提供程序崩溃时的稳健性

use*_*992 5 android robustness android-contentprovider ashmem

在 Android 平台上(已在 ICS 上确认),如果客户端在查询过程中(即具有打开的游标)内容提供者死亡,则框架会决定终止持有打开游标的客户端进程。

这是当我使用下载管理器查询尝试此操作时的 logcat 输出,该查询在执行查询后会休眠。“睡眠”是为了重现问题。您可以想象当提供者在正确/错误的时间死亡时,它会发生在常规用例中。然后杀死 com.android.media(托管 downloadProvider)。

“杀死 com.example (pid 12234),因为提供程序 com.android.providers.downloads.DownloadProvider 正处于死亡进程 android.process.media”

我在 ActivityManagerService::removeDyingProviderLocked 中跟踪了此代码

 10203     private final void removeDyingProviderLocked(ProcessRecord proc,
 10204             ContentProviderRecord cpr) {
 10205         synchronized (cpr) {
 10206             cpr.launchingApp = null;
 10207             cpr.notifyAll();
 10208         }
 10210         mProvidersByClass.remove(cpr.name);
 10211         String names[] = cpr.info.authority.split(";");
 10212         for (int j = 0; j < names.length; j++) {
 10213             mProvidersByName.remove(names[j]);
 10214         }
 10215 
 10216         Iterator<ProcessRecord> cit = cpr.clients.iterator();
 10217         while (cit.hasNext()) {
 10218             ProcessRecord capp = cit.next();
 10219             if (!capp.persistent && capp.thread != null
 10220                     && capp.pid != 0
 10221                     && capp.pid != MY_PID) {
 10222                 Slog.i(TAG, "Kill " + capp.processName
 10223                         + " (pid " + capp.pid + "): provider " + cpr.info.name
 10224                         + " in dying process " + (proc != null ? proc.processName : "??"));
 10225                 EventLog.writeEvent(EventLogTags.AM_KILL, capp.pid,
 10226                         capp.processName, capp.setAdj, "dying provider "
 10227                                 + cpr.name.toShortString());
 10228                 Process.killProcessQuiet(capp.pid);
 10229             }
 10230         }
 10231 
 10232         mLaunchingProviders.remove(cpr);
 10233     }
Run Code Online (Sandbox Code Playgroud)

这是一个政策决定还是提供者死亡后游标访问不安全?

看起来客户端光标持有一个由 CP 填充的 ashmem 位置的 fd。这就是当服务器(提供者)死亡时客户端被杀死而不是抛出像 Binders 这样的异常的原因吗?

Jim*_*Jim -1

Cursor不安全的。虽然我认为大多数时候Cursors 仅用于对数据的“读访问”,但它们比这更复杂。

Android 文档中有简短的解释,描述了Cursor

该接口提供对数据库查询返回的结果集的随机读写访问。

所以Cursors 不仅仅是对象中保存的数据。ResultSet它们通过数据库连接保持您的位置。使用ResultSetJDBC 访问数据库。因此,它Cursor仅充当“Java 友好”数据库调用。以下是 Android 相关文档ResultSet

当通过适当的 getter 方法读取数据时,JDBC 驱动程序将从数据库检索的 SQL 数据映射到应用程序调用的方法所隐含的 Java 类型。JDBC 规范有一个用于从 SQL 类型到 Java 类型的映射表。

保持ResultSet与数据库的连接。数据不会“复制”到接口中( 和Cursor都是ResultSet接口,而不是对象;某些实现可能会复制数据,但我没有测试它,因为通过关闭保留Statement和打开资源可能会导致数据库资源问题)。ResultSetConnection

Java 提供了接口来获取对数据库中“结果集表”的 JDBC 访问,如ResultSetJava 文档中所述:

表示数据库结果集的数据表,通常是通过执行查询数据库的语句生成的。

http://docs.oracle.com/javase/7/docs/api/java/sql/ResultSet.html

ResultSet您无法向已死亡的 ContentProvider 提供“数据库访问”,因为数据库中的表没有连接,因此Cursor应将其丢弃。

编辑:

一条评论表明 Android 不使用JDBC' orResultSet` - SQLite 的 Android 实现与 JDBC 非常相似,概念本质上是相同的,只是没有方便的名称和描述。

Android 使用自定义实现这一事实使得问题的描述变得更加困难,尽管我应该在我原来的帖子中引用这一点。如果需要更详细的参考和信息,这里是关于 JDBC 状态和 Android 中 SQLite 接口实现的 Google Groups 主题:

https://groups.google.com/forum/#!topic/android-developers/zz3qlNL2JDw

从讨论中,您可能可以向 Joerg Pleumann 询问更多细节......