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.
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.


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.
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.