Friday, July 6, 2012

RSS feed reader run in AsyncTask

It's a long long exercise. Refer to the previous exercise "A simple RSS reader IV, start browser to open the selected feed". It work on old version of Android. But fail with android:minSdkVersion="10" or higher, caused by android.os.NetworkOnMainThreadException!

To fix it, simple move the code into AsyncTask.

RSS feeder reader run in AsyncTask


Modify AndroidRssReader.java
package com.exercise.AndroidRssReader;

import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;

import android.app.ListActivity;
import android.content.Intent;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;

public class AndroidRssReader extends ListActivity {
 
 private RSSFeed myRssFeed = null;
 
 /** Called when the activity is first created. */
 @Override
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.main);
  
  new MyTask().execute();
   
 }
 private class MyTask extends AsyncTask<Void, Void, Void>{

  @Override
  protected Void doInBackground(Void... arg0) {
   try {
    URL rssUrl = new URL("http://www.gov.hk/en/about/rss/govhkrss.data.xml");
    SAXParserFactory mySAXParserFactory = SAXParserFactory.newInstance();
    SAXParser mySAXParser = mySAXParserFactory.newSAXParser();
    XMLReader myXMLReader = mySAXParser.getXMLReader();
    RSSHandler myRSSHandler = new RSSHandler();
    myXMLReader.setContentHandler(myRSSHandler);
    InputSource myInputSource = new InputSource(rssUrl.openStream());
    myXMLReader.parse(myInputSource);
    
    myRssFeed = myRSSHandler.getFeed(); 
   } catch (MalformedURLException e) {
    e.printStackTrace(); 
   } catch (ParserConfigurationException e) {
    e.printStackTrace(); 
   } catch (SAXException e) {
    e.printStackTrace();
   } catch (IOException e) {
    e.printStackTrace(); 
   }
   
   return null;
  }

  @Override
  protected void onPostExecute(Void result) {
   if (myRssFeed!=null)
   {
    TextView feedTitle = (TextView)findViewById(R.id.feedtitle);
    TextView feedDescribtion = (TextView)findViewById(R.id.feeddescribtion);
    TextView feedPubdate = (TextView)findViewById(R.id.feedpubdate);
    TextView feedLink = (TextView)findViewById(R.id.feedlink);
    feedTitle.setText(myRssFeed.getTitle());
    feedDescribtion.setText(myRssFeed.getDescription());
    feedPubdate.setText(myRssFeed.getPubdate());
    feedLink.setText(myRssFeed.getLink());
    
    ArrayAdapter<RSSItem> adapter =
      new ArrayAdapter<RSSItem>(getApplicationContext(),
        android.R.layout.simple_list_item_1,myRssFeed.getList());
    setListAdapter(adapter); 
    
   }else{
    
    TextView textEmpty = (TextView)findViewById(android.R.id.empty);
    textEmpty.setText("No Feed Found!");
   }
   
   super.onPostExecute(result);
  }
  
 }
 
 @Override
 protected void onListItemClick(ListView l, View v, int position, long id) {
  
  Uri feedUri = Uri.parse(myRssFeed.getItem(position).getLink());
  Intent myIntent = new Intent(Intent.ACTION_VIEW, feedUri);
  startActivity(myIntent);
 }
}


In the original layout, the ListView without list (before AsyncTask finished) will show "No Data". So we also modify it to "waiting", it will be updated after AsyncTask finished if no RSS feed found.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    
    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="@string/hello" />
    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:id="@+id/feedtitle" />
    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:id="@+id/feeddescribtion" />
    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:id="@+id/feedpubdate" />
    <TextView
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:autoLink="web"
        android:id="@+id/feedlink" />
    
    <ListView
        android:id="@android:id/list"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content" />
    <TextView
        android:id="@android:id/empty"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="waiting" />
</LinearLayout>


Other files refer to the previous exercise "A simple RSS reader IV, start browser to open the selected feed", or the downloaded file.

Download the files.

4 comments:

Ben Jones said...

haha just as I managed to fix it myself, if only I had waited. great tutorial

Anonymous said...

Hi. I can't find where the details layout is used. I got warning that details layout is not being used, and I can't generate apk.

BTW, I spent days trying to move my java SAX parser to android platform. I hope this example will help me.

Thanks, Jack

Tom said...

For some reason, when I download the files, the java files, manifest, etc.. are all empty.

Anonymous said...

Awesome!Simple and it Works! Thank you.