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.
Related
I have an interesting problem. I have a SQLite update that I am performing within a AsyncTask on Android (because I also have had to do a ton of remote calls before doing the DB call). The code works like a charm, unless the application is pushed to the background (eg, using the Home button). The task continues to work in the background successfully, the DB call is made and returns 1 row changed, but the data never actually makes it to the DB. No errors or exceptions. Even stranger, the logs show everything working just fine - no exceptions, nada.
Again, when NOT pushed to the background this works fine.
The call:
result = (sqlDB.update("FormInstance", values, "InstanceId=?", new String[] { String.valueOf(form.getSubmissionId()) }) > 0);
Also there is no transaction involved with this call (unless it is happening under the hood of the Android SQLite code).
Anyone know of why this might be the case? Is there something that happens to DB connections or SQLLite that I am unaware of when pushed to the backround?
UPDATE
I have tried wrapping the DB call with a begintransaction/endtrans without any success:
sqlDB.beginTransaction();
try {
result = (sqlDB.update("FormInstance", values, "InstanceId=?", new String[] { String.valueOf(form.getSubmissionId()) }) > 0);
}
finally {
sqlDB.endTransaction();
}
Still acts as though it was successful but data never committed.Please note that I pulled the DB from the device and verified that it had NOT been updated.
After much testing, I found that while there was a onPause method occasionally updating the data, the real problem was that the SQLLite update was not really updating one column (FormStatus) when the update was performed. This was only the case when running in the background. I verified this by querying the result immediately after the update. The final solution was a secondary update that only updated the FormStatus column, which did work. Wrapping with begintrans/endtrans did not help.
The code at the moment reads something in the order of...
DoAnything() {
OpenTheDatabase()
// ... Do all the things! ...
}
However, the database object is never closed. This is worrisome.
The database is opened as follows:
var db = window.openDatabase( ... paramters ... );
No .closeDatabase function exists, or the documentation is incomplete. I thought the following might suffice:
db=null;
I see that sqlite3_close(sqlite3*) and int sqlite3_close_v2(sqlite3*) exist, but I'm unsure how to apply them in this case.
How do I close the database, and is it necessary?
Generally you only have one database connection that you open on app startup, and there is no need to close it while the app is open. It's a single threaded, single user app, so a lot of the normal rules about database connections don't apply.
When the app shuts down, you can rely on the browser to close everything - given the average quality of code on the web, browsers have to be pretty good at cleanup.
Setting db to null and letting the garbage collector do its thing will probably also work, but it is better not to create the extra objects in the first place.
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.
I have a ListView bound to a SimpleCursorAdapter, and I want it to refresh when I modify the database (by inserting, updating or deleting rows). cursor.notifyDataSetChanged() has no effect (it's called on the UI thread) and ListView.removeViewAt(int) throws an UnsupportedOperationException.
What am I supposed to do on Android to get such a basic behavior?
Note that the database is correctly affected and the modification is shown when I restart the activity. But restarting the activity is not an option here, and changing the ListView adapter is the last resource here, since it's a hack and can't guarantee a smooth transition
DISCLAIMER
Quite basic question, asked millions of times and answered zero.
Please, do not answer if you have never done this in your code, don't ask for mine, and don't bother with try this or try that. Only answer if you know how it's done
From API >= 11 the way to do this is using a CursorLoader, this is also included in the Android Compatibility Library, so you can also use this if you are targeting a previous Android version. CursorLoader will make the query in a background thread and return you the cursor. You will need to implement a ContentProvider. You can read the documentation to get an idea of how to use it. Basically you init a loader and then you restart it when you know data has changed. In the callback you just swap the cursor of your adapter.
Or you can just use requery() on the Cursor. The adapter will get automatically notified of the changes. This method is deprecated now and, of course, it's not the recommended way.
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.