Android effectively cursor usage? - android

In my Android activity, I use rawquery many times. And I use a static cursor for that rawquery. My question is: should i close my cursor before execute another rawquery or just leave it open til all the rawqueries are executed (close only once).
I try both and the result is the same. But in term of performance/debug, which one is better?

you should close your cursor after finishing with it. otherwise you will consume all the allowed cursor resources for your application (it is limited) and will get an exception.
making static does not make it one open cursor for all. remember that it is static on the java side; there is a native side here that uses new resources for each opened cursor. you need to free these resources.

If you don't close the cursor after each use, you are leaking memory quite badly. What you are actually doing is creating a new cursor with every query, but using a single static reference to refer to only the newest. (References to the older cursors are lost.)
Unclosed cursors can also cause fatal errors further down the line, if (for example) the runtime tries to do some garbage collection. The SQLiteDatabase's finalize() method (called on garbage collection) will throw an exception if it notices that some cursors are still open, and this can kill your app.

Related

How to use the same database and cursor through the whole app properly?

I have huge databases (~ 40K rows) which I deal with in my app. There is one activity which has a list view that uses a CursorAdaptor to display and search in the whole database rows. Each time the user deals with a database, I open the database and create the required Cursor and I call Cursor.getCount() in another thread to tackle the laziness issue of cursors and make it fully ready for future operations. It takes around 7 seconds to have Cursor.getCount() done for the huge databases.
Users may invoke this activity multiple times so I store the cursor in a global class as a static data member and I always reuse the same cursor.
I guess that if I did not close the cursor and the database properly I may get weird results. Right?!
My question is: When I should close the cursor and the database?
It will not be possible to close the database and the cursor in Activity.onStop() since I will have to re-open the database and the cursor each time the activity goes from being invisible to visible. And if I did the close in Activity.onDestroy, then the app might be killed by the OS (when device's available memory is low) before the onDestroy is called.
Why don't you turn this global static class into a singleton so that you don't run into instantiation problems, which it sounds like you are referencing. Once you call getInstance() in your singleton you will always get the same cursor and DB connection within that class.

Using the same database in successive activities in Android without memory leak

I'll preface this question with the note that I have looked at this similar question, but I'm still encountering issues. Basically, I want to access the same database in two activities in my Android application. However, when I open it in the second activity, I'm getting two series of messages in my LogCat:
First:
"Uncaught exception thrown by finalizer (will be discarded):
Ljava/lang/IllegalStateException;: Finalizing cursor android.database.sqlite.SQLiteCursor#436053b8 on dogs that has not been deactivated or closed
at android.database.sqlite.SQLiteCursor.finalize(SQLiteCursor.java:596)"
(dogs is the name of a table in my database, dog_data)
Second:
"ERROR/Database(1316): Leak found
ERROR/Database(1316): java.lang.IllegalStateException: /data/data/com..../databases/dog_data SQLiteDatabase created and never closed"
As far as I can tell, I am closing my database upon exiting the first activity. Following the style of the notepad tutorial, I have a wrapper class "DbAdapter" around my SQLiteDatabase, and in the onPause() method of the first activity, I call the close method on that Adapter (which calls the close methods on my SQLiteDatabase and my SQLiteOpenHelper).
I think the issue is how I am trying to reopen the database in my second activity:
SQLiteDatabase db = openOrCreateDatabase("dog_data",
SQLiteDatabase.CREATE_IF_NECESSARY, null);
(I choose not to use a wrapper because I only needed to run one query on the database, perhaps this is an issue).
Can someone advise as to where my issue might be? I'll admit (as may be clear from my question) that I don't fully understand the implications of "closing" a database (the documentation for SQLiteDatabase.close() is not particularly specific), which is probably the main reason for my problem.
Thanks.
Just in case someone happens to encounter a similar issue (seems possible but probably unlikely), I recently stumbled onto the solution. In the insert method of my "DbAdapter", I was (stupidly) checking uniqueness via a query for a row with a given value for one of the fields, and seeing whether that query returned any rows. This was creating a cursor that I wasn't closing, which resulted in the "Finalizing cursor" error noted above.
I've received that error before and had to use cursor.close() to correct the issue. I'm not exactly sure why because there are times when I didn't use close() and received no error. Maybe it's a warning that only gets noticed when it is sitting next to a show stopping error?
I will say the proper procedure is open database connection -> create cursor by running db method -> iterate through cursor -> close cursor -> close database connection.

Can't find the source of a DatabaseObjectNotClosedException error

I'm having a hard time figuring out what my problem is here. I'm receiving this error in my program, but it does not cause a crash or anything like that. I have an update I'd like to release, but I don't want to release it with this error being thrown at certain times. I've read all related posts on this error, but none apply to my situation.
I've made sure that I am closing my DatabaseHelper and SQLiteDatabase objects. I've also made sure that I'm closing all of my cursors. This error is pointing toward my method getActiveScheduleInfo, which returns a Cursor object. I've made sure that whenever I call this method, the returned cursor is closed in a Finally block.
Is this incorrect to do it this way? In my methods that call getActiveScheduleInfo, I have multiple return statements in them, based on certain conditions. So, instead of closing the cursor before each return line, I surround the condition testing with a Try, and close everything down in my Finally.
Everything looks like it should be working, so I'd really appreciate any help!
Thanks a lot!
Paul
I was able to figure this out! I hope that this helps someone else out there having the same problem.
I wasn't doing anything inherently incorrect here, but was rather taking too long to close some of my cursors. To give you a very brief background, I could not use a Managed Query or use startManagingCursor, since this code was in a custom class, not an activity. I am building against Android 2.0 (API level 5) so I am not using the new CursorLoader object.
I was taking the following steps:
Opening the database.
Creating a new Cursor and performing my query.
Iterating through the cursor and performing the needed tasks
Performing some other logic
Closing the Cursor and Database in a Finally block.
I found out that my step 4, performing some other logic, coming before closing my Cursor, was causing it to, for lack of a better term, timeout and cause this error. From now on, I read the necessary data from the Cursor, and not ONE LINE OF CODE FURTHER, I close the Cursor. :) This has completely eliminated these random errors, and I have clean-running code again.
I hope that helps others having the same problem! Take care,
Paul

Viewing an Android database cursor

Would anyone know how I can view what a cursor has in it during debugging so that I can determine the functionality of my database helper?
It keeps acting like it's returning data, but then when I attempt to use the cursor.isNull(0) method, I keep getting NullPointerException thrown and not being able to see what the cursor has in it while stepping through the execution is really frustrating me.
Any help would be extremely appreciated.
Thanks.
Android has provided a specific class just for debugging Cursors. It is called DatabaseUtils.
Call the method DatabaseUtils.dumpCursorToString(cursor) to view the contents of your cursor.
This helper loops through and prints out the content of the Cursor for you, and returns the cursor to its original position so that it doesn't mess up your iterating logic.
If that's null pointer exception, it seems your cursor really is null.
I would use Log.d() to help debug my cursors, you can simply create a helper method to dump the whole row of your cursor to LogCat.

Resetting or refreshing a database connection

This Android application on Google uses the following method to refresh the database after replacing the database file with a backup:
public void resetDbConnection() {
this.cleanup();
this.db =
SQLiteDatabase.openDatabase(
"/data/data/com.totsp.bookworm/databases/bookworm.db",
null, SQLiteDatabase.OPEN_READWRITE);
}
I did not build this app, and I am not sure what happens.
I am trying to make this idea work in my own application, but the data appears to be cached by the views, and the app continues to show data from the database that was replaced, even after I call cleanup() and reopen the database. I have to terminate and restart the activity in order to see the new data.
I tried to call invalidate on my TabHost view, which pretty much contains everything. I thought that the views would redraw and refresh their underlying data, but this did also not have the expected result.
I ended up restarting the activity programmatically, which works, but this seems to be a drastic measure. Is there a better way?
Agreed with Pentium10, at least conceptually.
The application is using a Cursor to show its data. A Cursor in Android is akin to a client-side cursor in ODBC, in that it is a cached copy of all of the data represented by the query's result set.
Now, the normal way of handling changes in the database contents is to call requery() on the Cursor. That will ripple its changes through the CursorAdapter to attached ListViews or other AdapterViews.
In your case, I am not completely certain that will work, since you are closing, replacing, and re-opening the database. That should not be done with an open Cursor on the data, AFAIK. So, in your case, you'd need to close the Cursor, do the database shuffle, then run the query again to get a fresh Cursor on your new database.

Categories

Resources