In this tutorial, we’ll be discussing Android RSS Reader and develop an RSS Feed Reader app in android studio.
The Android RSS Feed Reader app would showcase Movie Reviews from two popular websites.
Android RSS Reader
RSS stands for Really Simple Syndication. Websites generally provide RSS Feeds for their content. It’s in XML format and is typically used to access the latest contents fed in the website.
An RSS Feed xml document looks like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<channel> <title></title> <link></link> <description></description> <item> <title></title> <link></link> <pubDate></pubDate> <description></description> </item> <item> . . . . </item> </channel> |
channel is the root element used to hold the below-mentioned elements.
title is the title of the website.
link contains the url of the website
item element consists of contents of the website. Each item describes the title, url, publication date and description(body) of the respective content.
In our following Android Project, we’ll use DocumentBuilderFactory instance to parse the xml document.
We’ll be using HttpClient and jsoup jar library to fetch the XML feed contents of the websites in an AsyncTask.
We’ll be developing an application that fetches the movies reviews from two of the websites: Rediff.com and Cinemablend.com using their RSS Feed and display the items in the form of a ListView. Clicking any of the ListView would open its content inside a WebView. Let’s get started.
Android RSS Feed Reader Project Structure
The project consists of three activities.
The First contains two Buttons that’ll act as links to the RSS Feeds of the two websites.
The second would display the latest RSS Feeds containing the Movie Reviews in the form of a ListView.
The third would open up the link from any of the List Row selected in the previous activity and load the URL in a WebView.
We’ve imported the jsoup.jar in our libs folder.
We need to set useLibrary 'org.apache.http.legacy'
in our android{} block in the build.gradle as shown below:
This is done to allow HtttpClient and HttpUrlConnection classes to be imported in our activities.
Android RSS Reader Code
The code for the activity_main.xml layout class is given below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="https://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:orientation="vertical"> <Button android:id="@+id/btnRediff" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="REDIFF.COM RSS FEED" /> <Button android:id="@+id/btnCinemaBlend" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="CINEMA BLEND RSS FEED" /> </LinearLayout> |
The code for the MainActivity.java class is given below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
package com.journaldev.androidrssfeedtutorial; import android.content.Intent; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.Button; import java.util.ArrayList; public class MainActivity extends AppCompatActivity implements View.OnClickListener { ArrayList<String> rssLinks = new ArrayList<>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button btnRediff = findViewById(R.id.btnRediff); Button btnCinemaBlend = findViewById(R.id.btnCinemaBlend); btnRediff.setOnClickListener(this); btnCinemaBlend.setOnClickListener(this); rssLinks.add("https://www.rediff.com/rss/moviesreviewsrss.xml"); rssLinks.add("https://www.cinemablend.com/rss_review.php"); } @Override public void onClick(View view) { switch (view.getId()) { case R.id.btnRediff: startActivity(new Intent(MainActivity.this, RSSFeedActivity.class).putExtra("rssLink", rssLinks.get(0))); break; case R.id.btnCinemaBlend: startActivity(new Intent(MainActivity.this, RSSFeedActivity.class).putExtra("rssLink", rssLinks.get(1))); break; } } } |
In this, we pass over the RSS Url links via Intents to the RSSFeedActivity.java class that we’ll see next.
The code for the activity_rss_feed.xml layout is given below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/relativeLayout" android:orientation="vertical"> <ListView android:id="@android:id/list" android:layout_width="match_parent" android:layout_height="wrap_content" android:dividerHeight="1dp" /> </RelativeLayout> |
The code for the RSSFeedActivity.java class which extends a ListActivity is given below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
package com.journaldev.androidrssfeedtutorial; import android.app.ListActivity; import android.content.Intent; import android.os.AsyncTask; import android.os.Bundle; import android.support.v7.widget.Toolbar; import android.view.View; import android.widget.AdapterView; import android.widget.ListAdapter; import android.widget.ListView; import android.widget.ProgressBar; import android.widget.RelativeLayout; import android.widget.SimpleAdapter; import android.widget.TextView; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Locale; public class RSSFeedActivity extends ListActivity { private ProgressBar pDialog; ArrayList<HashMap<String, String>> rssItemList = new ArrayList<>(); RSSParser rssParser = new RSSParser(); Toolbar toolbar; List<RSSItem> rssItems = new ArrayList<>(); private static String TAG_TITLE = "title"; private static String TAG_LINK = "link"; private static String TAG_PUB_DATE = "pubDate"; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_rss_feed); String rss_link = getIntent().getStringExtra("rssLink"); new LoadRSSFeedItems().execute(rss_link); ListView lv = getListView(); lv.setOnItemClickListener(new AdapterView.OnItemClickListener() { public void onItemClick(AdapterView<?> parent, View view, int position, long id) { Intent in = new Intent(getApplicationContext(), BrowserActivity.class); String page_url = ((TextView) view.findViewById(R.id.page_url)).getText().toString().trim(); in.putExtra("url", page_url); startActivity(in); } }); } public class LoadRSSFeedItems extends AsyncTask<String, String, String> { @Override protected void onPreExecute() { super.onPreExecute(); pDialog = new ProgressBar(RSSFeedActivity.this, null, android.R.attr.progressBarStyleLarge); RelativeLayout relativeLayout = findViewById(R.id.relativeLayout); RelativeLayout.LayoutParams lp = new RelativeLayout.LayoutParams( RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT ); lp.addRule(RelativeLayout.CENTER_IN_PARENT); pDialog.setLayoutParams(lp); pDialog.setVisibility(View.VISIBLE); relativeLayout.addView(pDialog); } @Override protected String doInBackground(String... args) { // rss link url String rss_url = args[0]; // list of rss items rssItems = rssParser.getRSSFeedItems(rss_url); // looping through each item for (RSSItem item : rssItems) { // creating new HashMap if (item.link.toString().equals("")) break; HashMap<String, String> map = new HashMap<String, String>(); // adding each child node to HashMap key => value String givenDateString = item.pubdate.trim(); SimpleDateFormat sdf = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z"); try { Date mDate = sdf.parse(givenDateString); SimpleDateFormat sdf2 = new SimpleDateFormat("EEEE, dd MMMM yyyy - hh:mm a", Locale.US); item.pubdate = sdf2.format(mDate); } catch (ParseException e) { e.printStackTrace(); } map.put(TAG_TITLE, item.title); map.put(TAG_LINK, item.link); map.put(TAG_PUB_DATE, item.pubdate); // If you want parse the date // adding HashList to ArrayList rssItemList.add(map); } // updating UI from Background Thread runOnUiThread(new Runnable() { public void run() { ListAdapter adapter = new SimpleAdapter( RSSFeedActivity.this, rssItemList, R.layout.rss_item_list_row, new String[]{TAG_LINK, TAG_TITLE, TAG_PUB_DATE}, new int[]{R.id.page_url, R.id.title, R.id.pub_date}); // updating listview setListAdapter(adapter); } }); return null; } protected void onPostExecute(String args) { pDialog.setVisibility(View.GONE); } } } |
In this, we instantiate an instance of RSSParser class.
Inside the LoadRSSFeedItems AsyncTask method we call getRSSFeedItems()
to get the RSSItems from the URL which are then stored inside an ArrayList of rssItems. The RSSItems ArrayList is then eventually loaded into the ListView rows.
The code for the rss_item_list_row.xml layout is given below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="8dip"> <TextView android:id="@+id/page_url" android:layout_width="fill_parent" android:layout_height="wrap_content" android:visibility="gone" /> <TextView android:id="@+id/title" android:layout_width="fill_parent" android:layout_height="wrap_content" android:paddingBottom="1dip" android:textColor="#212121" android:textSize="18sp" android:textStyle="bold" /> <TextView android:id="@+id/pub_date" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@id/title" android:paddingBottom="3dip" android:textColor="#9b737775" android:textSize="14sp" /> </RelativeLayout> |
On ListView row click listener we pass the current item’s url to the next Activity in the key url
. Before we look at the next activity let’s see the RSSItem.java and RSSParser.java classes.
DO NOT forget to add the Activities in the Manifest file.
RSSItem.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
package com.journaldev.androidrssfeedtutorial; / public class RSSItem { public String title; public String link; public String description; public String pubdate; public String guid; 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; } } |
RSSParser.java
This is responsible for parsing the xml feed document.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 |
package com.journaldev.androidrssfeedtutorial; import android.util.Log; 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.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 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; 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_ITEM = "item"; private static String TAG_PUB_DATE = "pubDate"; private static String TAG_GUID = "guid"; // constructor public RSSParser() { } public List<RSSItem> getRSSFeedItems(String rss_url) { List<RSSItem> itemsList = new ArrayList<RSSItem>(); String rss_feed_xml; rss_feed_xml = this.getXmlFromUrl(rss_url); if (rss_feed_xml != null) { try { Document doc = this.getDomElement(rss_feed_xml); NodeList nodeList = doc.getElementsByTagName(TAG_CHANNEL); Element e = (Element) nodeList.item(0); NodeList items = e.getElementsByTagName(TAG_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; } public String getXmlFromUrl(String url) { String xml = null; try { 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; } 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 = 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; } 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 ""; } public String getValue(Element item, String str) { NodeList n = item.getElementsByTagName(str); return this.getElementValue(n.item(0)); } } |
getRSSFeedItems()
is what returns the RSSItem List to the RSSFeedActivity.java.
The code for the activity_browser.xml layout is given below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:id="@+id/relativeLayout" android:layout_height="match_parent"> <android.support.v4.widget.NestedScrollView android:layout_width="match_parent" android:layout_height="match_parent"> <WebView android:id="@+id/webView" android:layout_width="match_parent" android:layout_height="match_parent" /> </android.support.v4.widget.NestedScrollView> </RelativeLayout> |
The code for the BrowserActivity.java class is given below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
package com.journaldev.androidrssfeedtutorial; import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.text.TextUtils; import android.webkit.WebChromeClient; import android.webkit.WebResourceError; import android.webkit.WebResourceRequest; import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.Toast; public class BrowserActivity extends AppCompatActivity { WebView webView; String url; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_browser); Intent in = getIntent(); url = in.getStringExtra("url"); if (TextUtils.isEmpty(url)) { Toast.makeText(getApplicationContext(), "URL not found", Toast.LENGTH_SHORT).show(); finish(); } webView = findViewById(R.id.webView); initWebView(); webView.loadUrl(url); } private void initWebView() { webView.setWebChromeClient(new MyWebChromeClient(this)); webView.clearCache(true); webView.getSettings().setJavaScriptEnabled(true); webView.setHorizontalScrollBarEnabled(false); webView.setWebViewClient(new WebViewClient() { @Override public void onPageStarted(WebView view, String url, Bitmap favicon) { super.onPageStarted(view, url, favicon); } @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { webView.loadUrl(url); return true; } @Override public void onPageFinished(WebView view, String url) { super.onPageFinished(view, url); } @Override public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) { super.onReceivedError(view, request, error); invalidateOptionsMenu(); } }); webView.clearCache(true); webView.clearHistory(); webView.getSettings().setJavaScriptEnabled(true); webView.setHorizontalScrollBarEnabled(false); } private class MyWebChromeClient extends WebChromeClient { Context context; public MyWebChromeClient(Context context) { super(); this.context = context; } } } |
The url passed from the RSSFeedActivity.java Activity is loaded here.
DO NOT forget to add the Internet Permissions in your AndroidManifest.xml file.
The output of the above application in action is given below.
This brings an end to this tutorial. You can download the Android RSSFeedTutorial from the link below.