Let's All Learn How to Develop an Android App, Part Five

By Colin Polonowski on at

So we've arrived at the penultimate installment of our App development series -- we may have lost some travelling companions along the way but we're here now and nearly ready to see our App working for the first time. Before we get there we have just a few hurdles left to jump...

Last time we'd created two of our key RSS reader classes -- RSSItem and RSSHandler. This time we'll look at those that remain and what we have to do to get them to work as a whole.

 

RSSListener

The idea behind a 'listener' is that it monitors for user input and then tells the app to do a specific function based on what the user does. Our app is pretty straighforward in that there are very limited interactions with the user -- ONE in fact!

RSSListener is a really simple class -- it has one function; to listen for a user 'click' on a piece of content and then to decide what to do with it. In our case, we just want to open the default browser on the device and load the associated link. Easy!

As the Android SDK already has a lot of functionality built in, it does most of this for you with it's own OnItemClickListener. All we need to do is extend the core functionality to add our specific public facing listener. Within our listener, we could do all sorts of cool stuff -- we could handle swipes, gestures and all sorts of other touch screen interactions, but for now all we need is the simple click.

As we're attaching our listener to our RSSItem we need to ensure we include that in the imported package list too. You can see how we're starting to tie our various functions into each other -- the Listener is applied to the Item. The Item is populated with the Handler -- hopefully the simple underlying structure we're building will make sense...

Here's our basic Listener code:

package com.gizmodouk;
import java.util.List;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import com.gizmodouk.RSSItem;
public class RSSListener implements OnItemClickListener {
ListlistItems;
Activity activity;
public RSSListener(List aListItems, Activity anActivity) {
listItems = aListItems;
activity = anActivity;
}
public void onItemClick(AdapterView parent, View view, int pos, long id) {
Intent i = new Intent(Intent.ACTION_VIEW);
i.setData(Uri.parse(listItems.get(pos).getLink()));
activity.startActivity(i);
}
}

 

RSSLoader

The last of our quartet is the one that takes the RSS URL, pulls in the feed and uses the SAX Parser to convert the RSS XML structure into a list of our RSSItems. Again, a lot of the heavy lifting is done for us -- SAX takes our building blocks and glues them together.

Think of the process as a factory line. RSSItem is a box, the SAX parser is the robotic arm, and the RSSHandler is the software that tells the arm where in the box each item needs to go.

Sticking with that analogy, the RSSLoader is the conveyor belt which takes the 'feed' of content and presents it to the SAX robotic arm:

package com.gizmodouk;
import java.util.List;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import com.gizmodouk.RSSItem;
public class RSSLoader {
private String rssUrl;
public RSSLoader(String rssUrl) {
this.rssUrl = rssUrl;
}
public List<RSSItem> getItems() throws Exception {
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser saxParser = factory.newSAXParser();
RSSHandler handler = new RSSHandler();
saxParser.parse(rssUrl, handler);
return handler.getItems();
}
}

 

Nearly there...

So, that's all of our RSS reader classes ready to go. We just now need to tell our App to use them. This is done back in the MainActivity code.

When refreshing or loading our content, we don't want to lock the user out completely -- so what we want to do is access the external feed 'asynchronously' -- that is, we don't want the main application to wait for the response of the feed load before doing anything else. This is something that is important to think about in your app development if, as in this case, your app will be using an internet-based resource.

The great news is, yet again, the Android SDK makes this straightforward -- it has a built in AsyncTask class that we can extend to add our custom functionality. All our custom code will do is take the feed URL, try and access it and then pass it over to the RSSLoader to do the rest. If it can't access the feed we use a 'Try/Catch' exception handler to log an error message -- these Try/Catch blocks are a way of defensively handling exceptions rather than letting your code bomb out, and means you are able to allow your app to gracefully break in a way that you can then work around.

The other thing we need to do is bind the RSSListener to each item in our list. For this we use the onPostExecute event -- that is, once everything is loaded and processed we just tell the our app that each RSSItem has an event to listen for on it.

Here's how our MainActivity should end up looking:

package com.gizmodouk;
import java.util.List;
import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import com.gizmodouk.RSSItem;
import com.gizmodouk.RSSListener;
import com.gizmodouk.RSSLoader;
public class MainActivity extends Activity {
private MainActivity local;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
local = this;
RSSTask task = new RSSTask();
task.execute("http://www.gizmodo.co.uk/feed/");
}
private class RSSTask extends AsyncTask<String, Void, List<RSSItem> > {
@Override
protected List<RSSItem> doInBackground(String... urls) {
try {
RSSLoader rssLoader = new RSSLoader(urls[0]);
return rssLoader.getItems();
} catch (Exception e) {
Log.e("RSSLoader", e.getMessage());
}
return null;
}
@Override
protected void onPostExecute(List<RSSItem> result) {
ListView gizItems = (ListView) findViewById(R.id.rssListView);
ArrayAdapter<RSSItem> adapter = new ArrayAdapter<RSSItem>(local,android.R.layout.simple_list_item_1, result);
gizItems.setAdapter(adapter);
gizItems.setOnItemClickListener(new RSSListener(result, local));
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}

 

Next time...

When we next meet, we'll be rounding off our series by running our app for the first time, returning to that pesky 'Settings' page and looking at how we save and use our own RSS URL, and then thinking about how we handle any bugs we might spot in our testing. We'll also have all of the code so far available to download in a handy package and will let you all loose on creating your own, much better, versions of the app.

Colin Polonowski is a developer at Giz UK’s parent company Future Publishing, working across a number of their sites. He also runs The Digital Fix in his spare time. Check in on the 18th of April for his next App Millionaire guide.

To get on-the-spot news, app tips and the full lowdown on Samsung’s latest mobile announcements check out Samsung’s Your Mobile Life over here.