I implemented a Loader in my application for querying data from the database. I listen the changes that happen' by implementing LoaderCallbacks<Cursor> listener. The problem that I have is when using the onLoaderReset(Loader<Cursor> loader) method when my data change and I want to invalidate and free any data associated with the loader. In all the examples, in this method there is the following call:
mAdapter.swapCursor(null);
But the thing is I don't use the data from the cursor in adapter, I use it in some other way in my application.
(directly from the returned cursor in onLoadFinished(Loader<Cursor> loader, Cursor data), for example)
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
if (data.moveToFirst()) {
TOTAL_CARDS = data.getCount();
mView.createCards(TOTAL_CARDS);
} else {
TOTAL_CARDS = 0;
mView.createCards(TOTAL_CARDS);
}
}
What would be the corresponding thing to do here, that is similar with mAdapter.swapCursor.
I don't have much experience with loaders, in fact I just started working with them, so if someone has a solution to this, I would appreciate it. Thx!
EDIT:
For now, I am passing null to the loader and it works, like this:
#Override
public void onLoaderReset(Loader<Cursor> loader) {
loader = null;
}
};
But is this the right solution?
Doing
#Override
public void onLoaderReset(Loader<Cursor> loader) {
loader = null;
}
is just as good as doing nothing. In your example code, you are merely nulling your method's local reference to its argument. However, this reference will always be removed after the return of the method call. (You might want to read Is Java "pass-by-reference" or "pass-by-value"? for a further discussion of the topic.)
The onLoaderReset(Loader) method gets called when your loader's callback (usually an Activity or Fragment instance) is asked to release all references to the Cursor which it has gained via onLoadFinished(Loader, Cursor) before. Basically this method asks you to clean up since the Loader will soon close the Cursor it provided to you before. After the cursor was closed, you will not longer be able to retrieve data by it. If the cursor would however still be in use (typically by a CursorAdapter as you mentioned) after it was closed, this would cause an exception to be thrown.
Similarly, onLoadFinished(Loader, Cursor) has an implicit contract asking that after the method returns any formerly provided Cursor objects must not longer be in use. Instead, you have to replace these references by the new cursor which is provided as a method argument. In contrast, onLoaderReset(Loader) asks you to fulfill the same contract, but without providing a replacement, i.e. you should remove all references to a formerly retrieved Cursor.
In your example, you do not let your Cursor escape the method scope but instead you are reading the data right away. Therefore, it is not necessary to remove any references to a Cursor object which was provided via onLoadFinished(Loader, Cursor) since there are none. An empty implementation of onLoaderReset(Loader) to fulfill the interface contract will therefore do the job for you.
Related
So I have this app that loads data via a provider into a cursor.
The cursor is then used to fetch the data for the app to use.
The loading process is done with the LoaderManager API.
Now the problem arises after I call swapCursor() from onLoadFinished() in some circumstances; the cursor reports having the correct amount of data, but when I try to access it I get a CursorIndexOutOfBoundsException.
The peculiar thing is, I know exactly which line this occurs on, so I put a breakpoint there that suspends all threads and tried to inspect the data to see what was up, suddenly the error disappears.
Now by trial and error I figured that the problem goes away even if I do nothing. Like literally nothing after it suspends everything. If I just wait for a few seconds before resuming execution the data is perfectly accessible.
So my question is: Does anybody know if the Android operating system does something in the background that creates a race condition? I don't really see how this can be a bug on my side since when I tested it I suspended all threads, so my app can't do anything while I wait for the issue to resolve.
As a sidenote, if I just replace the swapCursor() call with changeCursor() (doing this means I have to use restartLoader() instead of initLoader() due to some other feature of the system) the bug goes away, but I've read somewhere that this is not the proper way to do it, since it doesn't handle the closing of the cursor properly (I think this was the reason I had to use restartLoader(), but I can't really recall at the moment).
Some code excerpts:
#Override
public void onCreate( Bundle savedInstanceState ) {
super.onCreate( savedInstanceState );
...
getLoaderManager().initLoader( ACTIVE_TEAM_LOADER, null, this );
}
...
#Override
public void onLoadFinished( Loader<Cursor> loader, Cursor data ) {
switch ( loader.getId() ) {
case ACTIVE_TEAM_LOADER:
activeTeamAdapter.swapCursor(
new MergeCursor( new Cursor[]{ data, createNewCursor } ) );
...
}
And the offending line, called deep in the stack from a CursorAdapter.bindView():
long value = data.getLong( data.getColumnIndex( getDBName() ) );
To me it looks like the data in the cursor is not properly loaded yet.
The problem was caused by the fact that swapCursor() does not close the previous MergeCursor cursor. Closing the previous cursor solved the problem, but introduced a new problem because you are not supposed to close cursors from onLoadFinished(), and when closing a MergeCursor the MergeCursor closes all sub-cursors as well.
This was solved by switching back to using changeCursor() whenever I use adapters to only display MergeCursors (some of the adapters display MergeCursors and MatrixCursors when there is no data from onLoadFinished() and for some reason MatrixCursors seems like they need to be closed between usages so this does not apply to them, I don't know why) and wrapping any cursors in a class that disregards closing.
onLoadFinished() excerpt:
Cursor[] cursors = new Cursor[] { new UnclosableCursor( data ), createNewCursor };
activeTeamAdapter.changeCursor( new MergeCursor( cursors ) );
The wrapper:
public class UnclosableCursor extends CursorWrapper {
public UnclosableCursor( Cursor cursor ) {
super( cursor );
}
#Override
public void close() {}
}
A previous version of this answer alluded to the fact that this had to do with casting, but after more research this seems to only have been a symptom of the problem, not the actual problem.
I need some help on database and cursor managing. I noticed that, when entering /leaving certain fragments, I get:
W/SQLiteConnectionPool﹕ A SQLiteConnection object for database '+data+data+database' was leaked! Please fix your application to end transactions in progress properly and to close the database when it is no longer needed.
That made me go back from scratch and check what I'm doing and when. I have:
a DatabaseHelper class extending SQLiteOpenHelper, with just some methods for creating and updating the db;
a DatabaseManager class, extending nothing. I use this, among other things, to keep a single reference to a DatabaseHelper object:
public class DatabaseManager {
private DatabaseHelper h; //class extending SQLiteOpenHelper
public DatabaseManager(Context c) {
if (h==null) {
h = new DatabaseHelper(c.getApplicationContext()); }
public Cursor query(...) {
SQLiteDatabase db = h.getReadableDatabase();
return db.rawQuery(...)
}
public void closeConnection() {
SQLiteDatabase db = h.getWritableDatabase();
db.close();
h.close();
}
}
in this class, some methods querying the database and returning a Cursor object;
in this class, a closeConnection() method, which I'm not really sure of.
I use this class from fragments, calling each time new DatabaseManager(getActivity()). This should not create a new helper reference. Right now I am:
calling Cursor.close() as soon as I got the information I wanted from the query;
never calling open() on my helper neither on my SQLiteDatabase, although I read somewhere that should be done. When exactly? Why it all works even without calling it?
calling manager.closeConnection() in the onStop() method of fragments that make use of my database. As you can see, that calls close on h (a reference to the helper class) and on a readable SQLiteDatabase object. However, I'm not really sure about that, because it closes the helper reference h without making it null, so maybe there are some problems with future calls to new DatabaseManager() ? Maybe dealing with database with a singleton pattern does not require you to call h.close()?
Apart from that, needless to say (that's why I'm asking), when switching through fragments I get the above mentioned warning. What's wrong? What should I do? What does end transactions in progress mean? Should I modify my closeConnection() method, call it in different lifecycle times, or don't call it at all?
After embarrassing issue pointed out by #Selvin, I made h static. Now if I remove any call to closeConnection(), it all works well and I don't get any warnings. That means I'm never calling neither h.close() or db.close(). Is that ok? If not, when should I call it?
I have a similar problem to the one described in this discussion: I need to refresh a ListView when the underlying database changes, but the query is expensive so I'm doing it in an AsyncTask.
Here's what I do when the updated Cursor is ready. (This is also how the list is initially populated on startup.)
#Override
protected void onPostExecute(Cursor result) {
if (activity != null) {
if (currentCursor != null) {
// existing cursor is closed by adapter.changeCursor() so
// we don't need to explicitly close it here
stopManagingCursor(currentCursor);
}
currentCursor = result;
startManagingCursor(currentCursor);
if (adapter == null) {
adapter = getAdapter(result);
setListAdapter(adapter);
} else {
adapter.changeCursor(result);
}
activity.onGotList(result, dbAdapter);
}
}
Here's the error I get. It doesn't happen every time, which is even more frustrating.
Releasing statement in a finalizer. Please ensure that you explicitly call close() on your cursor: SELECT DISTINCT t._id AS _id, t.amount, t.date, t.memo, t.synced, t.flag, (children.pa
android.database.sqlite.DatabaseObjectNotClosedException: Application did not close the cursor or database object that was opened here
at android.database.sqlite.SQLiteCompiledSql.<init>(SQLiteCompiledSql.java:62)
at android.database.sqlite.SQLiteProgram.<init>(SQLiteProgram.java:100)
at android.database.sqlite.SQLiteQuery.<init>(SQLiteQuery.java:46)
at android.database.sqlite.SQLiteDirectCursorDriver.query(SQLiteDirectCursorDriver.java:53)
at android.database.sqlite.SQLiteDatabase.rawQueryWithFactory(SQLiteDatabase.java:1412)
at android.database.sqlite.SQLiteDatabase.rawQuery(SQLiteDatabase.java:1382)
So, I am obviously not closing the Cursor correctly. If I call currentCursor.close() instead of relying on the outgoing Cursor being closed by adapter.changeCursor(), then I get warnings about closing the Cursor twice or closing a null Cursor.
What is the correct way to do this?
In the discussion I linked to, Dianne Hackborn suggests using a Loader instead. That is not an option for me since my code has to run on Android 2.1.
Try to .close() the Cursor when the Activity pause or terminates.
In the onPause() or onDestroy() section of the activity.
Basically, it's possible, but very bad practice to access the same database from two different helpers, so if you have an activity performing database queries, you shouldn't also have a thread accessing it, otherwise android will throw up a quiet error in logcat, and then forget about the query...
The best solution I have found is to implement a thread pool of runnables, each one is a database query and they all use the same database helper. Consequently, only one thread is accessing the database at any one time and the database is just opened and closed when the thread pool starts/stops.
An implementation of the thread pool pattern can be found here: http://mindtherobot.com/blog/159/android-guts-intro-to-loopers-and-handlers/
If you are not changing anything other then redrawing from the list is it necessary to change the cursor at all. Could you get away with just requiring the current adapter.
something along the lines of
adapter.getCursor().requery();
although if you are in a thread other then the main ui thread you may want to call it with
//Did not realize this was deprecated Thanks to Graham Borland for the heads up
runOnUiThread(new Runnable(){
public void run(){
adapter.getCursor().requery();
}
});
Depending on your setup.
New solution still need testing and make sure it is not going to cause issues apparently startManaginCursor and stopManaginCursor are deprecated too so this solution is not worth good either.
stopManagingCursor(adapter.getCursor());
if (!adapter.getCursor().isClosed())
adapter.getCursor().close();
//cursor creation stuff here if needed
startManagingCursor(newCursor);
adapter.changeCursor(newCursor);
What is difference between ContentObserver and DatasetObserver?
When one or another should be used?
I get Cursor with single row. I want to be notified about data changes - eg. when row is updated.
Which observer class should I register?
If you are using a ContentProvider (via ContentResolver or Activity.managedQuery()) to get your data, simply attach a ContentObserver to your Cursor. The code in onChange() will be called whenever the ContentResolver broadcasts a notification for the Uri associated with your cursor.
Cursor myCursor = managedQuery(myUri, projection, where, whereArgs, sortBy);
myCursor.registerContentObserver(new ContentObserver() {
#Override
public void onChange(boolean selfChange) {
// This cursor's Uri has been notified of a change
// Call cursor.requery() or run managedQuery() again
}
#Override
public boolean deliverSelfNotifications() {
return true;
}
}
Make sure your ContentProvider is a "good citizen" and registers the Uri with the cursor after a query:
cursor.setNotificationUri(getContentResolver(), uri);
It should also notify the ContentResolver of any changes to the underlying data (for instance, during insert, delete, and update operations on your SQLite database):
getContentResolver().notifyChange(uri, null);
This approach is a good example of the Observer Pattern of object-oriented design.
I'm not sure if this question is still on anyone's radar. I have been struggling with the same question for a little while now. What I came up with as my litmus test for deciding whether to use a DataSet Observer or a ContentObserver is pretty straight-forward:
If I need to send a URI in my notification I use a ContentObserver. If I simply need to notify one object that another object has changed -- I use a DataSetObserver.
The delimiting factor, for me at least, is does the object that is sending out the notification expose it's underlying resources (be they objects, records, queries, or cursors) as "Universal Resource Identifiers" to the rest of the application; or does the object hide the source of its data.
To provide the supplement to ptc's answer, DataSetObserver is used for handling content changes in the Adapter, for example, it can be used for updating listview dynamically with Adapter. You can register a DataSetObserver using the Adapter#registerDataSetObserver() method.
DataSetObserver can also be used to observe the content changes in the Cursor in a similar fashion.
From my last app developed I can say.
The main difference between ContentObserver and DataSetObserver, is that ContentObserver makes to Observer any change affects on ContentProvider. On the other hand, DataSetObserver Observer any change effect on the database.
Dear Fellow Android Developers!
EDIT:
Thank you all for your answers. I see from many of them that it seems to be common (and accepted) practice to write your own close() method in your database adapter. Fair enough.
But how does that work with a ContentProvider? Usually when querying my database through my ContentProvider I simply issue something like:
Cursor managedCursor = managedQuery(...);
I don't see how I, with this methodology, can access the custom close() method in my custom ContentProvider implementation. Should I instead, from my Activity, do something like:
MyCustomProvider myProvider = (MyCustomProvider) getContentResolver();
and then:
myProvider.query(...);
myProvider.close();
And above all; is this at all necessary (as of point 2 below)?
END EDIT
To a certain degree I must say that I get the concept of the SQLiteOpenHelper, what it is, how it's used and so. I even use it on a regular basis when I write my own ContentProvider's.
The thing is that I'm not sure what to do with the SQLiteDatabase object, returned by the myOpenHelper.getWritableDatabase() (or the myOpenHelper.getReadableDatabase() function for what matters) when I'm done with it.
According to Android ContentProvider.onCreate() documentation:
You should defer nontrivial initialization (such as opening, upgrading, and scanning databases) until the content provider is used (via query(Uri, String[], String, String[], String), insert(Uri, ContentValues), etc).
[...]
If you do use SQLiteOpenHelper, make sure to avoid calling getReadableDatabase() or getWritableDatabase() from this method. (Instead, override onOpen(SQLiteDatabase) to initialize the database when it is first opened.)
The above gives me a hint where to initialize the database (the query(...), insert(...), etc functions), but it doesn't tell me anything on how to treat the created SQLiteDatbase object when I've finished using it.
Should I save it as a member variable of my ContentProvider implementation (and treat it much like a "private singleton" for future use)?
Should I just leave it when exiting the query(...), insert(...), etc. functions and trust that the SQLiteOpenHelper will manage it for me in future calls?
[Insert your alternative point-of-view here]
Being the confiding (or lazy) developer I've implemented my code according to the second alternative above. But I can't get rid of the creepy feeling that I'm neglecting something important.
It depends on what you're doing with your database. If you just do an insert, delete or select where you get an business object back, then you can close the database right after using it. As far as I know it is designed that you simply close it and request a new one when ever you need it.
But be careful when you're working with a cursor then you have to keep the database open as long as the cursor is in use. Otherwise the application will crash when the cursor has to reload data.
I guess you should close it, for example in onDestroy() of an activity that is using it.
So in my DBAdapter class I have:
/**
* Close the database
*/
public void close() {
mDb.close(); //mDb was obtained using mDbHelper.getWritableDatabase();
}
And in my activity:
public void onCreate(Bundle bundle){
...
mDBAdapter = new DBAdapter(this);
// Open or create the database
mDBAdapter.open();
}
#Override
public void onDestroy() {
// Close the database
mDBAdapter.close();
super.onDestroy();
}
Not sure if this is suitable for your provider concept.
If you check the example of use for that object in the API of Android, you can see the object is just used, but no close is necesary.
They implement the method close() though, but I havent seen they use it.