4

在 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     }

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

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

4

1 回答 1

-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 访问权限,如下面ResultSet的 Java 文档中所述:

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

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

您不能为已死的 ContentProvider 提供“数据库访问”,因为ResultSet数据库中的表没有连接,因此Cursor应该处理掉。

编辑:

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

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

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

从讨论中,您可能可以向 Joerg Pleumann 询问更多详细信息……

于 2015-01-15T20:35:13.473 回答