Pony Express Tutorial Part 1: You gotta start somewhere..

So, where to start. Well, if you haven’t read my last post where I explained the rationale for this series of posts then go back and read it ;), as for me, well, for a change I thought I wouldn’t start with ‘Hello World!’  If you really want to, you can find it in the Android developer docs, so I won’t repeat it here.  Instead let’s start with the big picture and then break it down.  What does the app need to do, and what functions and assets does it need in order to do it?  The app I am developing, Pony Express, needs to do three things: download podcast episodes, play them and sent status updates to identi.ca.   In the various parts of this series we will look at each of these, but let’s get started with what’s required to download the podcasts.

Catching those ‘casts…

In order to download podcast episodes we need to do three things:

  • Find out where we can download the podcast episodes from.
  • Download the episodes.
  • Record somewhere that the episodes have been downloaded, so we don’t download them again next time.

So, this part of the series will focus on how we determine where we download the podcast episodes from.

‘They seek him here, they seek him there…’

Podcasts are usually published via an RSS feed from a website such as feedburner.  An RSS feed is written in XML, and consists of a number of elements.  Each element holds a piece of information, as can be seen in the example below.

<rss version="2.0">
    <item>
       <title>Linux Outlaws 155 - There is No Fabian Stable</title>
        <pubDate>Fri, 11 Jun 2010 14:13:46 +0200</pubDate>
        <enclosure url="http://media.libsyn.com/media/linuxoutlaws/linuxoutlaws155.ogg" fileSize="47403238" type="application/ogg">
    </item>
    <item>
        <title>.......</title>
        <pubDate>...... </pubDate>
        <enclosure .................>
    <\item>;
</rss>

So, this feed has rss, item, title, pubDate and enclosure elements .  The title, pubDate and enclosure elements are between the opening and closing tags of the item elements, which defines them as child elements of each item element.  The item elements are likewise child elements of the rss element.  So, an RSS feed for a podcast would usually contain many item elements, one for each episode; each with its own title, pubDate, and enclosure child elements.  To download each podcast we need to know the URL that it can be downloaded from, which in this example is given by the url ‘attribute’ of the enclosure element.  Now, this is all very nice, but how can we access this information with our app?

What we need to use is one of the the Android XML libraries parse() methods.  These methods take an XML source and a SAX (Simple API for XML) Content Handler.  The XML source can be a String object, the XML from a Reader, or the XML from an InputStream.  A SAX Content Handler instance defines which elements we are interested in, and what we want to do when we encounter them.  Leaving aside the SAX Content Handler for a moment, the XML we want to parse is an RSS feed served by a website.  We can use the Java net library to open a connection to the site, and to return an InputStream object, with a class like this:


package org.sixgun.ponyexpress.util;

import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;

public abstract class BaseFeedParser {
    final URL mFeedUrl;
    /** Constuctor - Takes the String url of the feed to be passed.
    * @param feedurl
    */
    protected BaseFeedParser(String feedUrl){
        try {
             mFeedUrl = new URL(feedUrl);
        } catch (MalformedURLException e) {
            throw new RuntimeException(e);
        }
    }
    /**
    * Opens a connection to mFeedUrl
    * @return an InputStream from the mFeedUrl
    */
    protected InputStream getInputStream() {
        try {
            return mFeedUrl.openConnection().getInputStream();
        } catch (IOException e) {
            return null;
        }
    }
}

Note that this is an abstract class, this is because we may want to use this functionality in different sub classes, each handling different XML streams.  In order to use this class to parse the RSS feed, we need to subclass it and create an EpisodeFeedParser class.

package org.sixgun.ponyexpress.util;

import java.util.ArrayList;
import java.util.List;
import java.io.InputStream;

import org.sixgun.ponyexpress.Episode;

public class EpisodeFeedParser extends BaseFeedParser {
    public List<Episode> parse() {
        final Episode episode = new Episode();
        final List<Episode> episodes = new ArrayList<Episode>();

        InputStream istream = getInputStream();
        //We will add the code to set up the parsing next.
        return episodes;
    }
}

The EpisodeFeedParser class has only one method of its own, parse(), which at the moment only calls the getInputStream() method of its super class, BaseFeedParser, and then returns an empty List of Episodes.  The Episode type is a regular Java object which I won’t define here, for the sake of simplicity.  It has fields for each episodes url, title and publication date etc.. as well as getters and setters for them.  We now need to add the code that will allow us to create a SAX Content Handler and allow us to parse the RSS feed.

Play that SAX!

To create a SAX Content Handler, you first need to describe the structure of the XML you are interested in, using the RootElement and Element classes of the Android SAX library.  The RootElement is a single element that all of the other elements that you want to access, are child elements of.  In the example RSS feed above, and in most RSS feeds, the rss element is the root element of all the other elements, so we can define it as our RootElement like so:


RootElement root = new RootElement("rss");

We can then declare our interest in each of the item and enclosure child elements using the requireChild() method of the RootElement class :


Element item = root.requireChild("item");
Element enclosure = item.requireChild("enclosure");

Next we need to set some ‘listeners’ for each of the elements we want to catch.  In Android, listeners are callback functions that are called when particular events occur.  Here, we can set listeners for the start or the end of each element and they will be called when the XML is parsed.  We use a StartElementListener when we want to read the attributes of an element, so this is the type we will use to read the podcast URL from the enclosure element.

enclosure.setStartElementListener(new StartElementListener() {
    @Override
    public void start(Attributes attributes) {
        String episodeUrl = attributes.getValue("","url");
        episode.setLink(url);
    }
});

In this code, the start(attributes) method is called when the start of the enclosure element is parsed.  The first parameter of the getValue method is the namespace URI, which we don’t need here so we pass in an empty string.  The second parameter is the attribute we want the value of, in this case “url”.

We use an EndTextElementListener when we want to read the text held by an element, such as the podcast title in the title element, or the publication date in the pubDate element.

item.getChild("title").setEndTextElementListener(new EndTextElementListener() {
    @Override
    public void end(String body) {
        episode.setTitle(body);
    }
});

Here the end(String body) method is called when the end of a title element is parsed, and the body of the title element is passed in to be stored in an episode instance.

The EndElementListener’s end() method is called when the end of a particular element is reached; we will use it with the item elements, to store the episode instance we have been using in an array (episodes), before moving onto the next episode/item.

item.setEndElementListener(new EndElementListener() {
    @Override
    public void end() {
        episodes.add(episode);
    }
});

We now have everything in place and are ready to create the SAX Content Handler and pass it and the RSS InputStream in to the XML.parse method like this.

Xml.parse(istream, Xml.Encoding.UTF-8, root.getContentHandler());

Now if we put all that code into the EpisodeFeed Parser class we started earlier, we get the finished class shown below.

package org.sixgun.ponyexpress.util;

import java.util.ArrayList;
import java.util.List;
import java.io.InputStream;

import org.sixgun.ponyexpress.Episode;

import android.sax.Element;
import android.sax.EndElementListener;
import android.sax.EndTextElementListener;
import android.sax.RootElement;
import android.sax.StartElementListener;
import android.util.Xml;

public class EpisodeFeedParser extends BaseFeedParser {
    public List<Episode> parse() {
        final Episode episode = new Episode();
        final List<Episode> episodes = new ArrayList<Episode>();

        InputStream istream = getInputStream();

        RootElement root = new RootElement("rss");
        Element item = root.requireChild("item");
        Element enclosure = item.requireChild("enclosure");

        enclosure.setStartElementListener(new StartElementListener() {
            @Override
            public void start(Attributes attributes) {
                String episodeUrl = attributes.getValue("","url");
                episode.setLink(url);
            }
        });

        item.getChild("title").setEndTextElementListener(new EndTextElementListener() {
            @Override
            public void end(String body) {
               episode.setTitle(body);
            }
        });

        item.setEndElementListener(new EndElementListener() {
            @Override
            public void end() {
                episodes.add(episode));
            }
        });

        try {
            Xml.parse(istream,XmlEncoding.UTF-8, root.getContentHandler());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        return episodes;
}
}

We can now create an instance of EpisodeFeedParser from within Pony Express; the RSS feed we give it will be parsed and a List of Episodes, each with their titles and all important URL’s, stored in the appropriate fields, will be returned.

EpisodeFeedParser parser = new EpisodeFeedParser("http://feeds.feedburner.com/linuxoutlaws-ogg");
List<Episode> episodes = parser.parse();

So, that is it for part 1 of this series.  We have created the classes that will enable Pony Express to determine the URL’s from which to download the podcasts.  Next time, we will look at utilising Android Services to download the episodes to our device.

In the meantime, if you want to check out Pony Express further take a look at the code hosted on gitorious.

About these ads

About Paul Elms

Stay at home Dad, ex-biologist, wannabe Android app developer.
This entry was posted in Android, Development, Pony Express. Bookmark the permalink.

6 Responses to Pony Express Tutorial Part 1: You gotta start somewhere..

  1. Tom Sutch says:

    Just thought I’d say thanks for this informative post and I look forward to some more. :) I ploughed through the Android Hello World etc tutorials a few weeks ago, but when I tried to build an app that actually did anything remotely useful I got frustrated with not really knowing where to start, so gave up. Maybe I’ll have another go now — being able to follow what Pony Express does and the principles underlying it will surely help…

  2. Paul Elms says:

    Thanks for your comment Tom. I hope to have the next part of the series published by the weekend. My reason for writing this series was to help others get a better sense of how the various Android methods fit together into an App. I spent weeks going over the documentation before I had half a clue what was going on, so I hope the series helps you!

  3. Mike Hingley says:

    Hi Paul,

    I’ve had various stabs at android development – you post has inspired me to take another go at it (again).

    I’ve downloaded the code from gitorious, but I’ve found that the android emulator runs so slowly on my machine – I started the emulator sunday morning – this morning it was still at the animated flashing android logo. That can’t be right can it? Did you find issues with the emulator?

    • Paul Elms says:

      Hi Mike,
      The emulator is slow, but it shouldn’t be *that* slow! On my laptop which has a Core2 Duo 1.66 GHz processor, the emulator takes a few minutes to start up and maxes out one core resulting in the fan going mad. When it has started it does runs Pony Express OK, if a little slowly. I tend to develop on my phone rather than the emulator because of this. Sorry I can’t help further :(

  4. Gratzi says:

    Hello,

    What about error handling? What happens if an item doesn’t have the “enclosure” tag?

  5. Paul Elms says:

    Hi Gratzi,
    Your right, ideally this code should have some error handling included. In the case you mention, if there is no enclosure tag the requireChild(“enclosure”) method will raise a SAXException, which as there is no code to handle it, will crash the program. The similar getChild() method does not raise an exception in this situation and so would likely lead to problems later in the program. So, this code could be improved by catching any SAXException and warning the user that the RSS feed is not correctly formatted.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s