In this android rss reader tutorial we will see how to make demo app that can read rss of a website or blog. So without wasting much time lets begin.
Typically the rss feed of a website or blog look something like shown below.
<channel>
<title></title>
<link></link>
<description></description>
<item>
<title></title>
<link></link>
<pubDate></pubDate>
<description></description>
</item>
<item>
<title></title>
<link></link>
<pubDate></pubDate>
<description></description>
</item>
.
.
.
.
</channel>
The data we get is in xml format.
Here channel tag is the root or the main element from where rss begins.
title shows the name of the website.
description tells the content of website.
link is the url to the website.
There are multiple item tags that contain article or post information like post title, link, publish date, etc.
First of all create a new android project to read the rss in android app.
Then we need to give internet permission in manifest to access the rss in application.
<uses-permission android:name="android.permission.INTERNET"/>
Now we are adding some of the dependency that we need in our project.
implementation 'com.android.support:design:28.0.0'
implementation 'com.android.support:recyclerview-v7:28.0.0'
implementation 'com.android.support:cardview-v7:28.0.0'
We are designing our activity_main.xml file.
activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<Button
android:id="@+id/fetchFeedButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:text="Fetch" />
<android.support.design.widget.TextInputLayout
android:id="@+id/textInputLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="10dp"
android:layout_toStartOf="@id/fetchFeedButton"
android:hint="Rss feed source">
<EditText
android:id="@+id/rssFeedEditText"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</android.support.design.widget.TextInputLayout>
<TextView
android:id="@+id/feedTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/textInputLayout"
android:text="Feed Title: " />
<TextView
android:id="@+id/feedDescription"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/feedTitle"
android:text="Feed Description: " />
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/swipeRefreshLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/feedDescription"
android:layout_marginTop="20dp">
<android.support.v7.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</android.support.v4.widget.SwipeRefreshLayout>
</RelativeLayout>
Here we are using swipe refresh layout just to refresh the rss content of a website.
Now we have to code MainActivity.java for building our functionality of application so first we have to reference xml components in java.
public class MainActivity extends AppCompatActivity {
private RecyclerView mRecyclerView;
private EditText mEditText;
private Button mFetchFeedButton;
private SwipeRefreshLayout mSwipeLayout;
private TextView mFeedTitleTextView;
private TextView mFeedDescriptionTextView;
private List<RssFeedModel> mFeedModelList;
private String mFeedTitle;
private String mFeedContent;
private String mFeedDescription;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mRecyclerView = (RecyclerView) findViewById(R.id.recyclerView);
mEditText = (EditText) findViewById(R.id.rssFeedEditText);
mFetchFeedButton = (Button) findViewById(R.id.fetchFeedButton);
mSwipeLayout = (SwipeRefreshLayout) findViewById(R.id.swipeRefreshLayout);
mFeedTitleTextView = (TextView) findViewById(R.id.feedTitle);
mFeedDescriptionTextView = (TextView) findViewById(R.id.feedDescription);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
mFetchFeedButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
new FetchFeedTask().execute((Void) null);
}
});
mSwipeLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
new FetchFeedTask().execute((Void) null);
}
});
}
We have to define a java class called FetchFeedTask which is used to get the rss feed from url.
private class FetchFeedTask extends AsyncTask<Void, Void, Boolean> {
private String urlLink;
@Override
protected void onPreExecute() {
mSwipeLayout.setRefreshing(true);
urlLink = mEditText.getText().toString();
}
@Override
protected Boolean doInBackground(Void... voids) {
if (TextUtils.isEmpty(urlLink))
return false;
try {
if(!urlLink.startsWith("http://") && !urlLink.startsWith("https://"))
urlLink = "http://" + urlLink;
URL url = new URL(urlLink);
InputStream inputStream = url.openConnection().getInputStream();
mFeedModelList = parseFeed(inputStream);
return true;
} catch (IOException e) {
e.printStackTrace();
} catch (XmlPullParserException e) {
e.printStackTrace();
}
return false;
}
@Override
protected void onPostExecute(Boolean success) {
mSwipeLayout.setRefreshing(false);
if (success) {
mFeedTitleTextView.setText("Feed Title: " + mFeedTitle);
mFeedDescriptionTextView.setText("Feed Description: " + mFeedDescription);
// Fill RecyclerView
mRecyclerView.setAdapter(new RssFeedListAdapter(mFeedModelList));
} else {
Toast.makeText(MainActivity.this,
"Enter a valid Rss feed url",
Toast.LENGTH_LONG).show();
}
}
}
Now we are defining a list and parsing xml data to get the attribute of items from the rss feed we have extracted.
public List<RssFeedModel> parseFeed(InputStream inputStream) throws XmlPullParserException,
IOException {
String title = null;
String content = null;
String description = null;
boolean isItem = false;
List<RssFeedModel> items = new ArrayList<>();
try {
XmlPullParser xmlPullParser = Xml.newPullParser();
xmlPullParser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
xmlPullParser.setInput(inputStream, null);
xmlPullParser.nextTag();
while (xmlPullParser.next() != XmlPullParser.END_DOCUMENT) {
int eventType = xmlPullParser.getEventType();
String name = xmlPullParser.getName();
if(name == null)
continue;
if(eventType == XmlPullParser.END_TAG) {
if(name.equalsIgnoreCase("item")) {
isItem = false;
}
continue;
}
if (eventType == XmlPullParser.START_TAG) {
if(name.equalsIgnoreCase("item")) {
isItem = true;
continue;
}
}
Log.d("MyXmlParser", "Parsing name ==> " + name);
String result = "";
if (xmlPullParser.next() == XmlPullParser.TEXT) {
result = xmlPullParser.getText();
xmlPullParser.nextTag();
}
if (name.equalsIgnoreCase("title")) {
title = result;
} else if (name.equalsIgnoreCase("content:encoded")) {
content = result;
} else if (name.equalsIgnoreCase("description")) {
description = result;
}
if (title != null && content != null && description != null) {
if(isItem) {
RssFeedModel item = new RssFeedModel(title, content, description);
items.add(item);
}
else {
mFeedTitle = title;
mFeedContent = content;
mFeedDescription = description;
}
title = null;
content = null;
description = null;
isItem = false;
}
}
return items;
} finally {
inputStream.close();
}
}
Defining RssModel java class which we used to hold the attribute of every post.
public class RssFeedModel {
public String title;
public String content;
public String description;
public RssFeedModel(String title, String content, String description) {
this.title = title;
this.content = content;
this.description = description;
}
}
Defining RecyclerAdapter to attach our list data to recyclerview.
public class RssFeedListAdapter
extends RecyclerView.Adapter<RssFeedListAdapter.FeedModelViewHolder> {
private List<RssFeedModel> mRssFeedModels;
public class FeedModelViewHolder extends RecyclerView.ViewHolder {
private View rssFeedView;
public FeedModelViewHolder(View v) {
super(v);
rssFeedView = v;
}
}
public RssFeedListAdapter(List<RssFeedModel> rssFeedModels) {
mRssFeedModels = rssFeedModels;
}
@Override
public FeedModelViewHolder onCreateViewHolder(ViewGroup parent, int type) {
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.recycler_layout, parent, false);
FeedModelViewHolder holder = new FeedModelViewHolder(v);
return holder;
}
@Override
public void onBindViewHolder(FeedModelViewHolder holder, int position) {
final RssFeedModel rssFeedModel = mRssFeedModels.get(position);
((TextView)holder.rssFeedView.findViewById(R.id.titleText)).setText(rssFeedModel.title);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
((TextView)holder.rssFeedView.findViewById(R.id.descriptionText)).setText(Html.fromHtml(rssFeedModel.description,Html.FROM_HTML_MODE_LEGACY));
} else {
((TextView)holder.rssFeedView.findViewById(R.id.descriptionText)).setText(Html.fromHtml(rssFeedModel.description)); //we have used Html.fromHtml() in textview to make our textView support html content
}
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent=new Intent(getApplicationContext(),Final.class);
intent.putExtra("content",rssFeedModel.content);
startActivity(intent);
}
});
}
@Override
public int getItemCount() {
return mRssFeedModels.size();
}
}
Finally the MainActivity.java file will look like.
MainActivity.java
package com.example.anmol.rssdemo;
import android.content.Intent;
import android.os.AsyncTask;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.text.Html;
import android.text.TextUtils;
import android.util.Log;
import android.util.Xml;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.WebView;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class MainActivity extends AppCompatActivity {
private RecyclerView mRecyclerView;
private EditText mEditText;
private Button mFetchFeedButton;
private SwipeRefreshLayout mSwipeLayout;
private TextView mFeedTitleTextView;
private TextView mFeedDescriptionTextView;
private List<RssFeedModel> mFeedModelList;
private String mFeedTitle;
private String mFeedContent;
private String mFeedDescription;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mRecyclerView = (RecyclerView) findViewById(R.id.recyclerView);
mEditText = (EditText) findViewById(R.id.rssFeedEditText);
mFetchFeedButton = (Button) findViewById(R.id.fetchFeedButton);
mSwipeLayout = (SwipeRefreshLayout) findViewById(R.id.swipeRefreshLayout);
mFeedTitleTextView = (TextView) findViewById(R.id.feedTitle);
mFeedDescriptionTextView = (TextView) findViewById(R.id.feedDescription);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
mFetchFeedButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
new FetchFeedTask().execute((Void) null);
}
});
mSwipeLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
new FetchFeedTask().execute((Void) null);
}
});
}
private class FetchFeedTask extends AsyncTask<Void, Void, Boolean> {
private String urlLink;
@Override
protected void onPreExecute() {
mSwipeLayout.setRefreshing(true);
urlLink = mEditText.getText().toString();
}
@Override
protected Boolean doInBackground(Void... voids) {
if (TextUtils.isEmpty(urlLink))
return false;
try {
if(!urlLink.startsWith("http://") && !urlLink.startsWith("https://"))
urlLink = "http://" + urlLink;
URL url = new URL(urlLink);
InputStream inputStream = url.openConnection().getInputStream();
mFeedModelList = parseFeed(inputStream);
return true;
} catch (IOException e) {
e.printStackTrace();
} catch (XmlPullParserException e) {
e.printStackTrace();
}
return false;
}
@Override
protected void onPostExecute(Boolean success) {
mSwipeLayout.setRefreshing(false);
if (success) {
mFeedTitleTextView.setText("Feed Title: " + mFeedTitle);
mFeedDescriptionTextView.setText("Feed Description: " + mFeedDescription);
// Fill RecyclerView
mRecyclerView.setAdapter(new RssFeedListAdapter(mFeedModelList));
} else {
Toast.makeText(MainActivity.this,
"Enter a valid Rss feed url",
Toast.LENGTH_LONG).show();
}
}
}
public List<RssFeedModel> parseFeed(InputStream inputStream) throws XmlPullParserException,
IOException {
String title = null;
String content = null;
String description = null;
boolean isItem = false;
List<RssFeedModel> items = new ArrayList<>();
try {
XmlPullParser xmlPullParser = Xml.newPullParser();
xmlPullParser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, false);
xmlPullParser.setInput(inputStream, null);
xmlPullParser.nextTag();
while (xmlPullParser.next() != XmlPullParser.END_DOCUMENT) {
int eventType = xmlPullParser.getEventType();
String name = xmlPullParser.getName();
if(name == null)
continue;
if(eventType == XmlPullParser.END_TAG) {
if(name.equalsIgnoreCase("item")) {
isItem = false;
}
continue;
}
if (eventType == XmlPullParser.START_TAG) {
if(name.equalsIgnoreCase("item")) {
isItem = true;
continue;
}
}
Log.d("MyXmlParser", "Parsing name ==> " + name);
String result = "";
if (xmlPullParser.next() == XmlPullParser.TEXT) {
result = xmlPullParser.getText();
xmlPullParser.nextTag();
}
if (name.equalsIgnoreCase("title")) {
title = result;
} else if (name.equalsIgnoreCase("content:encoded")) {
content = result;
} else if (name.equalsIgnoreCase("description")) {
description = result;
}
if (title != null && content != null && description != null) {
if(isItem) {
RssFeedModel item = new RssFeedModel(title, content, description);
items.add(item);
}
else {
mFeedTitle = title;
mFeedContent = content;
mFeedDescription = description;
}
title = null;
content = null;
description = null;
isItem = false;
}
}
return items;
} finally {
inputStream.close();
}
}
public class RssFeedModel {
public String title;
public String content;
public String description;
public RssFeedModel(String title, String content, String description) {
this.title = title;
this.content = content;
this.description = description;
}
}
public class RssFeedListAdapter
extends RecyclerView.Adapter<RssFeedListAdapter.FeedModelViewHolder> {
private List<RssFeedModel> mRssFeedModels;
public class FeedModelViewHolder extends RecyclerView.ViewHolder {
private View rssFeedView;
public FeedModelViewHolder(View v) {
super(v);
rssFeedView = v;
}
}
public RssFeedListAdapter(List<RssFeedModel> rssFeedModels) {
mRssFeedModels = rssFeedModels;
}
@Override
public FeedModelViewHolder onCreateViewHolder(ViewGroup parent, int type) {
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.recycler_layout, parent, false);
FeedModelViewHolder holder = new FeedModelViewHolder(v);
return holder;
}
@Override
public void onBindViewHolder(FeedModelViewHolder holder, int position) {
final RssFeedModel rssFeedModel = mRssFeedModels.get(position);
((TextView)holder.rssFeedView.findViewById(R.id.titleText)).setText(rssFeedModel.title);
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
((TextView)holder.rssFeedView.findViewById(R.id.descriptionText)).setText(Html.fromHtml(rssFeedModel.description,Html.FROM_HTML_MODE_LEGACY));
} else {
((TextView)holder.rssFeedView.findViewById(R.id.descriptionText)).setText(Html.fromHtml(rssFeedModel.description));
}
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent=new Intent(getApplicationContext(),Final.class);
intent.putExtra("content",rssFeedModel.content);
startActivity(intent);
}
});
}
@Override
public int getItemCount() {
return mRssFeedModels.size();
}
}
}
We have to define the layout used in every recyclerview post.
recycler_layout.xml
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/titleText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textStyle="bold" />
<TextView
android:id="@+id/descriptionText"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/colorAccent" />
</LinearLayout>
Atlast we have to use our adapter in our onPostExecute method to distribute our list in recyclerAdapter.
mRecyclerView.setAdapter(new RssFeedListAdapter(mFeedModelList));
We have defined another activity which is called when we click on anycard of recyclerView which opens full content of a post in webView.
activity_final.xml file
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".Final">
<WebView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/web"/>
</android.support.constraint.ConstraintLayout>
And we have to define its java class too.
Final.java
package com.example.anmol.rssdemo;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.webkit.WebView;
import android.webkit.WebViewClient;
public class Final extends AppCompatActivity {
WebView webview;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_final);
webview=(WebView)findViewById(R.id.web);
webview.getSettings().setJavaScriptEnabled(true);
webview.loadData(getIntent().getStringExtra("content"),"text/html","UTF-8");
webview.setWebViewClient(new WebViewClient());
}
}
That’s all, now you can run your app in your physical device or in your emulator and see your rss feed in list format.
Comment below if you have any queries related to above android rss reader example.