12

I was reading the doc, but I am still not too sure. Its says to use getContentResolver(), but then that really isn't using CursorLoader. So is there a way to do it through CursorLoader? I know how to do it with query(). Are the steps very similar? Even just a link that explains exactly this would be helpful.

Please note, do not link me to the Google doc as they do not have an example that ever uses the insert() method from ContentProvider using a CursorLoader.

Thanks in advance!!

Edit: I should probably mention the reason I am confused with this is because calling a new CursorLoader automatically calls ContentProviders query() method. But how can I do the same for insert?

Alex Lockwood
  • 83,063
  • 39
  • 206
  • 250
Andy
  • 10,553
  • 21
  • 75
  • 125
  • https://developer.android.com/guide/topics/providers/content-provider-basics.html#Inserting – Mia Apr 20 '17 at 09:13

2 Answers2

40

Check out my blog post on the subject:

Content Resolvers and Content Providers


The CursorLoader has nothing to do with it.

Insertion is a totally different concept... it has absolutely nothing to do with the CursorLoader. When coupled with the LoaderManager, the CursorLoader automatically queries your database and updates itself when the ContentObserver is notified of a datastore change. It has nothing to do with the actual process of inserting data into your database.

How requests to the ContentResolver are resolved

When you insert (or query or update or delete) data into your database via the content provider, you don't communicate with the provider directly. Instead, you use the ContentResolver object to communicate with the provider (note that the ContentResolver is a private instance variable in your application's global Context) . More specifically, the sequence of steps performed is:

  1. You call getContentResolver().insert(Uri, ContentValues);

  2. The ContentResolver object determines the authority of the Uri.

  3. The ContentResolver relays the request to the content provider registered with the authority (this is why you need to specify the authority in the AndroidManifest.xml).

  4. The content provider receives the request and performs the specified operation (in this case insert). How and where the data is inserted depends on how you implemented the insert method (ContentProvider is an abstract class that requires the user to implement insert, query, delete, update, and getType).

Hopefully you were able to wrap your head around that at least a little. The reason why there are so many steps involved is because Android (1) allows applications to have more than one content provider, and (2) needs to ensure that apps can securely share data with other third-party apps. (It wasn't because it wanted to confuse you, I promise).

Inserting data via the ContentProvider

Now that you (hopefully) have a better idea of how the ContentResolver is able to relay these requests to the content provider, inserting the data is fairly straight forward:

  1. First, decide which uri you want to have matched by your content provider. This depends on how you decided to match your uris with the UriMatcher. Each uri you have represents a different means of inserting data into your internal database (i.e. if your app has two tables, you will probably have two uris, one for each table).

  2. Create a new ContentValues object and use it to package the data you wish to send to the content provider. The ContentValues object maps column names to data values. In the below example, the column name is "column_1" and the value being inserted under that column is "value_1":

    ContentValues values = new ContentValues();
    values.put("column_1", "value_1");
    
  3. Once received, the content provider will (in your case) pass the values object to your SQLiteDatabase (via the SQLiteDatabase.insert(String table, String nullColumnHack, ContentValues values) method). Unlike the ContentProvider, this method is implemented for you... the SQLiteDatabase knows how to handle the values object and will insert the row into the database, returning the row id of the inserted row, or -1 if the insertion failed.

... and that's how you insert data into your database.


TL;DR

Use getContentResolver().insert(Uri, ContentValues);

Alex Lockwood
  • 83,063
  • 39
  • 206
  • 250
  • btw, IDK if you started reading but I updated the first half of the post to make it more clear :) – Alex Lockwood Jun 21 '12 at 04:16
  • +1. I really have no idea how you guys can write a canon answer in a few minutes. – davidcesarino Jun 21 '12 at 04:28
  • @David, if it makes you feel any better, this took at least an hour to type up... I needed it to be perfect :P – Alex Lockwood Jun 21 '12 at 04:45
  • Wow. Honestly, very few people on here ever explain it as well as you do. I really appreciate that explanation on `ContentProviders`. You essentially hit it spot on. No wonder the docs did not provide anything for those instances. And you are probably right, I am sure they didn't do it to confuse us haha. So just to reiterate and to fully understand, using `getContentResolver().insert(Uri, ContentValues)` is still like using the `LoaderManager` with the `CursorLoader`, assuming it was used before? Or not lol. Thats about the only thing I am iffy about. (Oh and I was busy all day, so my bad). – Andy Jun 22 '12 at 00:31
  • 2
    Actually, now after reading it like 20 times, you are essentially saying that `CursorLoader` and `LoaderManager` don't really have anything to do with it. But my question now is, is this done in the background, off the UI Thread essentially? – Andy Jun 22 '12 at 00:39
  • 4
    `insert` and `query` are performed on the **UI thread**. I'm planning on writing up something on how to perform expensive insertions/queries on a separate thread sometime this weekend... I'll let you know. – Alex Lockwood Jun 22 '12 at 16:47
  • @Andy, I wrote a [**blog post**](http://www.androiddesignpatterns.com/2012/06/content-resolvers-and-content-providers.html) about this topic... mostly because I was lazy and I already wrote most of the stuff here :D. But I'm really really really hoping I will find some time this week to write about Loaders because there is literally nothing good on the web about loading data asynchronously (well, there are a couple good sites but nothing that describes it in detail). I'll post it to one of your questions later when I write it :) – Alex Lockwood Jun 26 '12 at 03:41
  • Crap. Which means I am doing my database stuff (mainly `insert`) on the UI Thread. And I might as well just add you on G+. If you don't mind. You can just let me know there when you get a chance. But I will also definitely do some research on asynchronous data loading. Because I will need it eventually. Thanks. Btw, if its done on the UI Thread, how can I get it off? You can just give me a hint and I'll research it :) – Andy Jun 26 '12 at 03:50
  • Yeah, on my blog I basically advertise people to add me on G+ so its fine haha. Yes, it's always recommended to perform inserts/queries on a different thread. The best way to do it is with a `CursorLoader` (and this is the suggested way). Unfortunately, basically the only examples online that give `CursorLoader` tutorials involve querying... not inserting. That's why I wanted to write something about it (and it will probably be harder to write because I'll have to come up with some examples or something as well). But yeah, add me on G+ and I'll let you know. – Alex Lockwood Jun 26 '12 at 03:58
  • 2
    @Andy, First post on `Loader`s and the `LoaderManager` is done... the next one is going to be very specific to how the `LoaderManager` works. And then the third one is going to be very specific towards how `Loader`s work... and then after that I'll probably give some sample code on how to offload insert/delete/update transactions to a separate thread... and also give some details on how to use a `Loader` to query data from a SQLite database w/o a `ContentProvider`. I need to stop being so lazy. http://www.androiddesignpatterns.com/2012/07/loaders-and-loadermanager-background.html – Alex Lockwood Jul 08 '12 at 01:42
  • @AlexLockwood haha. Its fine. I will be sure to keep reading them. I am intent on learning everything I can on Android. Thanks to you my Android coding ability is shaping up, so much appreciated. – Andy Jul 08 '12 at 02:49
  • 1
    @Andy I finally wrote up that post on Loaders by the way... it took all summer. Your numerous questions about them really motivated me to write the series so thanks :D – Alex Lockwood Sep 08 '12 at 01:10
  • [For asynchronous insert someone suggests AsyncQueryHandler](http://stackoverflow.com/a/13572393) – Alex Oct 02 '14 at 11:22
  • What does the insert method return? How can I tell that the insert was successful without running a select statement? – AdamMc331 Dec 18 '14 at 02:06
  • 2
    @AlexLockwood You said below "the only examples online that give CursorLoader tutorials involve querying... not inserting. That's why I wanted to write something about it", but your blog posts actually don't address using `CursorLoader` to do async `insert()` at all. Is the recommended way to use an `AsyncTask`, an `AsyncQueryHandler`, extend `AsyncTaskLoader`, or some other way? – Tim Rae Jun 24 '15 at 05:53
  • 1
    @AlexLockwood Sorry to bother you, but did you write the post on how to perform insertions/updates/deletes on a separate thread? There's absolutely not a single good example out there on how to do this. The only thing they use is AsyncTasks, which obviously is not a good idea. I was thinking on using `AsyncQueryHandler`, but I'm not sure about configuration changes(what happen to the operations? Will the continue to do their work, or will they be cancelled?). Please if you could find the time to write that article, we would be extremely grateful. Thanks. – Axel Jun 30 '15 at 12:53
  • @AlexLockwood How would you handle inserts/updates/deletes on a separate thread? I mean, how would you normally do this? AsyncQueryHandler perhaps? You can just give us a hint and we'll research it. – eddy Jun 30 '15 at 19:33
  • You could use an `ExecutorService`, or an `IntentService` I suppose. I guess it's really up to you how you want to design it. – Alex Lockwood Jun 30 '15 at 19:40
  • 1
    @AlexLockwood Thanks for the reply. I would love to use IntentService, but wouldn't it be a little bit too overkill for CRUD operations that are not so complex? Would you use it? I was also thinking about using AsyncTasks, but then I remember that I have to be careful with configuration changes. It would be really awesome if your could have something like CursorLoaders, but for CRUD operations. I think I could use `AsyncQueryHandler`, but I'm not sure if it's dependent or not on the activity’s lifecycle. What happens to the operations running on the background on configuration changes? – Axel Jul 01 '15 at 05:52
  • @AlexLockwood **Please** do you think you could give us a hint of the approach you were going to take in the post in which you were going to address how to execute CRUD operation off the UI thread. – eddy Jul 02 '15 at 12:14
2

Convert Cursor to ContentValues for easy database insertion.

Its says to use getContentResolver(), but then that really isn't using CursorLoader

Besides what Alex said, there's nothing preventing you from iterating through the Cursor returned, putting them into ContentValues and then inserting that (say, to a different DB).

Just an example from the top of my head (a simple Cursor of two String columns):

@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
    if (cursor.moveToFirst()) {
        ArrayList<ContentValues> values = new ArrayList<ContentValues>();
        do {
            ContentValues row = new ContentValues();
            DatabaseUtils.cursorStringToContentValues(cursor, fieldFirstName, row);
            DatabaseUtils.cursorStringToContentValues(cursor, fieldLastName, row);
            values.add(row);
        } while (cursor.moveToNext());
        ContentValues[] cv = new ContentValues[values.size()];
        values.toArray(cv);
        getContentResolver().bulkInsert(CONTENT_NAMES, cv);
    }
}

There are probably more efficient ways to do that (and definitely some integrity checks), but that was my first thought...

davidcesarino
  • 16,160
  • 16
  • 68
  • 109
  • Obviously, this mostly applies when your `Cursor` source is _not_ your own DB. Otherwise, you'll be often _updating_ the rows, not inserting new ones from your own data (inserting the same data). Still, you can adapt to what you want to do _exactly_... – davidcesarino Jun 21 '12 at 04:26
  • My head is basically melted at this point from all the StackOverflow, but this is definitely correct and efficient (i.e. using `bulkInsert` instead of individual inserts, re-using the old data before `onLoadFinished` returns, etc.). That said, I can't think of a reason (off the top of my head) why you would actually *need* to do this haha. – Alex Lockwood Jun 21 '12 at 04:55