android - CursorLoader & SQLite without Content Provider

21,450

Solution 1

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:

  1. 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...

  2. 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.

  3. 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.

Solution 2

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);
Share:
21,450
dorjeduck
Author by

dorjeduck

Updated on July 09, 2022

Comments

  • dorjeduck
    dorjeduck almost 2 years

    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

  • dorjeduck
    dorjeduck over 11 years
    Great, thanks for this detailed response. Taking this I just feel I should implement a content provider even so it seems like a overkill for any small database i might want to list on the display. Hmmm ...
  • Alex Lockwood
    Alex Lockwood over 11 years
    @dorjeduck Yeah, that's what I usually do too. I really want to find a way to find a way to do it though... I'm not finished with this yet :)
  • dorjeduck
    dorjeduck over 11 years
    love androiddesignpatterns.com by the way - very helpful and what I want to look into after having done my first steps in the android world
  • Alex Lockwood
    Alex Lockwood over 11 years
    @dorjeduck Thanks! Until then, I'll try to write some more cool stuff :P
  • powder366
    powder366 almost 11 years
    Any news here? Can one do it without using ContentProvider?
  • Yeung
    Yeung almost 11 years
    On the other hand, what exactly the drawback with ContentProvider? As I know, the contentProvider is nothing more a mapping layer. And if I set my provider with android:exported="false". It would not visible for other apps.
  • Alex Lockwood
    Alex Lockwood almost 11 years
    @Yeung There is no drawback other than it requires a little bit more code. It's literally just an abstraction layer over a private data store.
  • Eugene
    Eugene over 10 years
    @AlexLockwood, does it mean in order to load data in async manner and get notifications of data changes we need (and it is recommended way?) to implement a ContentProvider in first place? I thought the purpose of ContentProviders is to share data with other application, as it stands in their description, but looks like it is not the only purpose.
  • Alex Lockwood
    Alex Lockwood over 10 years
    @Eugene The global notifications that come with a content provider is not "the purpose" of a content provider... it is just an additional feature. The main purpose of the content provider is to allow for the sharing of data with other applications.
  • IgorGanapolsky
    IgorGanapolsky over 10 years
    @Yeung ~"what exactly the drawback with ContentProvider?" If you have multiple ContentProviders in your application, then you will have to synchronize them yourself, as they are not thread-safe.
  • eddy
    eddy over 9 years
    @AlexLockwood Please, do you think you could help with this stackoverflow.com/questions/27257416/…?
  • vabhi vab
    vabhi vab about 8 years
    @AlexLockwood To use CursorLoader we will use ContentProvider instead of Common database. But documentation claims that ContentProviders are used to share data among two apps. And Other side it is said that "Google re-commanded CursorLoader to get data from a database." Why so ? And is There any way through which i can set data of a Cursor to a recyclerView (in onBindView() method) directly ?