I am writing a content provider for this application and in my content provider I am opening a database connection, running a query and returning the cursor of results to th
One of my content providers manages multiple databases, same schema with different data sets. To prevent the IllegalStateException being through when garbage collection discovered that there was an open database in my content provider that no longer had anything referencing it, even though the SQLiteCursor does left me with several choices:
1) Leave the SQLiteDatabase object open and place it into a collection, never to be used again.
2) Leave the SQLiteDatabase object open and develop a cache that would allow me to reuse the database object when accessing the same database.
3) Close the database when the cursor is closed.
Solution 1 goes against my better judgement. Solution one is just another form of a resource leak; its one saving grace is that it does not upset the system. I ruled this choice out immediately.
Solution 2 was my idea of the best solution. It conserved resources at the same time reducing run-time by not having to reopen database connections. The down side of this solution was that I would have to write the cache and it would have increased the size of the application. Size really was not an issue but the time to write it was. I passed on this solution for the present time and may come back to it later.
Solution 3 is the one that decided to go with. First, I thought it would be simple to do; in the activity, all I needed to do was recast the Cursor returned by my Content Provider back into a SQLiteCursor. Then I could invoke its getDatabase() method and invoke close() on the database.
This is not possible. It turns out that the cursor returned from the Content Provider is within a wrapper class that prevents direct access to the actual Cursor object. The wrapper delegates method calls it receives to the Cursor. No chance to cast the cursor back to its derived type.
So, instead of placing the responsibility of closing the database on the activity, I went a different and easier route. I derived my own Cursor class by extending the SQLiteCursor class. I added two data members to my derived class; one to reference the database and the other was a ID for use in debugging. The class's ctor had the same signature as the SQLiteCursor ctor with an extra parameter added to the end for setting the ID value. The ctor set the database data member to insure that something was referencing the database if a garbage collection occurred before the cursor was closed.
I overrode the close() method so that it would both close the cursor [super.close()] and close the database if the reference was not null.
I also overrode the toString() method so that the ID number was appended to the string. This allowed me to track which cursors were still open and which ones have been opened and closed in the Log file.
I also added a method closeForReuse() so that the Content Provider could reuse a 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. It created my new derived cursor class and managed the ID value passed to each one.
This solution still requires that every activity closes the cursors passed to it when they are done using the cursor. Since this is good programming practice, it was not a concern.