Android: How to Use Two Data Sources in ListViews?

Sometimes in our apps we have the need to link to data of other sources. You have a database of your own for your app, but some records also point to the MediaStore content provider to calendar entries or to contacts.

For all detail pages this is not much of a problem. You get the data from your database and query the external content provider for additional data based on the id or lookup uri that you stored in your database.

But what to do, if you want to show data in ListViews? Say contact pictures, event titles for multiline list elements and so on. Then it gets complicated.

From what I see, we have the following choices. Neither is fully satisfying – which is why I would like to hear how you deal with these kind of problems.

  • You get a list of all elements of the ListView by querying your database. And you query the content provider for each list entry using an AsyncTask. This is the a very memory efficient solution.

    For images (album covers, contact pictures…) this is probably the best option. Especially if combined with an in-memory cache. Even if users would love to see images immediately, they know this is not feasible and accept some lag before images are displayed.

    But not so for any text elements. The second line of a multiline list element should be visible instantaneously. So for text this option is not acceptable.

  • You could query the content provider for all entries at startup, put them in a HashMap and use this map later on. Well this might work if the content provider has only a few entries. But there might be thousands of media files on a SD card or thousands of contacts for a big company. In cases like these this won’t work. It would be a big waste of memory – especially since you probably only need very few of these rows at all.

    The Hashmap would also need too long to be created in the first place. The waiting period for the inital screen would be inacceptable. So this is basically a no-go.

  • You could keep redundant data in your own database. For every record that contains a link to the external content provider you also include the data, you need in the listview (e.g. the band name). That’s the option I have chosen. You only cache the results you actually need.

    There are two drawbacks to this solution: First you are wasting device memory. That is flash memory not RAM. No problem for most high end devices but low cost devices often have very limited memory available.

    Second you have to sync the data whenever your app restarts. If your app stays in the background for quite a while, the user might have changed the external data source. In fact this might even happen while your app is in the foreground if a sync adapter changes values based on changes of the backend. Now your list reflects the wrong values until your database has re-synced with the external provider.

    You could choose to listen to changes in the content provider in a service and update your database when needed. Activities then could query for changes.

    Whether these two issues of this option pose problems for you depend on the type of app. In most cases a correction of a changed value later on should be okay. And if you do not have hundreds or thousands of rows in your own table the inefficiencies are not that bad as well.

Now I would really like to hear what you think. Probably there are other options, I have overlooked. Or better ways to make one of these three options work. Please let me know in the comments, on G+ or on Twitter. Thanks!

If you are curious about the G+ discussion, have a look at my G+ announcement of this post.

Share this article:

You can leave a response, or trackback from your own site.

3 Responses to “Android: How to Use Two Data Sources in ListViews?”

  1. I vote for solution number #1 !!!

    More seriously, I consider this problem is exactly similar to the having an image coming from the network in your ListView’s items. Even if contacts and/or media entries are on the same device, sandboxing makes you see these data as if they were available on a server.

    As far as I know, there are actually no way to “join” ContentProviders data. In my opinion, this is due to Android sandboxing and also to the fact a ContentProvider doesn’t know the underlying persistance mechanism (in-memory, SQLite database, etc.)

    If we were sure that a ContentProvider was SQlite-based and there was no sandboxing we could have imagined a way to open the two underlying databases and query them. That would make everything faster, of course, but that would also be a huge break to the “encapsulation principle”.

  2. lazarus says:

    I also had this problem of inter-classing two cursors and displaying them in a ListView.
    My solution was to wrap the original cursors in a custom MergeCursor that extends AbstractCursor. In the onMove method I looked for the row that comes next in each cursor. (There’s a built-in MergeCursor but that was not doing what I needed).

  3. Gyuri Grell says:

    I’d say mostly #1, though if all the data is in my own database, i join the data from multiple tables and use the cursor. Yup that’s right — if I can avoid using ContentProviders, I do, mainly for this reason. Of course you could do a join in your content provider too if columns are requested from multiple tables.

Leave a Reply

You can also subscribe without commenting.

Subscribe to RSS Feed My G+-Profile Follow me on Twitter!