I currently have a list fragment that loads a list of contacts from the ContactsContract.Data.CONTENT_URI using the LoaderManager.LoaderCallbacks. I also have a sqlite table that has contacts that have been added manually into my app by the user, I am using a ContentProvider for my other table.
I would like to load both the sqlite table as well as the ContactsContract.Data.CONTENT_URI using LoaderManager callbacks but I have not been able to find any examples as to how this can be done.
Is this even possible, can some one point me to an example of how to implement this behavior?
you supply a different id for each loader you want so that you can decipher between what loader is called
getLoaderManager().initLoader(0,null,this);
getLoaderManager().initLoader(1,null,this);
then in your onLoadFinished just get the if of the loader that was called
public void onLoadFinished(Loader<Cursor> loader, Cursor arg1) {
loader.getId()
....
}
Related
Firstly let me describe my case: I'm receiving list of categories which populate RecyclerView. Each View on this list is some kind of widget and most common is another list (horizontal RecyclerView) with some news (also parsed from JSON).
I'm keeping all stuff in ContentProvider and SQLiteDatabase (with helper), which contains two tables - for categories and for news (each have category id key). So I'm using Loader for each widget to load data from database or if missing I'm downloading proper data and inserting into db. And then loader automatically refresh list delivering new cursor.
At once there may be present on the screen two (or more) horizontal lists with news. Each of these lists is registering own Loader like here:
loaderManager.initLoader(getWidgetCategory().getCategoryID(), null, getLoader());
under getLoader(); we have same Loader (for widgets with news) like below:
private class NewsLoader implements LoaderManager.LoaderCallbacks<Cursor> {
#Override
public Loader<Cursor> onCreateLoader(final int id, final Bundle args) {
return new CursorLoader(getContext(),
CustomContract.News.CONTENT_URI,
CustomContract.News.PROJECTION_ALL,
CustomContract.News.KEY_CATEGORY_ID+"=?",
new String[]{String.valueOf(getWidgetCategory().getCategoryID())},
CustomContract.News.ORDER_BY_DEFAULT
);
}
#Override
public void onLoadFinished(final Loader<Cursor> loader, final Cursor data) {
if (data.getCount()>=15) {
swapCursor(data);
displayContent();
}
}
#Override
public void onLoaderReset(final Loader<Cursor> loader) {
swapCursor(null);
}
}
And the problem is: when I download and insert to database news from only one category all the Loaders are firing refreshing lists and it is really bad for performance... For example - there are two horizontal lists with news on the screen, we are scrolling a bit down and another (third) is coming. It doesn't have any news so background task is downloading them and inserting into db and then LoaderManager fires Loader for third widget. BUT not only... Also Loaders attached to two lists above are firing refreshing own lists.
I'm assuming there is a problem with same CONTENT_URI, maybe other params in CursorLoader constructor. The only change in there (for every news horizontal list widget) is WHERE clause with different category id and all Loaders are getting content from the same db table. How can I keep firing only one Loader for widget, which downloaded/inserted news? Note that id of registered loader and WHERE selection are the same...
Like I wrote there might be different types of widgets (and also lists, but not with news), which are populated from other tables and their Loaders are not firing with news Loaders. I'm assuming that if I get list of categories with only just-text-widgets (also shared db table) downloading text for one of the widgets will fire all `Loader's and redraw other widgets.
edit:
In other, short words: in my custom ContentProvider I have overriden query method, which contains line:
#Override
public Cursor query(
final Uri uri,
final String[] projection,
final String selection,
final String[] selectionArgs,
String sortOrder
) {
//selection, etc.
//newly created cursor from selection above
cursor.setNotificationUri(getContext().getContentResolver(), uri);
return cursor;
}
and it's notifying all observing Loaders with passed uri. I have few lists (does not matter in fact that these are RecyclerViews) which are consuming data from one URI, but are passing custom selection param to each CursorLoader. But URI is common for all so all loaders are notified and I want to notify only this one, which fired database activity. I want to notify only one Loader with known id, which is also passed to query method inside selectionArgs. Yeah, I know that this might be impossible inside query, but maybe another way? For notifying Loaders not only by their URI
URI is common for all so all loaders are notified and I want to notify only this one, which fired database activity.
The easiest fix is to create a URI pattern where the URI is different for each category, so when you call Cursor.setNoficationUri() each cursor (and ultimately, each loader) will only receive ContentChanged notifications for the data it's concerned with.
There's no way to turn off the ContentObserver on a CursorLoader, so if you absolutely positively can't have different URIs, then the only other thing I can suggest is to write a Loader<Cursor> subclass that knows when to register and unregister its observer on the cursor.
(Edited from earlier answer)
I have an image gallery. I have to show images with uploaded or not uploaded icon. A service is running in the background to upload the images and update the database.Suppose i am in gallery screen and some not uploaded images has been uploaded and update database. So i want to update my view without press any button.
whenever database is getting updated, is there any way i can get callback ??
what is the best approach to update my view without reload all??
You will need to use CursorLoaders/CursorAdapters. You can find an example here
https://github.com/ksarmalkar/ContentProviderJoin
Two possible ways to update the UI would be:
NotifyDatasetChanged() method which can be used if you are using an adapter to display the images (I would assume you are using an adapter for your gallery). You can call this method each time an upload completes which would then tell the view to update itself.
Otherwise maybe run an AsyncTask which checks the status of the upload every so often and in the onProgressUpdate() method have a callback to your UI thread to update that the image has been uploaded.
I hope this helps.
You can use loader for this.
Loaders have these characteristics:
They are available to every Activity and Fragment.
They provide asynchronous loading of data.
They monitor the source of their data and deliver new results when the content changes.
They automatically reconnect to the last loader's cursor when being recreated after a configuration change. Thus, they don't need to re-query their data.
Find here a get started help.
So :
First you should init you loader like this:
getLoaderManager().initLoader([YOUR_LOADER_ID], null, this);
Define loading instruction in onCreateLoader() :
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
// You can return other type than cursor or create your own custom cursorloader but I guess that cursor is right fro you since you want load database content.
Uri baseUri = [you media uri , suppose that you sue content provider];
return new CursorLoader(getActivity(), baseUri,
[projection], [select], [arg],
[orderby]);
}
You get onLoadFinished() callback when data finishes to be loaded :
public void onLoadFinished(Loader loader, Cursor data) {
// Swap the new cursor in. (The framework will take care of closing the
// old cursor once we return.)
mAdapter.swapCursor(data);
}
In your case, loader monitors every change in gallery database. So everytime there is a change, swap cursor is called automatically, then your view is updated according what you defined in your adapter.
i'm using Sqliteloader by commnsware , this looks pretty simple because without help of content provider i'm able to use the loader functionality just by using simple SQL querys.
Now, on tapping on a button, i'm filling a form and insert the item to database like this.
ContentValues values = new ContentValues();
values.put(Tbldata.ACCOUNT, getActiveAccount());
values.put(Tbldata.time, dateobj.getTime());
getWritableDatabase().insert('Tbldata', 'ACCOUNT', values);
After this call, i will be calling finish() on my activity and it returns to my listview. Here the listview item is not updated
What is the best way to update listview?, should i call requery like this?
mLoaderManager.initLoader(0, null, this);
is it correct way of doing?,
What is the best way to update listview?
Call insert() on the SQLiteCursorLoader, not on the SQLiteDatabase. Quoting the documentation:
If you use the insert(), update(), delete(), replace(), and
execSQL() methods on SQLiteCursorLoader, the loader
framework will automatically update you to reflect a new
Cursor with the changed data. These methods take
the same parameters as they do on SQLiteDatabase.
Note that I am no longer maintaining SQLiteCursorLoader.
In Honeycomb the Loader APIs were introduced as the proper way to provide data to an application by doing the heavy lifting on a background thread. In my application I'm working to replace all my Cursors with Loaders that return Cursors. Since Cursor.requery() is depreciated now, it is recommended to just call restartLoader and allow the work to again be done on a background thread and then changeCursor when it returns in onLoadFinished.
All of this works wonderfully except that the ListView doesn't maintain it's scroll position when I want to requery the data, using Cursor.requery() this used to work because it was the same Cursor instance with updated data.
How can I refresh my Loader without loosing the scroll position?
The Loader class by itself doesn't know when the dataset changes and is implementation specific to CursorLoader currently. Now inside my application the data I needed to refresh is inside a local SQLite database (no ContentProvider) so I had to modify the CursorLoader to use my local database instead, as Dianne Hackborne mentioned on the developer group.
Unfortunately just returning a Cursor from loadInBackground doesn't allow the ContentObserver to properly notify the Loader when the data changes, this is because AbstractCursor only calls notifyChanged() when requery() is called on the Cursor.
Since the ContentObserver will never notify the Loader that the data has changed, I ended up calling Loader.onContentChanged() myself as needed. At this point it everything seems to be working but I'll update this answer if any significant issues arrise.
You must just do not create a new adapter in each Loaders loading; I mean..
#Override
public void onLoadFinished(Loader<List<NotificationItem>> loader, List<NotificationItem> notifications) {
// We must do it in this way to not losing listview scroll position after a reload.
if(mAdapter==null) {
mAdapter = new HomeUserActivityListAdapter(getActivity(), notifications);
mListView.setAdapter(mAdapter);
}else{
mAdapter.refill(notifications);
}
}
And in you adapter create a method to items refill
public void refill(List<NotificationItem> notifications) {
mNotificationsList.clear();
mNotificationsList.addAll(notifications);
notifyDataSetChanged();
}
And that's all, it will maintain your scroll position exactly :-)
I've got a ListActivity that displays a list of items from a database using a CursorAdapter, which initially contains all items in the table. I also provide an EditText view where the user can enter search text, and as characters are entered, I requery the database using a "LIKE" or "MATCH" where clause to filter the results (IOW, what lots of apps do when searching).
Currently, I do this in an AsyncTask by creating a new Cursor from the query, creating a new instance of my CursorAdapter class, and then calling list.setAdapter from the UI thread when the task completes. This is all working, but is there a more elegant way of effectively requerying the database with a new WHERE clause from withing the existing adapter/cursor and avoiding having to create new object instances each time? Any examples of this technique?
Doug Gordon
GHCS Software
First of all, CursorAdapter has an changeCursor-Method, were you can change the Cursor without changing the adapter itself. When changing the Cursor the corresponding AdapterView will automatically be notified and update itself.
For further abstraction, you might provide a business object that wraps the query and optionally the execution of the query and provide it to both the Adapter and the TextView (or the Activity possessing the TextView). The TextView changes the business object, the object creates a new cursor and tells the Adapter (using the Observer Pattern, e.g. a Listener) that the cursor changed, and the Adapter retrieves the new Cursor and updates itself.