News

Network Android apps

In part two of his Android app series, Tam Hanna shows how you can make the most of networked applications by creating an RSS reader

Networking Android apps

Read part one of the series here.

As mobile data started to become cheaper and faster, users began to acquaint themselves with a variety of ‘always-on’ applications. Services such as WhatsApp would not have worked in a time when mobile internet access was billed by the minute.

Adding networking to your Android developer capabilities opens endless new opportunities. You can create readers for RSS feeds, communicate with remote services and fetch data from your customer’s favourite websites. Finally, you could also resort to using the network link for more sinister purposes, such as providing usage data to a tracking server in a third-world country.

Whatever you do, a set of rules must be observed in order to keep your application chugging along nice and smoothly. Mobile data is not as reliable as you might expect: you only need to board a high-speed train or drive into a tunnel in order to experience the joys of intermittent connectivity.

Traditional synchronous applications tend to lock up in such cases. This is caused by Android’s GUI stack, which provides one shared thread for all applications – if your code blocks it, all other ones have to wait. Fortunately, there is a large variety of workarounds ready and waiting to be used. Simply follow the instructions outlined in this tutorial in order to get the best coding you can! Difficult concepts are learned faster by observing them in action. The steps over the next few pages will lead you to a small RSS reader, which can use to download blog posts from a website of your choice.

In 2000, blog authors were faced with a difficult obstacle: readers wanted a more direct way to access the content of the individual websites. Because of that, a common data exchange format had to be developed. As time went by, two standards were established.

For now, we will limit ourselves to processing data based on the RSS 2.0 standard. The following example was taken from the official documentation – it provides a rough overview of the way the retrieved resources will look after they have been downloaded:

<?xml version="1.0"?>
<rss version="2.0">
   <channel>
      <title>Liftoff News</title>
      <link>http://liftoff.msfc.nasa.gov/</link>
      <description>Liftoff to Space Exploration.</description>
      <language>en-us</language>
      <pubDate>Tue, 10 Jun 2003 04:00:00 GMT</pubDate>
      . . .
      <item>
         <title>Star City</title>
         <link>http://liftoff.msfc.nasa.gov/news/2003/news-starcity.asp</link>
         <description>How do Americans get ready to work with Russians aboard the International Space Station? They take a crash course in culture, language and protocol at Russia's &lt;a href="http://howe.iki.rssi.ru/GCTC/gctc_e.htm"&gt;Star City&lt;/a&gt;.</description>
         <pubDate>Tue, 03 Jun 2003 09:39:21 GMT</pubDate>
         <guid>http://liftoff.msfc.nasa.gov/2003/06/03.html#item573</guid>
      </item>
      <item>
      . . . (more items)
   </channel>
</rss>

With that out of the way, it is time to start work on our example application. Fire up your favourite Android IDE and create a new project. Its manifest file must declare the INTERNET permission, which can be accomplished by adding these lines to the XML file:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.rsstool"
    android:versionCode="1"
    android:versionName="1.0" >

    . . .
    <uses-permission android:name="android.permission.INTERNET"/>

Work in the background

RSS files are to be downloaded via HTTP. Implementing the required interactions by hand is a cumbersome procedure that can be accelerated by using the HttpURLConnection class. It is a staple food of the ‘java.net’ package, and should be familiar to quite a few developers who have worked with Java in the past.

On a desktop, HTTP transactions tend to be completed within a few milliseconds. Sadly, as many of us know all too well, a mobile phone with a weak signal can work much slower than that. The synchronous nature of HttpURLConnection means that any invocation of its methods on the GUI thread runs the risk of stalling the entire system.

Creating another thread solves a part of the problem. Invoking the method in it leaves the GUI thread free to accomplish other tasks. Sadly, Android’s UI stack is not thread safe – accessing a widget from another thread leads to an exception.

Even though Runnables can be scheduled to run on the GUI thread, a more convenient solution is provided in the AsyncTask. Let us  harness its capabilities by adding a new class called HTTPTask:

public class HTTPTask extends AsyncTask<String, Integer, Integer> {

   @Override
   protected Integer doInBackground(String... arg0) 
   {
      // TODO Auto-generated method stub
      return null;
   }

   @Override
   protected void onPostExecute(Integer result) 
   {
      // TODO Auto-generated method stub
      super.onPostExecute(result);
   }

   @Override
   protected void onProgressUpdate(Integer... values) 
   {
      // TODO Auto-generated method stub
      super.onProgressUpdate(values);
   }
}

AsyncTasks are created from a template class. It requires a total of three parameters, which are used to pass data into and out of the various methods provided:

public class ExampleWorker extends AsyncTask<Params, Progress, Result>

The type passed to Params will be used for setting up the worker method. The second parameter is to be passed to the progress updater, while the third one is used to communicate results.

In our example, three functions are shown. doInBackground contains the actual payload, which will be run on the thread spawned for the AsyncTask. It returns a value, which will be passed on to onPostExecute. This method will be run on the GUI thread, and is ideally suited to displaying the results of the operation in your widget of choice.

onProgressUpdate will not be used i n our example, and is shown here only for didactic reasons. Long-running operations should provide the user with feedback about the progress; this can be done by calling publishProgress from the background worker method. Keep in mind that onProgressUpdate will also run on the GUI thread and should not be used for long-running operations.

Our example must invoke the worker from the constructor of its activity. This can be accomplished by modifying onCreate:

public class MainActivity extends Activity 
{
HTTPTask myTask;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

myTask=new HTTPTask();
myTask.execute("http://www.rssboard.org/files/sample-rss-2.xml");
. . .

An AsyncTask is started by calling its execute function. Invoking doInBackground directly is a serious error, as the logic required for spawning a new thread is skipped in its entirety in this case.

Get the data

Classes derived from AsyncTask expect to receive one more parameter. Fortunately, our HTTPTask is not intended for reuse; we can simply hard-wire the position of the element in question. This leads to the following method:

@Override
protected Integer doInBackground(String... arg0) 
{  
  HttpURLConnection urlConnection=null;
  try
  {
    URL url = new URL(arg0[0]);
    urlConnection = (HttpURLConnection) url.openConnection();
      InputStream in = new BufferedInputStream(urlConnection.getInputStream());
      //Data is here
    } 
    catch (Exception e) 
    {
      return 0;
    }
    finally 
    {
    if(urlConnection!=null)
      urlConnection.disconnect();
    }
    return 1;
  }

Our method starts out by creating a new instance of the URL class. It has the unfortunate habit of throwing an exception if a malformed string is passed in, thereby forcing us to declare the urlConnection outside of the try-catch block.

HttpUrlConnection then connects itself to the URL specified. It returns a reading stream, which can be used for retrieving the data the server provided in response to the query.

Parsing XML

As discussed previously, both Atom and RSS are XML-based formats. Android provides a group of helper functions that simplify the processing so parsing the files by hand is not necessary.

The default parser provided by Google works by simply reading the tags in as they come. This forces you to create a state machine, which will receive that data and sort it into the corresponding data slots. For convenience, the stores are created as member variables of the AsyncTask:

public class HTTPTask extends AsyncTask<String, Integer, Integer> 
{
    Vector<String> titleVect=new Vector<String>();
    Vector<String> descVect=new Vector<String>();

Our parser must start out with the <rss> tag, which contains one or more <channel> tags. Due to that, we set the state variable to -2 in order to inform the parser about the rut it finds itself in. The rest of the code uses the newPullParser to create a new instance:

XmlPullParser parser = Xml.newPullParser();
    parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
    parser.setInput(urlConnection.getInputStream(), null);

        int eventType = parser.getEventType();
        int state=-2;

The actual data processing takes place in a loop. Here we keep invoking getEventType in order to write the latest state into the eventType variable:

while (eventType != XmlPullParser.END_DOCUMENT) 
{
  if(eventType == XmlPullParser.START_TAG) 
  {
    String what=parser.getName();
    if(what.compareTo("rss")==0 || what.compareTo("channel")==0)
    {//Assuming only one channel is present
        state++;
    }
    else if(what.compareTo("item")==0)
    {
        state++;
    }
    else if(state==1 && what.compareTo("title")==0)
    {//Read title next
        state=2;
    }
    else if(state==1 && what.compareTo("description")==0)
    {//Read title next
        state=3;

  }
}

Finding an RSS or a channel tag increments the state variable. Once the item tags are reached, the base state of zero has been reached. An incoming <item> tag sets state to 1, and informs the parser that following title and description tags will refer to a feed entry.

Endings are announced via an END_TAG event. We check which type of tag has been closed and proceed to set the state variable accordingly:

else if(eventType == XmlPullParser.END_TAG) 
{
  String what=parser.getName();
  if(what.compareTo("item")==0)
  {
      state--;
  }
  else if(state==2 && what.compareTo("title")==0)
  {//Read title next
      state=1;
  }
  else if(state==3 && what.compareTo("description")==0)
  {//Read title next
      state=1;

}

The actual textual content of the individual tags is provided via the TEXT event. Our routine checks whether we are interested in the element in question – if yes, the data is dumped into one of the aforementioned vectors:

} else if(eventType == XmlPullParser.TEXT) 
{
  if(state=2)
  {
    titleVect.add(parser.getText());
    state=1;

  }
  else if(state==3)
  {
    descVect.add(parser.getText());
    state=1;
    }
  }
  eventType = parser.next();
}

Show the data

With that, it is time for the final act. The data must be written out into the widgets used for displaying the data – but Android makes creating list views extraordinarily difficult, so for now a simple textbox shall suffice. This leads us to the following classes:

public static class PlaceholderFragment extends Fragment 
{
  HTTPTask myTask;
  TextView myView;

  public PlaceholderFragment() {
  }

  @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
      Bundle savedInstanceState) 
  {
    View rootView = inflater.inflate(R.layout.fragment_main, container,
        false);

    return rootView;
  }

  @Override
  public void onViewCreated(View view, Bundle savedInstanceState) {
    // TODO Auto-generated method stub

    myTask=new HTTPTask();
    myView = (TextView) getView().findViewById(R.id.aTextView); 
    myTask.myView=myView;
    myTask.execute("http://www.rssboard.org/files/sample-rss-2.xml");

    super.onViewCreated(view, savedInstanceState);
  }
}

Inside the HTTPTask, the output is assembled via a StringBuilder:

public class HTTPTask extends AsyncTask<String, Integer, Integer> 
{
  public TextView myView;

  protected void onPostExecute(Integer result) 
  {
    StringBuilder assembledOutput=new StringBuilder();
    // TODO Auto-generated method stub
    for(int i=0;i<titleVect.size();i++)
    {
      assembledOutput.append(titleVect.elementAt(i));
      assembledOutput.append("n");
      assembledOutput.append(descVect.elementAt(i));
      assembledOutput.append("n------------------n");
    }
    myView.setText(assembledOutput.toString());
    super.onPostExecute(result);
  }

Conclusion

After working through the steps in this tutorial, we have created a basic feed reader that downloads and parses RSS data found on the internet. Even though the program is not particularly useful in itself, it demonstrates an array of interesting subjects that can be adjusted for other purposes. Next issue, we reveal how to get to grips with advanced user interfaces, so your app will be picture-perfect. See you next time!

×