Android内容提供者数据库泄漏问题

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

Related of "Android内容提供者数据库泄漏问题"

你不会错过任何东西。 Android缺lessContentProvideronDestroy() (或等价物)。 在这个领域的源代码中甚至没有任何东西暗示有某种onDestroy()只是没有在SDK中浮出水面。

如果您查看AlarmProviderLauncherProvider的源代码,他们甚至会在每个API调用的基础上创build数据库对象(例如,每次获取insert() ,都会打开一个永远不会closures的可写数据库句柄)。

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

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

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

3)closures游标时closures数据库。

解决scheme1违背了我的判断。 解决scheme一是资源泄露的另一种forms; 它的一个优点就是不会打乱系统。 我立即排除了这个select。

解决scheme2是我的最佳解决scheme。 它节约了资源,同时通过不必重新打开数据库连接来减less运行时间。 这个解决scheme的缺点是,我将不得不写入caching,这将增加应用程序的大小。 大小真的不是一个问题,但写的时间是。 我现在通过这个解决scheme,以后可能会回来。

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

这不可能。 事实certificate,从内容提供者返回的游标是在一个包装类中,阻止直接访问实际的Cursor对象。 包装委托方法调用它接收到光标。 没有机会将光标转换回其派生types。

所以,我没有把closures数据库的责任放在活动上,而是走了一条不同而又简单的路线。 我通过扩展SQLiteCursor类派生了我自己的Cursor类。 我添加了两个数据成员到我的派生类; 一个引用数据库,另一个是用于debugging的ID。 该类的ctor与SQLiteCursor ctor具有相同的签名,并在最后添加了一个额外的参数来设置ID值。 ctor设置数据库数据成员,以确保在游标closures之前发生了垃圾收集时,某事正在引用数据库。

我重写了close()方法,这样它将closures游标[super.close()]并closures数据库,如果引用不为null。

我也重写了toString()方法,以便ID号被附加到string。 这使我能够跟踪哪些游标仍然打开,哪些游标已经在日志文件中打开和closures。

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

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

这个解决scheme仍然要求每个活动closures使用游标完成传递给它的游标。 由于这是一个很好的编程习惯,所以这不是一个问题。

在应用程序的整个运行时间内保持打开数据库连接是完全正确的,你只需要确保在完成之后closures游标。

我猜你在查询和使用活动中的游标? 如果是这样,请确保通过调用cursor.close();来closures游标cursor.close(); 方法,我注意到如果你没有closures活动中的游标,然后移动到另一个活动,你会得到这些泄漏消息时运行另一个查询。

我发现最好的做法是重写活动中的onDestroy方法,并closures其中的所有游标。