【问题标题】:Android Content Provider database leak issueAndroid Content Provider 数据库泄漏问题
【发布时间】:2009-09-04 13:18:20
【问题描述】:

我正在为这个应用程序编写一个内容提供程序,并且在我的内容提供程序中我打开一个数据库连接,运行一个查询并将结果光标返回给调用程序。如果我在提供程序中关闭此数据库连接,则游标没有结果。如果我让它保持打开状态,我的 DDMS 日志中会出现“发现泄漏”错误。我在这里想念什么?返回数据库结果游标的干净、正确的方法是什么?

【问题讨论】:

标签: java android


【解决方案1】:

你没有错过任何 AFAIK。 Android 缺少ContentProvideronDestroy()(或等效项)。该区域的源代码中甚至没有任何内容表明 SDK 中没有出现某种 onDestroy()

如果您查看 AlarmProviderLauncherProvider 的源代码,它们甚至会基于每个 API 调用创建数据库对象(例如,每次获得 insert() 时,它们都会打开一个可写的数据库句柄他们从不关闭)。

【讨论】:

  • 啊这意味着我必须使用开放数据库:(
  • “他们甚至在每个 API 调用的基础上创建数据库对象 [...] 他们永远不会关闭”,请参阅 SQLiteOpenHelper#getWritableDatabase:“一旦成功打开,数据库就会被缓存”。虽然我同意,但应该在某个时间关闭一个实例......
【解决方案2】:

我的一个内容提供商管理多个数据库,相同的架构具有不同的数据集。为了防止 IllegalStateException 在垃圾收集发现我的内容提供程序中有一个不再有任何引用它的开放数据库时通过,即使 SQLiteCursor 确实给我留下了几个选择:

1) 让 SQLiteDatabase 对象保持打开状态并将其放入一个集合中,不再使用。

2) 让 SQLiteDatabase 对象保持打开状态并开发一个缓存,让我在访问同一个数据库时可以重用数据库对象。

3) 游标关闭时关闭数据库。

解决方案 1 违背了我的更好判断。解决方案一只是资源泄漏的另一种形式;它的一个可取之处是它不会扰乱系统。我立即排除了这个选择。

解决方案 2 是我认为的最佳解决方案。它节省了资源,同时通过不必重新打开数据库连接来减少运行时间。这个解决方案的缺点是我必须写缓存,它会增加应用程序的大小。大小真的不是问题,但写它的时间是。我暂时传递了这个解决方案,以后可能会回来。

解决方案 3 是决定采用的解决方案。首先,我认为这样做很简单;在活动中,我需要做的就是将我的 Content Provider 返回的光标重新转换为 SQLiteCursor。然后我可以调用它的 getDatabase() 方法并在数据库上调用 close()。

这是不可能的。事实证明,从 Content Provider 返回的游标位于阻止直接访问实际 Cursor 对象的包装类中。包装器将它接收到的方法调用委托给 Cursor。没有机会将光标转换回其派生类型。

因此,我没有将关闭数据库的责任放在活动上,而是选择了一条不同的更简单的路线。我通过扩展 SQLiteCursor 类派生了我自己的 Cursor 类。我在派生类中添加了两个数据成员;一个用于引用数据库,另一个是用于调试的 ID。该类的 ctor 与 SQLiteCursor ctor 具有相同的签名,并在末尾添加了一个额外的参数来设置 ID 值。 ctor 设置数据库数据成员以确保如果在游标关闭之前发生垃圾收集,则某些内容正在引用数据库。

我重写了 close() 方法,以便它会关闭游标 [super.close()] 并在引用不为 null 时关闭数据库。

我还覆盖了 toString() 方法,以便将 ID 号附加到字符串中。这使我能够在日志文件中跟踪哪些游标仍处于打开状态以及哪些游标已打开和关闭。

我还添加了一个 closeForReuse() 方法,以便内容提供者可以为多个查询重用一个数据库,而不必每次都打开一个新的数据库连接。

我还创建了一个实现 SQLiteDatabase.CursorFactory 接口的类。它创建了我的新派生游标类并管理传递给每个游标的 ID 值。

此解决方案仍然要求每个活动在使用游标完成时关闭传递给它的游标。由于这是一种良好的编程习惯,因此不必担心。

【讨论】:

    【解决方案3】:

    在您的应用程序的整个运行时保持打开数据库连接是非常好的,您只需确保在完成后关闭光标。

    我想您是在 Activity 中查询和使用游标?如果是这样,请确保您通过调用 cursor.close(); 方法来关闭游标,我注意到如果您没有在 Activity 中关闭游标,然后移动到另一个 Activity,您将在运行另一个查询时收到这些泄漏消息。

    我发现最好在你的活动中重写 onDestroy 方法并关闭其中的所有游标。

    【讨论】:

    • 我正在使用 managedQuery 调用来调用提供程序。我的印象是它为我处理了光标生命周期,但我会重新检查以确保。那么忽略这些泄漏错误是否安全?
    • 我也对生命周期做了同样的假设,我开始注意到一旦我开始关闭游标,错误消息就会消失。有时我会在调试时由于未关闭的游标而引发异常,当这种情况发生时它会终止我的调试会话,它几乎不会立即立即抛出这些错误和异常。
    猜你喜欢
    • 1970-01-01
    • 2011-05-19
    • 2012-11-12
    • 1970-01-01
    • 2017-05-19
    • 2019-08-16
    • 1970-01-01
    • 2010-10-03
    • 1970-01-01
    相关资源
    最近更新 更多