Android RSS Reader Application using SQLite Part 1

In this tutorial i am going to explain building simple rss reader application. If you are novice about RSS, please go through this RSS 2.0 Specification and get an idea about RSS.

Download Code

The Application Overview

The is very simple rss reader application. The complete application will have four views
1. A view will list all the websites (stored in SQLite table) in a listview
2. View to add new website to application.
3. On selecting single website from listview another listview will display list of articles for that particular website
4. Once the sinlge article is selected that web page will display on webview.
SQLite database is used to store websites entered by used.

So lets start by creating new project in Eclipse. Create a project and fill all the details.

1. Modifying the AndroidManifest.xml file

At first i am editing the AndroidManifest.xml file and adding required code like INTERNET Persmissions, new Activity class files. So open your manifest file and do following changes.

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.androidhive.rssreader"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk android:minSdkVersion="8" />

    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name" >
        <activity
            android:configChanges="keyboardHidden|orientation"
            android:name=".AndroidRSSReaderApplicationActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        
        <!-- Add New Site Activity -->
        <activity android:name=".AddNewSiteActivity" />
        
        <!-- List rss items Activity -->
        <activity android:name=".ListRSSItemsActivity" 
            android:configChanges="keyboardHidden|orientation"/>
        
        <!-- Display webpage Activity -->
        <activity android:name=".DisPlayWebPageActivity"
            android:theme="@android:style/Theme.NoTitleBar"
            android:configChanges="keyboardHidden|orientation" />
    </application>
    
    <!-- Internet Permissions -->
    <uses-permission android:name="android.permission.INTERNET" />

</manifest>

2. Downloading Jsoup html parser library

In order to get rss links from html source code i used Jsoup java library. Download and add the jar file to your project Java Build Path library.

You can download the jar file by going to http://jsoup.org/download

Once downloaded Right Click on project -> properties -> Select Java Build Path (on left side) -> Select third tab Libraries (on right side) -> Add External Jar -> Browse to downloaded jsoup .jar file.

android adding jsoup reference .jar
android adding jsoup reference .jar

3. Preparing required class files

If you look at below rss xml structor, you can see it has basic website information like title, link, description and an array of item nodes where each item node indicates single article. Again each item node has child nodes like title, link, pubDate and description. After parsing rss xml i prefer to maintain xml nodes into object. So create following class file in your project

<channel>
	<title></title>
	<link></link>
	<description></description>
	<item>
		<title></title>
		<link></link>
		<pubDate></pubDate>
		<description></description>
	</item>
	<item>
	.
	.
	.</item>
</channel>

Create a class file called RSSFeed.java and type the following code. This class file used to create an object for rss feed which handles website basic information like title, description, link, rss link, language and an array of rss items.

RSSFeed.java

package com.androidhive.rssreader;

import java.util.List;

/**
 * This class handle rss xml 
 * */
public class RSSFeed {
	// xml nodes
	String _title;
	String _description;
	String _link;
	String _rss_link;
	String _language;
	List<RSSItem> _items;

	// constructor
	public RSSFeed(String title, String description, String link,
			String rss_link, String language) {
		this._title = title;
		this._description = description;
		this._link = link;
		this._rss_link = rss_link;
		this._language = language;
	}

	/**
	 * All set methods
	 * */
	public void setItems(List<RSSItem> items) {
		this._items = items;
	}

	/**
	 * All get methods
	 * */
	public List<RSSItem> getItems() {
		return this._items;
	}

	public String getTitle() {
		return this._title;
	}

	public String getDescription() {
		return this._description;
	}

	public String getLink() {
		return this._link;
	}

	public String getRSSLink() {
		return this._rss_link;
	}

	public String getLanguage() {
		return this._language;
	}
}

Also create another class file called RSSItem.java which handles individual article information like title, link, pubDate and description.

RSSItem.java

package com.androidhive.rssreader;

/**
 * This class handle RSS Item <item> node in rss xml
 * */
public class RSSItem {
	
	// All <item> node name
	String _title;
	String _link;
	String _description;
	String _pubdate;
	String _guid;
	
	// constructor
	public RSSItem(){
		
	}
	
	// constructor with parameters
	public RSSItem(String title, String link, String description, String pubdate, String guid){
		this._title = title;
		this._link = link;
		this._description = description;
		this._pubdate = pubdate;
		this._guid = guid;
	}
	
	/**
	 * All SET methods
	 * */
	public void setTitle(String title){
		this._title = title;
	}
	
	public void setLink(String link){
		this._link = link;
	}
	
	public void setDescription(String description){
		this._description = description;
	}
	
	public void setPubdate(String pubDate){
		this._pubdate = pubDate;
	}
	
	
	public void setGuid(String guid){
		this._guid = guid;
	}
	
	/**
	 * All GET methods
	 * */
	public String getTitle(){
		return this._title;
	}
	
	public String getLink(){
		return this._link;
	}
	
	public String getDescription(){
		return this._description;
	}
	
	public String getPubdate(){
		return this._pubdate;
	}
	
	public String getGuid(){
		return this._guid;
	}
}

Now create a class file called Website.java which is used to handle SQLite database opoerations. This class will create an object for SQLite table single row.

Website.java

package com.androidhive.rssreader;


/**
 * This class file used while inserting data or retrieving data from 
 * SQLite database
 * **/
public class WebSite {
	Integer _id;
	String _title;
	String _link;
	String _rss_link;
	String _description;
	
	// constructor
	public WebSite(){
		
	}

	// constructor with parameters
	public WebSite(String title, String link, String rss_link, String description){
		this._title = title;
		this._link = link;
		this._rss_link = rss_link;
		this._description = description;
	}
	
	/**
	 * All set methods
	 * */
	public void setId(Integer id){
		this._id = id;
	}
	
	public void setTitle(String title){
		this._title = title;
	}
	
	public void setLink(String link){
		this._link = link;
	}
	
	public void setRSSLink(String rss_link){
		this._rss_link = rss_link;
	}
	
	public void setDescription(String description){
		this._description = description;
	}
	
	/**
	 * All get methods
	 * */
	public Integer getId(){
		return this._id;
	}
	
	public String getTitle(){
		return this._title;
	}
	
	public String getLink(){
		return this._link;
	}
	
	public String getRSSLink(){
		return this._rss_link;
	}
	
	public String getDescription(){
		return this._description;
	}
}

4. Writing RSS Parser Class

The main purpose of RSS Parser class is to parse the rss xml and return RSSFeed object. When user enters a website url this class will do following tasks

-> Will get the html source code of the website
-> Parse the html source code and will get rss url
-> After getting rss url will get rss xml and parse the xml.
-> Once rss xml parsing is done will return RSSFeed object of the rss xml.

In your project folder create a class file called RSSParser.java and paste the following code.

RSSParser.java

package com.androidhive.rssreader;

import java.io.IOException;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import org.jsoup.Jsoup;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import android.util.Log;

public class RSSParser {

	// RSS XML document CHANNEL tag
	private static String TAG_CHANNEL = "channel";
	private static String TAG_TITLE = "title";
	private static String TAG_LINK = "link";
	private static String TAG_DESRIPTION = "description";
	private static String TAG_LANGUAGE = "language";
	private static String TAG_ITEM = "item";
	private static String TAG_PUB_DATE = "pubDate";
	private static String TAG_GUID = "guid";

	// constructor
	public RSSParser() {

	}

	/***
	 * Get RSS feed from url
	 * 
	 * @param url - is url of the website 
	 * @return RSSFeed class object
	 */
	public RSSFeed getRSSFeed(String url) {
		RSSFeed rssFeed = null;
		String rss_feed_xml = null;
		
		// getting rss link from html source code
		String rss_url = this.getRSSLinkFromURL(url);
		
		// check if rss_link is found or not
		if (rss_url != null) {
			// RSS url found
			// get RSS XML from rss ulr
			rss_feed_xml = this.getXmlFromUrl(rss_url);
			// check if RSS XML fetched or not
			if (rss_feed_xml != null) {
				// successfully fetched rss xml
				// parse the xml
				try {
					Document doc = this.getDomElement(rss_feed_xml);
					NodeList nodeList = doc.getElementsByTagName(TAG_CHANNEL);
					Element e = (Element) nodeList.item(0);
					
					// RSS nodes
					String title = this.getValue(e, TAG_TITLE);
					String link = this.getValue(e, TAG_LINK);
					String description = this.getValue(e, TAG_DESRIPTION);
					String language = this.getValue(e, TAG_LANGUAGE);

					// Creating new RSS Feed
					rssFeed = new RSSFeed(title, description, link, rss_url, language);
				} catch (Exception e) {
					// Check log for errors
					e.printStackTrace();
				}

			} else {
				// failed to fetch rss xml
			}
		} else {
			// no RSS url found
		}
		return rssFeed;
	}

	/**
	 * Getting RSS feed items <item>
	 * 
	 * @param - rss link url of the website
	 * @return - List of RSSItem class objects
	 * */
	public List<RSSItem> getRSSFeedItems(String rss_url){
		List<RSSItem> itemsList = new ArrayList<RSSItem>();
		String rss_feed_xml;
		
		// get RSS XML from rss url
		rss_feed_xml = this.getXmlFromUrl(rss_url);
		
		// check if RSS XML fetched or not
		if(rss_feed_xml != null){
			// successfully fetched rss xml
			// parse the xml
			try{
				Document doc = this.getDomElement(rss_feed_xml);
				NodeList nodeList = doc.getElementsByTagName(TAG_CHANNEL);
				Element e = (Element) nodeList.item(0);
				
				// Getting items array
				NodeList items = e.getElementsByTagName(TAG_ITEM);
				
				// looping through each item
				for(int i = 0; i < items.getLength(); i++){
					Element e1 = (Element) items.item(i);
					
					String title = this.getValue(e1, TAG_TITLE);
					String link = this.getValue(e1, TAG_LINK);
					String description = this.getValue(e1, TAG_DESRIPTION);
					String pubdate = this.getValue(e1, TAG_PUB_DATE);
					String guid = this.getValue(e1, TAG_GUID);
					
					RSSItem rssItem = new RSSItem(title, link, description, pubdate, guid);
					
					// adding item to list
					itemsList.add(rssItem);
				}
			}catch(Exception e){
				// Check log for errors
				e.printStackTrace();
			}
		}
		
		// return item list
		return itemsList;
	}

	/**
	 * Getting RSS feed link from HTML source code
	 * 
	 * @param ulr is url of the website
	 * @returns url of rss link of website
	 * */
	public String getRSSLinkFromURL(String url) {
		// RSS url
		String rss_url = null;

		try {
			// Using JSoup library to parse the html source code
			org.jsoup.nodes.Document doc = Jsoup.connect(url).get();
			// finding rss links which are having link[type=application/rss+xml]
			org.jsoup.select.Elements links = doc
					.select("link[type=application/rss+xml]");
			
			Log.d("No of RSS links found", " " + links.size());
			
			// check if urls found or not
			if (links.size() > 0) {
				rss_url = links.get(0).attr("href").toString();
			} else {
				// finding rss links which are having link[type=application/rss+xml]
				org.jsoup.select.Elements links1 = doc
						.select("link[type=application/atom+xml]");
				if(links1.size() > 0){
					rss_url = links1.get(0).attr("href").toString();	
				}
			}
			
		} catch (IOException e) {
			e.printStackTrace();
		}

		// returing RSS url
		return rss_url;
	}

	/**
	 * Method to get xml content from url HTTP Get request
	 * */
	public String getXmlFromUrl(String url) {
		String xml = null;

		try {
			// request method is GET
			DefaultHttpClient httpClient = new DefaultHttpClient();
			HttpGet httpGet = new HttpGet(url);

			HttpResponse httpResponse = httpClient.execute(httpGet);
			HttpEntity httpEntity = httpResponse.getEntity();
			xml = EntityUtils.toString(httpEntity);

		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		} catch (ClientProtocolException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		// return XML
		return xml;
	}

	/**
	 * Getting XML DOM element
	 * 
	 * @param XML string
	 * */
	public Document getDomElement(String xml) {
		Document doc = null;
		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
		try {

			DocumentBuilder db = dbf.newDocumentBuilder();

			InputSource is = new InputSource();
			is.setCharacterStream(new StringReader(xml));
			doc = (Document) db.parse(is);

		} catch (ParserConfigurationException e) {
			Log.e("Error: ", e.getMessage());
			return null;
		} catch (SAXException e) {
			Log.e("Error: ", e.getMessage());
			return null;
		} catch (IOException e) {
			Log.e("Error: ", e.getMessage());
			return null;
		}

		return doc;
	}

	/**
	 * Getting node value
	 * 
	 * @param elem element
	 */
	public final String getElementValue(Node elem) {
		Node child;
		if (elem != null) {
			if (elem.hasChildNodes()) {
				for (child = elem.getFirstChild(); child != null; child = child
						.getNextSibling()) {
					if (child.getNodeType() == Node.TEXT_NODE || ( child.getNodeType() == Node.CDATA_SECTION_NODE)) {
						return child.getNodeValue();
					}
				}
			}
		}
		return "";
	}

	/**
	 * Getting node value
	 * 
	 * @param Element node
	 * @param key  string
	 * */
	public String getValue(Element item, String str) {
		NodeList n = item.getElementsByTagName(str);
		return this.getElementValue(n.item(0));
	}
}

5. Writing SQLite Database Handler Class

In this application i am storing user entered websites in sqlite database. The basic website information like title, link, rss_link, description is stored in database. I created a table called websites with the following columns.

android rss parser sqlite

Create class called RSSDatabaseHandler.java in your project and type the following code. This class has following functions

public void addSite(WebSite site) {} // add a new row in websites table

public List<WebSite> getAllSites() {} //returns all the rows as Website class objects

public int updateSite(WebSite site) {} // update existing row

public WebSite getSite(int id) {} // returns single row

public void deleteSite(WebSite site) {} // deletes a single row

public boolean isSiteExists(String rss_link) {} //check if a website is already existed

RSSDatabaseHandler.java

package com.androidhive.rssreader;

import java.util.ArrayList;
import java.util.List;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;

public class RSSDatabaseHandler extends SQLiteOpenHelper {

	// Database Version
	private static final int DATABASE_VERSION = 1;

	// Database Name
	private static final String DATABASE_NAME = "rssReader";

	// Contacts table name
	private static final String TABLE_RSS = "websites";

	// Contacts Table Columns names
	private static final String KEY_ID = "id";
	private static final String KEY_TITLE = "title";
	private static final String KEY_LINK = "link";
	private static final String KEY_RSS_LINK = "rss_link";
	private static final String KEY_DESCRIPTION = "description";

	public RSSDatabaseHandler(Context context) {
		super(context, DATABASE_NAME, null, DATABASE_VERSION);
	}

	// Creating Tables
	@Override
	public void onCreate(SQLiteDatabase db) {
		String CREATE_RSS_TABLE = "CREATE TABLE " + TABLE_RSS + "(" + KEY_ID
				+ " INTEGER PRIMARY KEY," + KEY_TITLE + " TEXT," + KEY_LINK
				+ " TEXT," + KEY_RSS_LINK + " TEXT," + KEY_DESCRIPTION
				+ " TEXT" + ")";
		db.execSQL(CREATE_RSS_TABLE);
	}

	// Upgrading database
	@Override
	public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
		// Drop older table if existed
		db.execSQL("DROP TABLE IF EXISTS " + TABLE_RSS);

		// Create tables again
		onCreate(db);
	}

	/**
	 * Adding a new website in websites table Function will check if a site
	 * already existed in database. If existed will update the old one else
	 * creates a new row
	 * */
	public void addSite(WebSite site) {
		SQLiteDatabase db = this.getWritableDatabase();

		ContentValues values = new ContentValues();
		values.put(KEY_TITLE, site.getTitle()); // site title
		values.put(KEY_LINK, site.getLink()); // site url
		values.put(KEY_RSS_LINK, site.getRSSLink()); // rss link url
		values.put(KEY_DESCRIPTION, site.getDescription()); // site description

		// Check if row already existed in database
		if (!isSiteExists(db, site.getRSSLink())) {
			// site not existed, create a new row
			db.insert(TABLE_RSS, null, values);
			db.close();
		} else {
			// site already existed update the row
			updateSite(site);
			db.close();
		}
	}

	/**
	 * Reading all rows from database
	 * */
	public List<WebSite> getAllSites() {
		List<WebSite> siteList = new ArrayList<WebSite>();
		// Select All Query
		String selectQuery = "SELECT  * FROM " + TABLE_RSS
				+ " ORDER BY id DESC";

		SQLiteDatabase db = this.getReadableDatabase();
		Cursor cursor = db.rawQuery(selectQuery, null);

		// looping through all rows and adding to list
		if (cursor.moveToFirst()) {
			do {
				WebSite site = new WebSite();
				site.setId(Integer.parseInt(cursor.getString(0)));
				site.setTitle(cursor.getString(1));
				site.setLink(cursor.getString(2));
				site.setRSSLink(cursor.getString(3));
				site.setDescription(cursor.getString(4));
				// Adding contact to list
				siteList.add(site);
			} while (cursor.moveToNext());
		}
		cursor.close();
		db.close();

		// return contact list
		return siteList;
	}

	/**
	 * Updating a single row row will be identified by rss link
	 * */
	public int updateSite(WebSite site) {
		SQLiteDatabase db = this.getWritableDatabase();

		ContentValues values = new ContentValues();
		values.put(KEY_TITLE, site.getTitle());
		values.put(KEY_LINK, site.getLink());
		values.put(KEY_RSS_LINK, site.getRSSLink());
		values.put(KEY_DESCRIPTION, site.getDescription());

		// updating row return
		int update = db.update(TABLE_RSS, values, KEY_RSS_LINK + " = ?",
				new String[] { String.valueOf(site.getRSSLink()) });
		db.close();
		return update;

	}

	/**
	 * Reading a row (website) row is identified by row id
	 * */
	public WebSite getSite(int id) {
		SQLiteDatabase db = this.getReadableDatabase();

		Cursor cursor = db.query(TABLE_RSS, new String[] { KEY_ID, KEY_TITLE,
				KEY_LINK, KEY_RSS_LINK, KEY_DESCRIPTION }, KEY_ID + "=?",
				new String[] { String.valueOf(id) }, null, null, null, null);
		if (cursor != null)
			cursor.moveToFirst();

		WebSite site = new WebSite(cursor.getString(1), cursor.getString(2),
				cursor.getString(3), cursor.getString(4));

		site.setId(Integer.parseInt(cursor.getString(0)));
		site.setTitle(cursor.getString(1));
		site.setLink(cursor.getString(2));
		site.setRSSLink(cursor.getString(3));
		site.setDescription(cursor.getString(4));
		cursor.close();
		db.close();
		return site;
	}

	/**
	 * Deleting single row
	 * */
	public void deleteSite(WebSite site) {
		SQLiteDatabase db = this.getWritableDatabase();
		db.delete(TABLE_RSS, KEY_ID + " = ?",
				new String[] { String.valueOf(site.getId())});
		db.close();
	}

	/**
	 * Checking whether a site is already existed check is done by matching rss
	 * link
	 * */
	public boolean isSiteExists(SQLiteDatabase db, String rss_link) {

		Cursor cursor = db.rawQuery("SELECT 1 FROM " + TABLE_RSS
				+ " WHERE rss_link = '" + rss_link + "'", new String[] {});
		boolean exists = (cursor.getCount() > 0);
		return exists;
	}

}

In this part we completed building required classes for our application. In Android RSS Reader Application using SQLite Part 2 we can start with coding the application layouts and functioning.

This image is for thumbnail purpose
android rss reader application
Ravi is hardcore Android programmer and Android programming has been his passion since he compiled his first hello-world program. Solving real problems of Android developers through tutorials has always been interesting part for him.