Advertisement
Android SDK

Android Fundamentals: Downloading Data With Services

by

The tutorial content of the still-unnamed "TutList" application we've been building together is getting stale. The data has been the same for over a month now. It's time to breathe some life into the application by providing it with a means to read fresh Mobiletuts tutorial data on the fly.

As it stands now, our application reads a list of tutorial titles and links from a database. This is, fundamentally, the correct way to design the app. However, we need to add a component that retrieves new content from the Mobiletuts website and stores new titles and links in the application database. This component will download the raw RSS feed from the site, parse it for the title and link, then store that data into the database. The database will be modified to disallow duplicate links. Finally, a refresh option will be placed within the list fragment so the user can manually force an update of the database.
As with other tutorials in this series, the pacing will be faster than some of our beginner tutorials; you may have to review some of the other Android tutorials on this site or even in the Android SDK reference if you are unfamiliar with any of the basic Android concepts and classes discussed in this tutorial. The final sample code that accompanies this tutorial is available for download as open-source from the Google code hosting.

Step 0: Getting Started

This tutorial assumes you will start where our last tutorial, Android Fundamentals: Properly Loading Data, left off. You can download that code and work from there or you can download the code for this tutorial and follow along. Either way, get ready by downloading one or the other project and importing it into Eclipse.

Step 1: Creating the Service Class

One way to handle background processing, such as downloading and parsing the Mobiletuts tutorial feed, is to implement an Android service for this purpose. The service allows you to componentize the downloading task from any particular activity or fragment. Later on, this will allow it to easily perform the operation without the activity launching at all.

So let’s create the service. Begin by creating a new package, just like the data package. We named ours com.mamlambo.tutorial.tutlist.service. Within this package, add a new class called TutListDownloaderService and have it extend the Service class (android.app.Service). Since a service does not actually run in a new thread or process from the host (whatever other process or task starts it, in this case, an Activity), we need to also create a class to ensure that the background work is done asyncronously. For this purpose, we could use a Thread/Handler design or simply use the built-in AsyncTask class provided with the Android SDK to simplify this task.

We'll use AsyncTask for our application. Create a private inner class that extends AsyncTask for use just by the Service class. We'll get to the specific implementation details shortly, but here's the empty Service class implementation with the inner AsyncTask:

public class TutListDownloaderService extends Service {

    private static final String DEBUG_TAG = "TutListDownloaderService";
    private DownloaderTask tutorialDownloader;

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // TBD
        return Service.START_FLAG_REDELIVERY;
    }

    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    private class DownloaderTask extends AsyncTask<URL, Void, Boolean> {

        private static final String DEBUG_TAG = "TutListDownloaderService$DownloaderTask";

        @Override
        protected Boolean doInBackground(URL... params) {
            // TBD
        }

        private boolean xmlParse(URL downloadPath) {
            // TBD
        }

    }

}

Step 2: Configuring the Service

Within the Android Manifest file, you must now add a new entry to register the service. Like other entries, the <service> tag entry is simple. In this case, you need only supply its name property. The entry should be placed with the <application> tag.

<service
    android:name=".service.TutListDownloaderService"></service>

Step 3: Database Modifications

We're going to leverage the UNIQUE row attribute of the SQLite database to prevent duplicate entries from being inserted. Let's make this change now.
Within the TutListDatabase class, modify the CREATE_TABLE_TUTORIALS String as follows:

private static final String CREATE_TABLE_TUTORIALS = "CREATE TABLE "
        + TABLE_TUTORIALS + " (" + ID
        + " integer PRIMARY KEY AUTOINCREMENT, " + COL_TITLE
        + " text NOT NULL, " + COL_URL + " text UNIQUE NOT NULL);";
[/code

Since we have fundamentally changed how the database functions, you should now update the database version to 2 by editing the DB_VERSION constant.
<h2><span>Step 4: </span> Provider Modifications</h2>

Our previous implementation of the insert() method within the TutListProvider class used the SQLiteDatabase class method called insert(). In order to catch and ignore the constraint failure for duplicate titles, switch this to the method called insertOrThrow() and catch the SQLiteConstraintException exception. This exception will be fairly routine and may or may not be the most optimal way to prevent duplicates, but it's straightforward and easy to implement. Here's the updated implementation of the insert() method of TutListProvider:
[code language="java"]
@Override
public Uri insert(Uri uri, ContentValues values) {
    int uriType = sURIMatcher.match(uri);
    if (uriType != TUTORIALS) {
        throw new IllegalArgumentException("Invalid URI for insert");
    }
    SQLiteDatabase sqlDB = mDB.getWritableDatabase();
    try {
        long newID = sqlDB.insertOrThrow(TutListDatabase.TABLE_TUTORIALS, null,
                values);
        if (newID > 0) {
            Uri newUri = ContentUris.withAppendedId(uri, newID);
            getContext().getContentResolver().notifyChange(uri, null);
            return newUri;
        } else {
            throw new SQLException("Failed to insert row into " + uri);
        }
    } catch (SQLiteConstraintException e) {
        Log.i(DEBUG_TAG, "Ignoring constraint failure.");
    }
    return null;
}

Step 5: Implementing the AsyncTask

All of the core downloading and parsing background processing occurs within the DownloaderTask inner class, specifically within the doInBackground() callback method. Let's look at the doInBackground() method:

@Override
protected Boolean doInBackground(URL... params) {
    boolean succeeded = false;
    URL downloadPath = params[0];

    if (downloadPath != null) {
        succeeded = xmlParse(downloadPath);
    }
    return succeeded;
}

We're only acting on a single parameter. This could be extended to iterate through all URLs, downloading and parsing many XML files. However, this application is specific to just one feed. We pass on the URL to the xmlParse() helper method where all of the parsing will happen.

Step 6: Parsing the XML

If you're thinking we skipped right over downloading, we didn't. We're going to use the XmlPullParser class to parse the XML. It takes an InputStream. Since the URL class can provide an InputStream via its openStream() method, let's just do that:

XmlPullParser tutorials;

tutorials = XmlPullParserFactory.newInstance().newPullParser();
tutorials.setInput(downloadPath.openStream(), null);

The parts of the RSS format that we're interested in are the item tags and, within those, the link and title tags. We'll do this with two while loops. Here's the parsing:

while (eventType != XmlPullParser.END_DOCUMENT) {
    if (eventType == XmlPullParser.START_TAG) {
        String tagName = tutorials.getName();
        if (tagName.equals("item")) {
            // inner loop looking for link and title
            while (eventType != XmlPullParser.END_DOCUMENT) {
                if (eventType == XmlPullParser.START_TAG) {
                    if (tutorials.getName().equals("link")) {
                    } else if (tutorials.getName().equals(
                            "title")) {
                    }
                } else if (eventType == XmlPullParser.END_TAG) {
                    if (tutorials.getName().equals("item")) {
                        // save the data, and then continue with
                        // the outer loop
                        break;
                    }
                }
                eventType = tutorials.next();
            }
        }
    }
    eventType = tutorials.next();
}

Now the data needs to be added to database through the application’s content provider.

Step 7: Adding New Tutorials to the Content Provider

Adding data through a content provider is straightforward. Here's the code to add a new entry:

ContentValues tutorialData = new ContentValues();
tutorialData.put(
        TutListDatabase.COL_URL,
        "http://some url");
tutorialData.put(
        TutListDatabase.COL_TITLE,
        "Some Title");
getContentResolver().insert(
        TutListProvider.CONTENT_URI,
        tutorialData);

Basically, you're adding a set of content values to a particular URI as a new entry. We don't need to do anything with the results. Additionally, the list control is already listening for changes to the database so as soon as the data is added, the list updates. How nice is that?

Step 8: Adding a Refresh Menu Item

Finally, let's add a mechanism for the user to refresh the data on their own. For this, we'll use the options menu of the TutListFragment class. We could use the new Action Bar if we were only targeting Android 3.0, but we’d prefer if our app runs on a variety of platform versions. Adding a menu item to a Fragment causes that menu item to be added to whatever other menu items the Activity and other contained Fragments have. In this way, it doesn't matter what Activity this Fragment exists under, the option menu item will be there.

Here are the new methods to add to the TutListFragment class to managing an options menu with a refresh option:

private int refreshMenuId;
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
    Intent intent = new Intent(getActivity().getApplicationContext(),
            TutListDownloaderService.class);
    intent.setData(Uri
            .parse("http://feeds.feedburner.com/MobileTuts?format=xml"));
    inflater.inflate(R.menu.options_menu, menu);
    MenuItem refresh = menu.findItem(R.id.refresh_option_item);
    refresh.setIntent(intent);
    refreshMenuId = refresh.getItemId();
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    if (item.getItemId() == refreshMenuId) {
        getActivity().startService(item.getIntent());
    }
    return true;
}

Besides creating and configuring the options menu, you know see how to start a service. That's right! It's exactly like starting an Activity, except using the startService() method instead of the startActivity() method. Consistency is nice, huh?

And here's the options_menu.xml menu XML resource definition (referenced in the above code):

<?xml version="1.0" encoding="utf-8"?>
<menu
    xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:id="@+id/refresh_option_item"
        android:icon="@drawable/ic_menu_refresh"
        android:title="Refresh"></item>
</menu>

We also added a typical refresh icon to the resources. In fact, we borrowed ours from the platform resources included with the Android SDK. It's under creative commons licensed.

Now, when you run the application, you'll see that it has a menu:

Android SDK Figure

Finally,after pressing the refresh button, you'll see the new items appear at the bottom of the list:

Android SDK Figure

Refinements

There are several refinements that could be made to the application at this point. Ordering the list of tutorials may be high on your list. Search may also be high on the list. As these are tutorials, removing read items may not be, though, as you might need to reference an old tutorial sometime in the future. The RSS data also includes category information, so you might want to consider filtering tutorials based on metadata of this kind. Several other improvements also come to mind.

We've covered none of these topics. So far. :)

Any in particular you'd like to see discussed in a future tutorial? Let us know!

Conclusion

You learned about several new topics in this tutorial by adding a background download service to the "TutList" application. You learned how to create and use an Android Service. You learned how to create and using an AsyncTask object. You learned how to parse XML from the live network URL. Finally, you learned how to create and use a simple options menu in conjunction with Fragments.
As always, we look forward to your feedback.

About the Authors

Mobile developers Lauren Darcey and Shane Conder have coauthored several books on Android development: an in-depth programming book entitled Android Wireless Application Development and Sams Teach Yourself Android Application Development in 24 Hours. When not writing, they spend their time developing mobile software at their company and providing consulting services. They can be reached at via email to androidwirelessdev+mt@gmail.com, via their blog at androidbook.blogspot.com, and on Twitter @androidwireless.

Need More Help Writing Android Apps? Check out our Latest Books and Resources!

Buy Android Wireless Application Development, 2nd Edition  Buy Sam's Teach Yourself Android Application Development in 24 Hours  Mamlambo code at Code Canyon

Related Posts
  • Code
    Android SDK
    Create a Music Player on Android: User Controls0d63m preview image@2x
    We are building a simple music player app for Android in this series. So far, we have presented a list of the songs on the device and allowed the user to make selections from it, starting playback using the MediaPlayer class in a Service class. In this final part of the series, we will let the user control playback, including skipping to the next and previous tracks, fast-forwarding, rewinding, playing, pausing, and seeking to particular points in the track. We will also display a notification during playback so that the user can jump back to the music player after using other apps.Read More…
  • Code
    Android SDK
    Create a Music Player on Android: Song Playback0d63m preview image@2x
    In this series, we are creating a music player on Android using the MediaPlayer and MediaController classes. In the first part, we created the app and prepared the user interface for playback. We presented the list of songs on the user device and specified a method to execute when the user makes a selection. In this part of the series, we will implement a Service class to execute music playback continuously, even when the user is not directly interacting with the application.Read More…
  • Code
    Android SDK
    Create a Music Player on Android: Project Setup0d63m preview image@2x
    The Android platform provides resources for handling media playback, which your apps can use to create an interface between the user and their music files. In this tutorial series, we will create a basic music player application for Android. The app will present a list of songs on the user device, so that the user can select songs to play. The app will also present controls for interacting with playback and will continue playing when the user moves away from the app, with a notification displayed while playback elapses.Read More…
  • Code
    Android SDK
    Consuming Web Services with kSOAPEd4e2 preview image@2x
    In this tutorial, you'll learn how to consume web services using the popular kSOAP library in an Android application. kSOAP is a reputable library loved by developers who have the need to parse WSDL (Web Service Definition Language) and SOAP (Simple Object Access Protocol) messages.Read More…
  • Code
    Android SDK
    Android SDK: Next StepsAndroid preview@2x
    In this series, we've begun learning how to develop Android applications from scratch. We started exploring the development tools, got acquainted with the basic elements in an application project, looked at user interface design, interactivity, resources, and data, and we've also took a closer look at what happens when your application is running. What we've covered so far should put you in a good position to get started creating functional Android applications, but Android has a lot more to offer so the range of possibilities is virtually endless. You may therefore struggle to choose what to learn next. In this part, we'll wrap up the series by pointing out some possible directions for future learning. After this, the final part will be a quiz on what we covered throughout the series.Read More…
  • Code
    Android SDK
    Android SDK: Common Android ComponentsAndroid preview@2x
    In this series we are learning the essential features of Android development that you need to know to start building apps. So far, we've looked at the structure and typical elements in an Android app, including user interface elements and data storage. You can use what we covered already to start creating your own apps. But before you do, we will run through some common Android components in this tutorial, then have a quick look at the SDK samples in the next tutorial.Read More…