2009-09-04 105 views
19

我正在为此应用程序和我的内容提供者编写内容提供者我打开数据库连接,运行查询并将结果的光标返回给调用程序。如果我在提供程序中关闭了此数据库连接,则光标没有结果。如果我把它打开,我会在我的DDMS日志中发现“泄漏发现”错误。我在这里错过了什么?什么是干净,正确的方式来返回数据库结果的游标?Android内容提供者数据库泄漏问题

+2

似乎没有必要关闭数据库。该文档说,数据库被缓存,所以你将只有一个数据库实例在整个应用程序生命周期中打开。 http://developer.android.com/reference/android/database/sqlite/SQLiteOpenHelper.html#getWritableDatabase() – 2011-10-13 15:53:27

回答

5

通过在整个应用程序的整个运行时间内保持数据库连接打开状态是完全正常的,您只需确保在完成该操作后关闭游标。

我认为你正在查询和使用活动中的游标?如果确定通过调用cursor.close();方法确保关闭了游标,我注意到如果您没有关闭活动中的游标,然后移动到另一个活动,那么在运行另一个查询时将得到这些泄漏消息。

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

+0

我正在使用managedQuery调用来调用提供程序。我的印象是它为我处理光标生命周期,但我会重新审查一下,以确保它。是否安全,然后忽略这些泄漏错误? – MattC 2009-09-04 13:40:36

+0

我也对生命周期做了相同的假设,我开始注意到一旦我开始关闭游标,错误信息就会消失。有时我会在调试时因为未关闭游标而抛出异常,当它发生时它会中止我的调试会话,它几乎不会立即抛出这些错误和异常。 – 2009-09-04 14:04:11

13

您不会错过任何内容AFAIK。 Android对于ContentProvider缺少onDestroy()(或等价物)。源代码中甚至没有任何内容表明某种onDestroy()只是不在SDK中浮出水面。

如果你看一下AlarmProviderLauncherProvider的源代码,他们甚至在每个API呼叫的基础上创建数据库对象(例如,每次他们得到insert()时候,他们打开一个可写的数据库句柄,他们从来没有关闭)。

+0

哎呀,这意味着我不得不忍受打开数据库:( – Janusz 2010-07-26 13:37:47

+0

“他们甚至在每个API呼叫的基础上创建数据库对象[...],他们从来没有关闭”,看到'SQLiteOpenHelper#getWritableDatabase':“一旦打开成功地,数据库被缓存“。虽然我同意,一个实例应该关闭一段时间... – TWiStErRob 2014-07-14 22:19:58

11

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

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

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

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

解决方案1 ​​不利于我的判断。解决方案一是资源泄漏的另一种形式;它的一个优点是它不会扰乱系统。我立即排除了这个选择。

解决方案2是我的最佳解决方案。它通过不必重新打开数据库连接来节约资源,同时缩短运行时间。这个解决方案的缺点是我不得不编写缓存,这会增加应用程序的大小。大小真的不是问题,但写它的时间是。我现在通过这个解决方案,可能会在稍后回来。

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

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

因此,而不是放置在活动关闭数据库的责任,我去一个不同的,更容易的途径。我通过扩展SQLiteCursor类来派生自己的Cursor类。我在派生类中添加了两个数据成员;一个用于引用数据库,另一个用于调试。该类的ctor与SQLiteCursor ctor具有相同的签名,并在最后添加了一个用于设置ID值的额外参数。 ctor设置数据库数据成员,以确保在游标关闭之前发生垃圾收集时,某事正在引用数据库。

我推翻了close()方法,使得它会在附近移动光标[super.close()]和关闭数据库如果参考是不为空。

我也重载了toString()方法,使得ID号被附加到字符串。这使我可以跟踪哪些游标仍然打开,哪些游标已经在日志文件中打开和关闭。

我还添加了一个方法closeForReuse(),从而使内容提供者能重新使用多次查询数据库,而无需在每次打开一个新的数据库连接。

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

这个解决方案仍然需要每一个活动关闭传递给它的游标时,他们正在使用的游标完成。由于这是很好的编程练习,所以不是一个问题。