3

I have a simple database in my Android app that contains information about countries. One of the things I have to do is to populate a dropdown menu with the names of the countries.

So, I wrote some simple code like so:

public class FetchCountryAsync extends AsyncTask<String,Void,Cursor> {
    private Context con;
    private CountryConsumer consumer;
    //----------------------------------------------------------------------------------------------
    public FetchCountryAsync(Context con, CountryConsumer consumer) {
        this.con = con;
        this.consumer = consumer;
    }
    //----------------------------------------------------------------------------------------------
    @Override
    protected Cursor doInBackground(String... params) {
        CountryDatabaseHelper helper = new CountryDatabaseHelper(con);
        Cursor countries = helper.getCountries();
        return countries;
    }
    //----------------------------------------------------------------------------------------------
    @Override
    public void onPostExecute(Cursor countries){
        if(!isCancelled()){
            consumer.setCountries( countries );
        }
    }
    //----------------------------------------------------------------------------------------------
}  

There's a lot of mumbo-jumbo that I did to make it work - an AsyncTask, an interface.

My question is, would it have been a better approach to write my own ContentProvider and avoid the hassle of AsyncTask altogether?

An SO User
  • 24,612
  • 35
  • 133
  • 221
  • 2
    `ContentProvider + LoaderCallbacks` imo – Blackbelt Dec 02 '15 at 17:07
  • @Blackbelt A simple example would be great, if you dont mind :) A stub `ContentProvider` with LoaderCallbacks, I mean. Not the entire thing. :) – An SO User Dec 02 '15 at 17:08
  • Like this [one](http://www.androiddesignpatterns.com/2012/07/understanding-loadermanager.html) ? – Blackbelt Dec 02 '15 at 17:09
  • @Blackbelt definitely, like that one ! :) So, whats a good way to decide when to use `ContentProvider` and when to use `AsyncTask` ? :) – An SO User Dec 02 '15 at 17:11
  • @Blackbelt I am trying to keep the code as clean as possible. `AsyncTask` + `interface` approach leads to a lot of interface pollution. Plus, all the callbacks make it hard to maintaiin. – An SO User Dec 02 '15 at 17:12
  • ContentProvider is always the best option for content that is yours, as you can use a LoaderManager.LoaderCallbacks implementation to load data AS IT CHANGES. This happens without you doing anything, other than letting the ContentObserver know that data has change (CRUD operations) and the UI can then reflect the changes. With an AsyncTask, you execute a task that you use to load data, then let the ui know via callbacks to update. So essentially, an AsyncTask is just you doing the work yourself, while a ContentProvider paired with LoaderCallbacks does all the dirty work for you – Lucas Crawford Dec 02 '15 at 17:16
  • @LucasCrawford okay. And a single `ContentProvider` use multiple tables or there should be one `ContentProvider` per table? – An SO User Dec 02 '15 at 17:22
  • One ContentProvider for multiple tables. Your ContentProvider will divide all CRUD operations up by the uri type, and you define the uris to correspond to different tables (some may even operate on two tables!). A special class called a UriMatcher handles this matching for you that you construct when you define the ContentProvider – Lucas Crawford Dec 02 '15 at 17:23
  • Think of a URI as a path to that particular table or set of tables. When you want to access table A for an insert you pass a different URI than for accessing table B. e.g. public static final Uri CONTENT_URI1 = Uri.parse("content://"+ PROVIDER_NAME + "/sampleuri1"); public static final Uri CONTENT_URI2 = Uri.parse("content://"+ PROVIDER_NAME + "/sampleuri2"); – Lucas Crawford Dec 02 '15 at 17:24
  • can I move my comments into an answer? Many people potentially will read this and will learn something new – Lucas Crawford Dec 02 '15 at 17:28
  • @LucasCrawford go for it. A detailed example would be great for future reference. :) – An SO User Dec 02 '15 at 17:32

1 Answers1

1

It depends on you and what your plans are for your app.

Writing a ContentProvider would likely have been more work but it would provide a much more thorough, flexible access point to the data that you can reuse across your app. Eg query, insert, update, delete methods accessible via Uri.

ContentProviders allow you to centralize and abstract access to DB/other data in your app. This way if the db structure ever changes there's one access point to update for managing information. It just makes things cleaner in my experience. Also, if you ever decide to share the info to other apps the ContentProvider implementation will make that easy.

If its just a 1-off information retrieval task for a single activity in the app, what you have seems fine. If you'll be using it across the app and updating/inserting data in the db or doing more complex queries, it's probably worth the extra time/complexity to make a ContentProvider.

There's another good discussion related to this topic here.

Community
  • 1
  • 1
mabeechen
  • 121
  • 5
  • Ah, this answer adds clarity. Yes, it is a one-off access for the moment. If the data is shared across multiple activities then `ContentProvider` is the way to go, right? :) – An SO User Dec 02 '15 at 18:01
  • It's not a hard and fast rule, but it's definitely something you should consider if you need the information in more than one Activity. Other good criteria to consider is whether you need to insert/update/delete the data in the db, and whether there's any sort of complex variation you expect to have in the queries you want. Eg a subset of items instead of a full list, single items, cross-table query results, etc. – mabeechen Dec 02 '15 at 19:18
  • I see. Also I wanted to ask how I can insert a multivalued field into the database using ContentProvider. For example if a webservice returned a profile of the user as JSON and that JSON contains an array and I wish to persist this profile into the database using ContentProvider. (Not as JSON, though) – An SO User Dec 02 '15 at 20:01
  • When you build the ContentProvider you'll need to provide an implementation for an insert() method that takes a ContentValues object (eg the values you want to insert). To use it you'll create a uri for your ContentProvider and then make a call to the insert() method of a ContentResolver and pass in the uri and content values. The OS will take care of initializing the ContentProvider and passing the arguments to the method you implement. – mabeechen Dec 02 '15 at 20:39