Android content provider database leak problem

I am writing a content provider for this application, and in my content provider I open a database connection, run a query and return the cursor of results to the calling program. If I close this database connection in the provider, the cursor will not get any results. If I leave it open, I get "detected leakage" errors in my DDMS log. What am I missing here? What is the clean and correct way to return the cursor of database results?

+19
java android
04 Sep '09 at 13:18
source share
3 answers

You will not miss anything AFAIK. Android is missing onDestroy() (or equivalent) for ContentProvider . There is nothing in the source code in this area that assumes that there is some kind of onDestroy() that just does not appear in the SDK.

If you look at the source code for AlarmProvider and LauncherProvider , they even create database objects for each API call (for example, every time they get insert() ), they open a write descriptor for a record that they never close).

+13
Sep 04 '09 at 13:30
source share

One of my content providers manages multiple databases, the same schema with different data sets. To prevent an IllegalStateException from being skipped when the garbage collection detected that my content provider had an open database that no longer referenced it, even though SQLiteCursor left me with several options:

1) Leave the SQLiteDatabase object open and place it in the collection so that you no longer use it.

2) Leave the SQLiteDatabase object open and create a cache that will allow me to reuse the database object when accessing the same database.

3) Close the database when the cursor is closed.

Decision 1 contradicts my best judgment. A solution is another form of resource leakage; its only economic grace is that it does not violate the system. I decided this choice immediately.

Solution 2 was my idea for a better solution. It saved resources and simultaneously reduced runtime without opening a database connection. The downside of this solution was that I would have to write a cache, and that would increase the size of the application. Size really wasn't a problem, but time was written. I have passed this decision now and can return to it later.

Solution 3 is the one that decided to go. First, I thought it would be easy to do; in action, all I had to do was reset the cursor returned by my Content Provider back to SQLiteCursor. Then I could call its getDatabase () method and call close () on the database.

It's impossible. It turns out that the cursor returned by the content provider is in a wrapper class that prevents direct access to the actual Cursor object. The delegate-delegate method of the shell invokes it to be received in Cursor. There is no chance to return the cursor back to its derived type.

So, instead of blaming the closure of the database on activity, I went a different and simpler way. I got my own Cursor class by extending the SQLiteCursor class. I added two data members to my derived class; one for linking to the database, and the other an identifier for use in debugging. The ctor class had the same signature as the SQLiteCursor ctor, with an additional parameter added to the end to set the ID value. Ctor installed a database data member to ensure that something was referencing the database if garbage collection occurred before the cursor was closed.

I tried the close () method to close the cursor [super.close ()] and close the database if the link was not null.

I also overridden the toString () method so that the ID is added to the string. This allowed me to keep track of which cursors were open and which of them were open and closed in the log file.

I also added the closeForReuse () method so that the content provider can reuse the database for multiple queries without having to open a new database connection each time.

I also created a class that implemented the SQLiteDatabase.CursorFactory interface. He created my new derived cursor class and controlled the identifier value passed to each of them.

This decision still requires that each action close the cursors passed to it when they are made using the cursor. Since this is a good programming practice, this was not a problem.

+11
Mar 15
source share

This is great, leaving the database connection open for the duration of your application, you just need to make sure that you close the cursor after you are done with it.

I assume that you request and use cursors in Activity? if so, make sure you close the cursors by calling cursor.close(); , I notice that you do not close the cursors in the Activity, and then move on to another action that you will receive these leak messages when you run another request.

I believe that it is best to override the onDestroy method in your activity and close all cursors in it.

+5
Sep 04 '09 at 13:27
source share



All Articles