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.
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 |
<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.
1 |
<uses-permission android:name="android.permission.INTERNET"/> |
Now we are adding some of the dependency that we need in our project.
1 2 3 |
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
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 |
<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.
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 |
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.
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 |
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.
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 |
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.
1 2 3 4 5 6 7 8 9 10 11 12 |
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.
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 |
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
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 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 |
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
<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.
1 |
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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<?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
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
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.