CursorLoader - obtain information about what has changed in db - android

I use standard android ContentProvider and CursorLoader from support library.
I am looking for best approach for obtain information about what has changed in database.
I know that I can read and compare cursor in function:
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
}
but reading all records is probably not good solution.
Do you know good solution for this problem?

If you use a content provider, you abstract away all the changes made to the underlying database by implementing CRUD methods to insert, update, delete or retrieve an item in the database.
Sending a broadcast from the methods which you are interested would be an option to get notified of the changes being made to the database.
I recall that I had used this method to count the number of items added to the database during a refresh cycle.
Optionally you can send the Uri of the changed item as an intent extra to get a reference to the row in the database that has been changed.
Additionally you can declare a global variable to enable or disable these broadcasts to fire only during situations of interest to us.

Related

CRUD using LoaderManager and CursorAdapter

I have a MyListFragment implementation which uses MyCursorAdapter. As the names suggest, they both extend the respective Android classes.
I read through this Loader/LoaderManager tutorial. So, now my query happens in
#Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
return new CursorLoader(blah1, blah2, blah3, blah4, blah5, blah6);
}
and the LoaderManager along with the Loader takes care of when to re-query and update, runs separate threads for queries, synchronize without having me to worry about it. So far so good.
I have 2 questions:
1) I have implemented a MyContentProvider. Where does this come into picture?
-> It seems that the above new CursorLoader(......) makes the query through the MyContentProvider since it uses the URI as the first argument.
2) I also need to delete / update / insert items in the list. Should I just call my the respective methods from MyContentProvider inside MyListFragment at the desired places? If yes, then can I also load the items manually, without having the loader handle it? If not, then what is the right way to do it? I did not find any information online. Any pointers appreciated.
Yes, thats correct it will call the query method in MyContentProvider with the arguments used in the CursorLoader constructor.
If I understood that correctly the answer is yes you can just call the MyContentProvider methods as long as they are calling ContentResolver.notifyChange() as it will cause the CursorLoader to get a new cursor (this is the whole point of using CursorLoader). When using CursorLoader you should avoid manually changing the data at all if you can so as to allow what is shown to always reflect the current set of data coming from MyContentProvider.

Android Loaders Combining Two SQL Queries into a Data Object the Correct Way

I have a scenario where I am trying to use Loaders to load data from my SQLite db. There's two things I need to do:
I need to get a cursor that represents all goals (a.k.a. plans)
For each goal, I need to get another cursor that represents the payments toward that specific goal
It's easy to get a cursor that basically selects all the goals from the db, however, how do I add the second layer where I obtain a cursor for the payments made towards each goal (payments are stored in a separate Payments db, each payment is a record with a parent id for the goal to link it)?
I was thinking of doing everything in the Loader's onLoadFinished as such:
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
switch (loader.getId()) {
case LOADER_PLANS:
mListPlans.clear();
while (cursor.moveToNext()) {
mListPlans.add(new Plan(cursor));
mListPlanPos = mListPlans.size() - 1;
getLoaderManager().restartLoader(LOADER_PAYMENTS, null, this);
}
mAdapter.notifyDataSetChanged();
break;
case LOADER_PAYMENTS:
mListPlans.get(mListPlanPos).setPayments(cursor);
break;
default:
break;
} }
Please ignore the added complexity that I'm storing everything from the db into an ArrayList.
Obviously the above code doesn't work, and I think it's totally the wrong approach. I can't figure out how to do a "multi-layer" Loader such as this, any push in the right direction is appreciated.
Thanks!
Ok, I'll answer my own question if someone else comes across this let's say, "multi-instance" Loader situation. I got it to work as intended, but I had to change my approach in a couple key ways.
So, the above code is actually pretty close to what I used, here's the resulting code that works perfectly:
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
switch (loader.getId()) {
case LOADER_PLANS:
mListPlans.clear();
while (cursor.moveToNext()) {
mListPlans.add(new Plan(cursor));
}
getLoaderManager().restartLoader(LOADER_PAYMENTS, null, this);
break;
case LOADER_PAYMENTS:
for (Plan plan : mListPlans) {
plan.setPayments(cursor);
}
// Notify the adapter that we've updated it's data set mListPlans.
mAdapter.notifyDataSetChanged();
I haven't included the rest of the onLoadFinished() callback, it's not necessary for this demo. So, as you can see, there's no more looping of the Loader itself, instead, I've changed my logic to loop through each plan in my ArrayList in the Payment instance of the Loader. The Plans instance still loads the Payments instance, but just once each run.
For this to work, I changed the logic of the Payments query to grab all payments, and then I use my data Object's logic to add the right payments from the cursor to itself. This not only results in more straight-forward code, but it's a lot faster. Instead of multiple queries to slow internal storage (the DB), I'm putting the Cursor in much faster memory and running through it with loops.
I was pleasantly surprised by how clean this solution is versus the old way I used to query the DbHelper directly and use startManagingCursor() to well, ya know. The problem with the old way was Deprecated methods as well, I'd get ANRs for loading cursors on the UI thread. This solution fixes both of those, and it also structures my logic in a much more "detached" way, I'm no longer relying on the order of my code's execution, using the callbacks I have much more control over all data related aspects as I know exactly when and where the UI will be updated.
Loaders, especially when I start to combine them with ContentProviders, I'm sure will be a eurka! moment close to that of when I learned to convert my apps to using Fragments (also well worth the climb, still learning of course).
All in all, keep at it, I almost gave up here folks...
Thanks!

Disable notifications on a ContentProvider URI

I'm looking for a way to suspend notifications on a given ContentProvider's Uri. The use case is:
An Activity is bound to a CursorAdapter through a CursorLoader.
A Service may do a lot of batch, single-row updates on a ContentProvider.
The CursorLoader will reload its content on every row update, as the ContentProvider notifies listeners by ContentResolver#notifyChange.
Since I cannot edit the ContentProvider, and I have no control over the batch queries execution, is there a way to suspend notifications on a Uri (in the executing Service) until all of the ContentProvider-managed queries have been executed? I need this in order to avoid the flickering caused by the continuous requerying of the CursorLoader.
You cannot disable this mechanism in your Service. But you should try to batch them by using ContentProviderOperations.
I've written an introductory post about ContentProviderOperations and two additional posts covering the methods withYieldAllowed() and withBackReference() respectively.
Especially the latter one should be of interest for what you've described here.
With ContentProviderOperations you can batch multiple updates and inserts. If you then call applyBatch() on your ContentResolver object the ContentProvider executes them all at once.
Now I've never used Nicolas Klein's generator but since he is a very, very proficient Android developer and works at Google, I bet that the generated code makes use of transactions and calls notifyChange() only once for the complete batch at the end.
Exactly what you need.
Can you substitute your own ContentResolver?
You may try extends ContentResolver with your own class then and you will may override method notifyChange and realize your logic there.
In your Content provider class, inside query() method before returning the cursor, just comment the code which looks something like this
cursor.setNotificationUri(getContext().getContentResolver(), uri);

Calling a Java method from a sqlite trigger (android)

I want registered callback method using sqlite trigger
for example,
public void printLog(){
Log.i("TAG","1 row added");
}
this method calling after insert any row in sqlite.
Is it possible?
How to do that?
SQLite provides Data Change Notification Callbacks. I don't think that Android exposes them directly but it does have for example CursorAdapter which provides some change notifications.
You can use also use the getContentResolver().registerContentObserver but unfortunately it doesn't tell you what kind of change was made, it could be a delete, insert or update.
If you control the ContentProvider that interfaces with the DB then you could fire an Intent or use getContentResolver().notifyChange to send a special Uri notification that identifies both the table and action. An example Uri you could notify with might be: content://my-authority/change/table-name/insert
But even then you don't know exactly which rows were effected by the change.
Seems like triggers that write to a change log table will guarantee you hear about all changes regardless of where they came from, and you can know the exact id and action that occurred. Unfortunately it means slower inserts/updates/deletes and it means you probably need a Service of some kind to process and delete changes.
I'd love to hear if these is some better solution out there!
You can set content observer this link will help Receives call backs for changes to content http://developer.android.com/reference/android/database/ContentObserver.html

android - CursorLoader & SQLite without Content Provider

I know this has been discussed yet I wanted to ask about the current state of the matter. Do i have to create a ContentProvider to use CursorLoader in connection with a sqlite database?
I found
CursorLoader usage without ContentProvider
Looks exactly what I was hoping for yet as Emmby commented
Users should be aware of one limitation, which is that it has no mechanism to refresh on data changes (as Loaders are supposed to do)
So another solution is mentioned
https://github.com/commonsguy/cwac-loaderex
yet again some drawback is pointed out
However, to make use of the automatic re-querying, you need to use the same loader for the UI as well as for the updates, limiting its usability for background services.
Of course when using LoaderManager we want to get all the benefits for which it was introduced. So my question is if there is a way to use LoaderManager in connection with a sqlite database without having to implement a content provider yet have all the benefits of it.
Thanks
The two implementations you mention in your post both offer all of the benefits of the CursorLoader except the ability to receive notifications when the underlying content changes.
I've been looking into this a lot recently and I can confidently tell you that the Android API currently does not provide a means of doing this with only a raw SQLiteDatabase (it only provides the ContentResolver#notifyChange() and Cursor#setNotificationUri() methods, which are used to notify all Cursors registered under a certain notification Uri).
That said, your options right now are to:
Implement an observer yourself that is capable of receiving notifications from the SQLiteDatabase when the content changes, and is somehow able to relay these notifications to all existing Loaders in your application. I wrote a pretty extensive blog post on how to implement Loaders that might come in handy if you wish to take on this challenge. Or...
Use Mark Murphy's LoaderEx library and only make database modifications using the AsyncTask operations his library provides. Note that the reason why his tasks refresh the Loader is because they call onContentChanged on the Loader immediately after the insertion/update/delete is performed, effectively telling the Loader that the content has changed and that it should refresh its data.
Just use a ContentProvider with a CursorLoader and you can use the ContentResolver#notifyChange() method to notify the CursorLoader that a content change has occurred.
I'm trying to figure out a better solution, and I'll report back in the future if I ever find/implement one, but for now these will have to do.
Here is my solution, in my onCreateLoader
{
Uri u = Uri.parse("content://what_string_you_want");
return new CursorLoader(this, yourURI, projection, null, null, null) {
private final ForceLoadContentObserver mObserver = new ForceLoadContentObserver();
#Override
public Cursor loadInBackground() {
Cursor c = YOUR_DATABASE.doYourQuery(...);
if (c != null) {
// Ensure the cursor window is filled
c.getCount();
c.registerContentObserver(mObserver);
}
c.setNotificationUri(getContext().getContentResolver(), getUri());
return c;
}
};
}
After the code that will change DB, add
getContentResolver().notifyChange(
Uri.parse("content://same_with_first_string"), null);
how about having a boolean in shared preferences as false .. updating the content when that boolean is true....
and when any of the operations which changes the underlying database .. that boolean will be set to true and as shared preferences you a changelistener you can recieve changes live directly after the relevant methods are called

Categories

Resources