When I started out with my first Android project, I did what probably everyone does when starting with a new Framework/Language/…, I went and read the official developer documentation. I must say, Google is doing a very good job in documenting Android on their Android developer page.
Unfortunately, there is one thing that really annoys me in their documentation, and that is the following statement right at the top of the content provider page
You don’t need to develop your own provider if you don’t intend to share your data with other applications. However, you do need your own provider to provide custom search suggestions in your own application. You also need your own provider if you want to copy and paste complex data or files from your application to other applications.
This has cost me a lot of “going in the wrong direction with things” and from everything I have seen in my Uni course about Android programming this semester, I’m not the only one lead on by this statement!
Why the rant?
What’s the deal with this you ask? Well, let me explain. This statement suggests that one needs Content Providers only if one wants to share stuff with other applications or do some fancy copy and paste. I feel this statement is just wrong. I would argue, every medium sized, database backed application needs a content provider! Not for sharing with other applications (that is obviously cool), but for other reasons which are not discussed on this Google’s help page! Let’s have a look at them
1. Model life cycle management
I guess except for very special cases, some sort of MVC is always a really good idea to have. If you disagree, I’d love to hear why, but at least in my experience MVC is the way to cleanly/professionally tackle a development that involves a UI and data.
So when designing your Android app, there will come a point when a designated model comes into play. Models, contrary to views and controllers, are usually unique in the application (across multiple Activities), thus often implemented by singletons. Great idea, but wait. If I want to have my model as a singleton, it would be handy if the Android Runtime would be able to provide a dedicated life cycle to that Model. Then, so the idea, if the system runs low on memory or feels like the model is not needed anymore, it could be discarded or trimmed. On the other hand, a custom build singleton model is treated like any other unknown object and only killed if the whole application is killed (unless the activity life cycle is relayed to the model which makes things even messier).
Point being: It is an elegant approach to tell the Android Runtime: This is the model, “deal with it!”.
2. Forces a good MVC usage
A point can be made, that not only is the model beautifully embedded into the application life cycle, the ContentProvider abstraction also forces a very sleek and generic access onto the model, making it hard to mess up the model part of the MVC pattern! This is not necessarily a direct benefit, but really good practice!
3. Observer Pattern out of the box
This is where my own model (in my first app) started to really hurt. Parts of the database would be updated by one activity or background task, and other activities needed to be informed. Of course, it is not that hard to write listeners/observers patterns in ones own model, but at one point, things become really complex.
The content provider helps in two ways:
- Through content resolvers, “hooking in” as an observer is really easy and all life cycle again is managed!
- Content providers enforce access via URI, which takes off a lot of the complexity involved in notifying a variety of different listeners depending on what changed. Especially when observers are able to “listen to this URL and its parents” the whole thing creates nice “notification trees” that can help to save on a lot of code (and errors!)
4. Loaders
Loaders are just AWESOME! I wish I would have understood them right from the start and be able to design around them, but I guess that’s the learning curve and the reason for this post. Loaders essentially allow to push the data loading in an asynchronous task (AsyncTask), return a result, and then hang around to watch for the data source changing and act upon it. Of course, including life cycle management tied to to fragement/activity of the loader so no memory is left behind!
I’m especially smitten with the CursorLoader, which queries a content provider on a URI, then return the Cursor which is plugged into an adapter, and then waits for the URI to have a change notification and deliver the new database result to the adapter again. That makes the UI thread essentially free all the time and encapsulates all background logic in a few lines of code! THAT IS AWESOME and should be taught from day one in all tutorials. And guess what is the prerequisite: a content provider!
5. Synchronization
This is actually mentioned in the android developer docs, but I’d like to point it out nonetheless: Content providers are a key piece of androids synchronization framework. I’ll probably do a separate post one of these days on how I used them to connect to Firebase, but more on that later…
Bottom line
Use content providers, they rock!