Android RSS/ATOM feeds parsing with ROME


Wanna parse RSS feeds using your Android pimp-mobile….well, here’s how!

ROME is my all time fav in terms of Java RSS syndication feed parsing. It has some nifty features such as HTTP conditional GETs, ETags and GZip compression. It also covers a wide range of formats from RSS 0.90 – RSS 2.0, and Atom 0.3 & 1.0.

NOTES:

  1. Modify your AndroidManifest.xml to allow for internet browsing.
  2. Download the appropriate JAR files.
  3. There are differences in versions, and requires the jdom library. The version that worked for me are included in my download link provided:

  • rome-0.9.jar
  • jdom-1.0.jar

Download: AndroidRss.zip

File: AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest
	xmlns:android="http://schemas.android.com/apk/res/android"
	package="com.pfizer.android"
	android:versionCode="1"
	android:versionName="1.0">
	<application
		android:icon="@drawable/icon"
		android:label="@string/app_name">
		<activity
			android:name=".AndroidRss"
			android:label="@string/app_name">
			<intent-filter>
				<action
					android:name="android.intent.action.MAIN" />
				<category
					android:name="android.intent.category.LAUNCHER" />
			</intent-filter>
		</activity>
	</application>
	<uses-permission
		android:name="android.permission.INTERNET"></uses-permission>
</manifest> 

File: main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
	xmlns:android="http://schemas.android.com/apk/res/android"
	android:orientation="vertical"
	android:layout_width="fill_parent"
	android:layout_height="fill_parent">

	<TableLayout
		android:id="@+id/table"
		android:layout_width="fill_parent"
		android:layout_height="wrap_content"
		android:stretchColumns="0">
		<TableRow
			android:id="@+id/top_add_entry_row"
			android:layout_height="wrap_content"
			android:layout_width="fill_parent">

			<EditText
				android:id="@+id/rssURL"
				android:hint="Enter RSS URL"
				android:singleLine="true"
				android:maxLines="1"
				android:maxWidth="220dp"
				android:layout_width="wrap_content"
				android:layout_height="wrap_content">
			</EditText>
			<Button
				android:id="@+id/goButton"
				android:text="Go"
				android:layout_width="fill_parent"
				android:layout_height="wrap_content">
			</Button>
		</TableRow>
	</TableLayout>

	<!--  Mid Panel -->
	<ListView
		android:id="@+id/ListView"
		android:layout_weight="1"
		android:layout_width="fill_parent"
		android:layout_height="wrap_content">
	</ListView>

	<Button
		android:id="@+id/clearButton"
		android:text="Clear"
		android:layout_width="fill_parent"
		android:layout_height="wrap_content">
	</Button>
</LinearLayout>

File: AndroidRss.java

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.Toast;
import android.widget.AdapterView.OnItemClickListener;

import com.sun.syndication.feed.synd.SyndEntry;
import com.sun.syndication.feed.synd.SyndFeed;
import com.sun.syndication.io.FeedException;
import com.sun.syndication.io.SyndFeedInput;
import com.sun.syndication.io.XmlReader;

public class AndroidRss extends Activity
	{
		/** Called when the activity is first created. */

		private int selectedItemIndex = 0;

		private final ArrayList list = new ArrayList();
		private EditText text;
		private ListView listView;
		private Button goButton;
		private Button clearButton;
		private ArrayAdapter adapter = null;

		@Override
		public void onCreate(Bundle savedInstanceState)
			{
				super.onCreate(savedInstanceState);
				setContentView(R.layout.main);

				text = (EditText) this.findViewById(R.id.rssURL);
				goButton = (Button) this.findViewById(R.id.goButton);
				goButton.setOnClickListener(new OnClickListener()
					{
						@Override
						public void onClick(View v)
							{
								String rss = text.getText().toString().trim();
								getRSS(rss);
							}
					});

				clearButton = (Button) this.findViewById(R.id.clearButton);
				clearButton.setOnClickListener(new OnClickListener()
					{
						@Override
						public void onClick(View v)
							{
								adapter.clear();
								adapter.notifyDataSetChanged();
							}
					});

				listView = (ListView) this.findViewById(R.id.ListView);
				listView.setOnItemClickListener(new OnItemClickListener()
					{
						@Override
						public void onItemClick(AdapterView parent, View view, int position, long duration)
							{
								selectedItemIndex = position;
								Toast.makeText(getApplicationContext(), "Selected " + adapter.getItem(position) + " @ " + position, Toast.LENGTH_SHORT).show();
							}
					});

				adapter = new ArrayAdapter(this, R.layout.dataview, R.id.ListItemView);
				listView.setAdapter(adapter);

			}

		private void getRSS(String rss)
			{

				URL feedUrl;
				try
					{
						Log.d("DEBUG", "Entered:" + rss);
						feedUrl = new URL(rss);

						SyndFeedInput input = new SyndFeedInput();
						SyndFeed feed = input.build(new XmlReader(feedUrl));
						List entries = feed.getEntries();
						Toast.makeText(this, "#Feeds retrieved: " + entries.size(), Toast.LENGTH_SHORT);

						Iterator iterator = entries.listIterator();
						while (iterator.hasNext())
							{
								SyndEntry ent = (SyndEntry) iterator.next();
								String title = ent.getTitle();
								adapter.add(title);
							}
						adapter.notifyDataSetChanged();

					}
				catch (MalformedURLException e)
					{
						e.printStackTrace();
					}
				catch (IllegalArgumentException e)
					{
						e.printStackTrace();
					}
				catch (FeedException e)
					{
						e.printStackTrace();
					}
				catch (IOException e)
					{
						e.printStackTrace();
					}
			}

		private void clearTextFields()
			{
				Log.d("DEBUG", "clearTextFields()");
				this.text.setText("");
			}
	}

19 thoughts on “Android RSS/ATOM feeds parsing with ROME

  1. Keep the posting up. I never thought I would find an individual that had close to the same grasp of worldly knowldege , however, you are that beacon of hope in humanity!I hope you continue to bring your knowldge and enlightenment to the world and never let your expression be squelched! Live strong!

  2. Hi Arthur,
    With the ROME api , the feed URL, URI, images, categories…etc can be easilly retrieved.
    For more details see:

    https://rome.dev.java.net/apidocs/1_0/com/sun/syndication/feed/synd/SyndFeed.html

    Simple:
    You will notice the listview implements a OnItemClickListener with an onItem click.
    I have made a Toast.makeText().show() where you can modify and include the URL.

    A better way of handling this situation, would be to use the ViewHolder pattern:
    1. Create a separate layout for each list item.
    This is already done using dataview.xml. Modify it to include a TextView for URL , and ImageView for images.

    2. Create a Java class to hold necessary data you want to display of the SyndEntry eg. url, date published, image etc…Basically, you are selecting only those fields from the SyndEntry you need.

    3. Now the important part: Create a custom version of the ArrayAdapter
    eg. RssArrayAdapter rssAdapter= new RssArrayAdapter

    4. Override the getView( … ) and populate the row element…

    That’s it!

    The basic elements you will find by looking at some of my other posts above eg. creating a customizing an ArrayAdapter and overriding its getView() method…etc.

    Keep well

    • Hello all!

      I`m trying to set that OnItemClickListener to open the web browser but luck. I what to ask if you could show us an working example.

      Thanks for your time.

  3. Thank you so much for the tutorial. This has been incredibly helpful.

    Now the issue I’m having… I’m trying to integrate what I learned from your tutorial and read an RSS feed. My adapter seems to be setup incorrectly. I need to read and display the feed on opening the screen versus as the result of a button click. When GetRSS is run, I can see the adapter gets populated correctly. However, when I attempt to set the ListView adapter to the populated adapter, my program force quits. Any ideas? My updated code looks like this:


    adapter = new ArrayAdapter(this, R.layout.dataview, R.id.ListItemView);
    getRSS("http://www.example.com/wp/?feed=gigpress&artist=1");
    listView = (ListView) this.findViewById(R.id.ListView);
    listView.setAdapter(adapter);

    getRSS is unchanged from your example.

    • Hi Rob,

      “I need to read and display the feed on opening the screen versus as the result of a button click.”

      OK. i modified so it loads immediately , ie. No need to press any button.
      (I removed the goButton reference)
      I ran your URL: “http://www.example.com/wp/?feed=gigpress&artist=1″
      nothing blank screen
      ….then I checked the LogCat – FileNotFound error….

      I replaces your URL with CBC sports RSS feed link and viola – screenshot!

      package com.pfizer.android;

      import java.io.IOException;
      import java.net.MalformedURLException;
      import java.net.URL;
      import java.util.ArrayList;
      import java.util.Iterator;
      import java.util.List;

      import android.app.Activity;
      import android.os.Bundle;
      import android.util.Log;
      import android.view.View;
      import android.view.View.OnClickListener;
      import android.widget.AdapterView;
      import android.widget.ArrayAdapter;
      import android.widget.Button;
      import android.widget.EditText;
      import android.widget.ListView;
      import android.widget.Toast;
      import android.widget.AdapterView.OnItemClickListener;

      import com.sun.syndication.feed.synd.SyndEntry;
      import com.sun.syndication.feed.synd.SyndFeed;
      import com.sun.syndication.io.FeedException;
      import com.sun.syndication.io.SyndFeedInput;
      import com.sun.syndication.io.XmlReader;

      public class AndroidRss extends Activity
      {
      /** Called when the activity is first created. */

      private int selectedItemIndex = 0;

      private final ArrayList list = new ArrayList();
      private EditText text;
      private ListView listView;
      private Button goButton;
      private Button clearButton;
      private ArrayAdapter adapter = null;

      @Override
      public void onCreate(Bundle savedInstanceState)
      {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.main);

      text = (EditText) this.findViewById(R.id.rssURL);

      listView = (ListView) this.findViewById(R.id.ListView);
      listView.setOnItemClickListener(new OnItemClickListener()
      {
      public void onItemClick(AdapterView parent, View view, int position, long duration)
      {
      selectedItemIndex = position;
      Toast.makeText(getApplicationContext(), “Selected ” + adapter.getItem(position) + ” @ ” + position, Toast.LENGTH_SHORT).show();
      }
      });

      adapter = new ArrayAdapter(this, R.layout.dataview, R.id.ListItemView);
      listView.setAdapter(adapter);
      getRSS(“http://rss.cbc.ca/lineup/topstories.xml”);

      clearButton = (Button) this.findViewById(R.id.clearButton);
      clearButton.setOnClickListener(new OnClickListener()
      {
      public void onClick(View v)
      {
      adapter.clear();
      adapter.notifyDataSetChanged();
      }
      });

      }

      private void getRSS(String rss)
      {

      URL feedUrl;
      try
      {
      Log.d(“DEBUG”, “Entered:” + rss);
      feedUrl = new URL(rss);

      SyndFeedInput input = new SyndFeedInput();
      SyndFeed feed = input.build(new XmlReader(feedUrl));
      List entries = feed.getEntries();
      Toast.makeText(this, “#Feeds retrieved: ” + entries.size(), Toast.LENGTH_SHORT);

      Iterator iterator = entries.listIterator();
      while (iterator.hasNext())
      {
      SyndEntry ent = (SyndEntry) iterator.next();
      String title = ent.getTitle();
      adapter.add(title);
      }
      adapter.notifyDataSetChanged();

      }
      catch (MalformedURLException e)
      {
      e.printStackTrace();
      }
      catch (IllegalArgumentException e)
      {
      e.printStackTrace();
      }
      catch (FeedException e)
      {
      e.printStackTrace();
      }
      catch (IOException e)
      {
      e.printStackTrace();
      }
      }

      private void clearTextFields()
      {
      Log.d(“DEBUG”, “clearTextFields()”);
      this.text.setText(“”);
      }
      }

  4. Hi,

    I have a problem with the import com.sun, it’s like Eclipse doesn’t know this import.
    Do you have any idea from where this problem could come ?

    Thanks for the reply,

    Nico

    • Hi Nico,

      A good starting point is to download the complete source code for the tutorial: AndroidRss.zip (link is provided on the page), to look at the project structure.
      In your Android Eclipse project, do the following:

      1. Create a folder called libs/ in the root directory of your project.
      2. Copy the JAR files from the ROME package into it ….(as in the AndroidRSS.zip )
      3. Click on your project in the side pane, now RIGHT-CLICK and select Configure Build Path (or Project -> Properties -> Java Build Path)
      4. Click Add External JARS and browse to the libs/ folder in your project
      5. Add the JARS in the libs/ folder
      6. On the same window, Click the tab Export, now select the JARS you have just added.
      7. Click on Project -> Clean…
      8. Your done!

      If the problem remains…what is happening is that you may have warnings, set to errors in your compiler set-up.
      You can get around this by doing:
      1. Window -> Preferences -> Java -> Compiler -> Deprecated and Restricted API..
      select Warnings and Click Signal use of Deprecated API…and Signal overriding or implemented…

      that should make the errors which prevent compilation be interpreted as allowed warnings.

      But its most likely just doing step about the Build Path and adding the ROME JARS to the build path.

      Keep well,
      -w

  5. Hi Wagied,

    I tried what you said and my errors are :

    UNEXPECTED TOP-LEVEL EXCEPTION:
    java.util.zip.ZipException: error in opening zip file
    [2011-03-10 19:58:55 - RSSCooker]: Dx at java.util.zip.ZipFile.open(Native Method)
    [2011-03-10 19:58:55 - RSSCooker]: Dx at java.util.zip.ZipFile.(ZipFile.java:127)
    [2011-03-10 19:58:55 - RSSCooker]: Dx at java.util.zip.ZipFile.(ZipFile.java:144)
    [2011-03-10 19:58:55 - RSSCooker]: Dx at com.android.dx.cf.direct.ClassPathOpener.processArchive(ClassPathOpener.java:205)
    [2011-03-10 19:58:55 - RSSCooker]: Dx at com.android.dx.cf.direct.ClassPathOpener.processOne(ClassPathOpener.java:130)
    [2011-03-10 19:58:55 - RSSCooker]: Dx at com.android.dx.cf.direct.ClassPathOpener.process(ClassPathOpener.java:108)
    [2011-03-10 19:58:55 - RSSCooker]: Dx at com.android.dx.command.dexer.Main.processOne(Main.java:313)
    [2011-03-10 19:58:55 - RSSCooker]: Dx at com.android.dx.command.dexer.Main.processAllFiles(Main.java:233)
    [2011-03-10 19:58:55 - RSSCooker]: Dx at com.android.dx.command.dexer.Main.run(Main.java:185)
    [2011-03-10 19:58:55 - RSSCooker]: Dx at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    [2011-03-10 19:58:55 - RSSCooker]: Dx at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
    [2011-03-10 19:58:55 - RSSCooker]: Dx at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
    [2011-03-10 19:58:55 - RSSCooker]: Dx at java.lang.reflect.Method.invoke(Method.java:597)
    [2011-03-10 19:58:55 - RSSCooker]: Dx at com.android.ide.eclipse.adt.internal.build.DexWrapper.run(Unknown Source)
    [2011-03-10 19:58:55 - RSSCooker]: Dx at com.android.ide.eclipse.adt.internal.build.BuildHelper.executeDx(Unknown Source)
    [2011-03-10 19:58:55 - RSSCooker]: Dx at com.android.ide.eclipse.adt.internal.build.builders.PostCompilerBuilder.build(Unknown Source)
    [2011-03-10 19:58:55 - RSSCooker]: Dx at org.eclipse.core.internal.events.BuildManager$2.run(BuildManager.java:627)
    [2011-03-10 19:58:55 - RSSCooker]: Dx at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:42)
    [2011-03-10 19:58:55 - RSSCooker]: Dx at org.eclipse.core.internal.events.BuildManager.basicBuild(BuildManager.java:170)
    [2011-03-10 19:58:55 - RSSCooker]: Dx at org.eclipse.core.internal.events.BuildManager.basicBuild(BuildManager.java:201)
    [2011-03-10 19:58:55 - RSSCooker]: Dx at org.eclipse.core.internal.events.BuildManager$1.run(BuildManager.java:253)
    [2011-03-10 19:58:55 - RSSCooker]: Dx at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:42)
    [2011-03-10 19:58:55 - RSSCooker]: Dx at org.eclipse.core.internal.events.BuildManager.basicBuild(BuildManager.java:256)
    [2011-03-10 19:58:55 - RSSCooker]: Dx at org.eclipse.core.internal.events.BuildManager.basicBuildLoop(BuildManager.java:309)
    [2011-03-10 19:58:55 - RSSCooker]: Dx at org.eclipse.core.internal.events.BuildManager.build(BuildManager.java:341)
    [2011-03-10 19:58:55 - RSSCooker]: Dx at org.eclipse.core.internal.events.AutoBuildJob.doBuild(AutoBuildJob.java:140)
    [2011-03-10 19:58:55 - RSSCooker]: Dx at org.eclipse.core.internal.events.AutoBuildJob.run(AutoBuildJob.java:238)
    [2011-03-10 19:58:55 - RSSCooker]: Dx at org.eclipse.core.internal.jobs.Worker.run(Worker.java:55)
    [2011-03-10 19:58:55 - RSSCooker]: Dx2 errors; aborting
    [2011-03-10 19:58:55 - RSSCooker] Conversion to Dalvik format failed with error 1

    This will help maybe

    Nico

    • ROME was basically developed by SUN’s (now Oracle) for RSS feed parsersing. Its been widely used…just google it.
      I was’nt aware that there is a project that you mentioned…since its very fast on its own- that’s the way I use it.

      Regards,
      -w

  6. Thank you so much for the example.
    Everything is working fine, except one thing:
    SyndFeed feed = input.build(new XmlReader(feedUrl));
    Executing this line takes up to 45 seconds. Any ideas?
    Thank you, Andrew

  7. Pingback: Open webpage from xml list | Find Answer

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