androidandroid-asynctaskandroid-parser

How do I use AsyncTask on a parser?


I have been reading this tutorial and came across the problem that it uses a very low api. I got the NetworkOnMainThreadException. I found this answer on stackoverflow which says I have to use AsyncTask.

I have tried using AsyncTask on an empty project working with another tutorial which worked fine.

My problem is that I need to change this project so that I can use it on higher apis. So the thing is AndroidSaxFeedParser is a subclass and AsyncTask is a super class and the error line is on AndroidSaxFeedParser which extends BaseFeedParser and BaseFeedParser extends FeedParser which is an interface(btw I always thought interfaces had to be implemented instead of extended?).

To be more precise the errors are on these line(indicated with --->) : AndroidSaxFeedParser.java :

try
{
--->    Xml.parse(this.getInputStream(), Xml.Encoding.UTF_8, root.getContentHandler());
}
catch (Exception e)
{
--->    throw new RuntimeException(e);
}

MessageList.java :

private void loadFeed(ParserType type)
{
    try
    {
        Log.i("AndroidNews", "ParserType=" + type.name());
        FeedParser parser = FeedParserFactory.getParser(type);
        long start = System.currentTimeMillis();


    --->    messages = parser.parse();
        long duration = System.currentTimeMillis() - start;
        Log.i("AndroidNews", "Parser duration=" + duration);
        String xml = writeXml();
        Log.i("AndroidNews", xml);
        List<String> titles = new ArrayList<String>(messages.size());
        for (Message msg : messages)
        {
            titles.add(msg.getTitle());
        }
        ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, R.layout.row, titles);
        this.setListAdapter(adapter);
    }
    catch (Throwable t)
    {
        Log.e("AndroidNews", t.getMessage(), t);
    }
}

BaseFeedParser.java :

protected InputStream getInputStream()
{
    try
    {
    --->    return feedUrl.openConnection().getInputStream();
    }
    catch (IOException e)
    {
        throw new RuntimeException(e);
    }
}

So where and how should I use the AsyncTask. (I'm only using the AndroidSaxParser so the other parsers in the tutorial can be ignored).

AndroidSaxFeedParser.java

public class AndroidSaxFeedParser extends BaseFeedParser
{

    static final String RSS = "rss";

    public AndroidSaxFeedParser(String feedUrl)
    {
        super(feedUrl);
    }

    public List<Message> parse()
    {
        final Message currentMessage = new Message();
        RootElement root = new RootElement(RSS);
        final List<Message> messages = new ArrayList<Message>();
        Element channel = root.getChild(CHANNEL);
        Element item = channel.getChild(ITEM);
        item.setEndElementListener(new EndElementListener()
        {
            public void end()
            {
                messages.add(currentMessage.copy());
            }
        });
        item.getChild(TITLE).setEndTextElementListener(new EndTextElementListener()
        {
            public void end(String body)
            {
                currentMessage.setTitle(body);
            }
        });
        item.getChild(LINK).setEndTextElementListener(new EndTextElementListener()
        {
            public void end(String body)
            {
                currentMessage.setLink(body);
            }
        });
        item.getChild(DESCRIPTION).setEndTextElementListener(new EndTextElementListener()
        {
            public void end(String body)
            {
                currentMessage.setDescription(body);
            }
        });
        item.getChild(PUB_DATE).setEndTextElementListener(new EndTextElementListener()
        {
            public void end(String body)
            {
                currentMessage.setDate(body);
            }
        });
        try
        {
            Xml.parse(this.getInputStream(), Xml.Encoding.UTF_8, root.getContentHandler());
        }
        catch (Exception e)
        {
            throw new RuntimeException(e);
        }
        return messages;
    }
}

BaseFeedParser.java

public abstract class BaseFeedParser implements FeedParser
{

    // names of the XML tags
    static final String CHANNEL = "channel";
    static final String PUB_DATE = "pubDate";
    static final String DESCRIPTION = "description";
    static final String LINK = "link";
    static final String TITLE = "title";
    static final String ITEM = "item";

    private final URL feedUrl;

    protected BaseFeedParser(String feedUrl)
    {
        try
        {
            this.feedUrl = new URL(feedUrl);
        }
        catch (MalformedURLException e)
        {
            throw new RuntimeException(e);
        }
    }

    protected InputStream getInputStream()
    {
        try
        {
            return feedUrl.openConnection().getInputStream();
        }
        catch (IOException e)
        {
            throw new RuntimeException(e);
        }
    }
}

FeedParser.java

public interface FeedParser
{
    List<Message> parse();
}

FeedParserFactory.java

public abstract class FeedParserFactory
{
    static String feedUrl = "http://example.com/feed/";

    public static FeedParser getParser()
    {
        return getParser(ParserType.ANDROID_SAX);
    }

    public static FeedParser getParser(ParserType type)
    {
        switch (type)
        {
        case SAX:
            return new SaxFeedParser(feedUrl);
        case DOM:
            return new DomFeedParser(feedUrl);
        case ANDROID_SAX:
            return new AndroidSaxFeedParser(feedUrl);
        case XML_PULL:
            return new XmlPullFeedParser(feedUrl);
        default:
            return null;
        }
    }
}

Message.java

public class Message implements Comparable<Message>
{
    static SimpleDateFormat FORMATTER = new SimpleDateFormat("EEE, dd MMM yyyy hh:mm:ss Z",  Locale.ENGLISH);
    private String title;
    private URL link;
    private String description;
    private Date date;

    public String getTitle()
    {
        return title;
    }

    public void setTitle(String title)
    {
        this.title = title.trim();
    }

    // getters and setters omitted for brevity
    public URL getLink()
    {
        return link;
    }

    public void setLink(String link)
    {
        try
        {
            this.link = new URL(link);
        }
        catch (MalformedURLException e)
        {
            throw new RuntimeException(e);
        }
    }

    public String getDescription()
    {
        return description;
    }

    public void setDescription(String description)
    {
        this.description = description.trim();
    }

    public String getDate()
    {
        return FORMATTER.format(this.date);
    }

    public void setDate(String date)
    {
        // pad the date if necessary
        while (!date.endsWith("00"))
        {
            date += "0";
        }
        try
        {
            this.date = FORMATTER.parse(date.trim());
        }
        catch (ParseException e)
        {
            throw new RuntimeException(e);
        }
    }

    public Message copy()
    {
        Message copy = new Message();
        copy.title = title;
        copy.link = link;
        copy.description = description;
        copy.date = date;
        return copy;
    }

    @Override
    public String toString()
    {
        StringBuilder sb = new StringBuilder();
        sb.append("Title: ");
        sb.append(title);
        sb.append('\n');
        sb.append("Date: ");
        sb.append(this.getDate());
        sb.append('\n');
        sb.append("Link: ");
        sb.append(link);
        sb.append('\n');
        sb.append("Description: ");
        sb.append(description);
        return sb.toString();
    }

    @Override
    public int hashCode()
    {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((date == null) ? 0 : date.hashCode());
        result = prime * result + ((description == null) ? 0 : description.hashCode());
        result = prime * result + ((link == null) ? 0 : link.hashCode());
        result = prime * result + ((title == null) ? 0 : title.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj)
    {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        Message other = (Message) obj;
        if (date == null)
        {
            if (other.date != null)
                return false;
        }
        else if (!date.equals(other.date))
            return false;
        if (description == null)
        {
            if (other.description != null)
                return false;
        }
        else if (!description.equals(other.description))
            return false;
        if (link == null)
        {
            if (other.link != null)
                return false;
        }
        else if (!link.equals(other.link))
            return false;
        if (title == null)
        {
            if (other.title != null)
                return false;
        }
        else if (!title.equals(other.title))
            return false;
        return true;
    }

    public int compareTo(Message another)
    {
        if (another == null)
            return 1;
        // sort descending, most recent first
        return another.date.compareTo(date);
    }
}

MessageList.java

public class MessageList extends ListActivity
{

    private List<Message> messages;

    @Override
    public void onCreate(Bundle icicle)
    {
        super.onCreate(icicle);
        setContentView(R.layout.main);
        loadFeed(ParserType.ANDROID_SAX);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu)
    {
        super.onCreateOptionsMenu(menu);
        menu.add(Menu.NONE, ParserType.ANDROID_SAX.ordinal(), ParserType.ANDROID_SAX.ordinal(), R.string.android_sax);
        menu.add(Menu.NONE, ParserType.SAX.ordinal(), ParserType.SAX.ordinal(), R.string.sax);
        menu.add(Menu.NONE, ParserType.DOM.ordinal(), ParserType.DOM.ordinal(), R.string.dom);
        menu.add(Menu.NONE, ParserType.XML_PULL.ordinal(), ParserType.XML_PULL.ordinal(), R.string.pull);
        return true;
    }

    @SuppressWarnings("unchecked")
    @Override
    public boolean onMenuItemSelected(int featureId, MenuItem item)
    {
        super.onMenuItemSelected(featureId, item);
        ParserType type = ParserType.values()[item.getItemId()];
        ArrayAdapter<String> adapter = (ArrayAdapter<String>) this.getListAdapter();
        if (adapter.getCount() > 0)
        {
            adapter.clear();
        }
        this.loadFeed(type);
        return true;
    }

    @Override
    protected void onListItemClick(ListView l, View v, int position, long id)
    {
        super.onListItemClick(l, v, position, id);
        Intent viewMessage = new Intent(Intent.ACTION_VIEW, Uri.parse(messages.get(position).getLink().toExternalForm()));
        this.startActivity(viewMessage);
    }

    private void loadFeed(ParserType type)
    {
        try
        {
            Log.i("AndroidNews", "ParserType=" + type.name());
            FeedParser parser = FeedParserFactory.getParser(type);
            long start = System.currentTimeMillis();
            messages = parser.parse();
            long duration = System.currentTimeMillis() - start;
            Log.i("AndroidNews", "Parser duration=" + duration);
            String xml = writeXml();
            Log.i("AndroidNews", xml);
            List<String> titles = new ArrayList<String>(messages.size());
            for (Message msg : messages)
            {
                titles.add(msg.getTitle());
            }
            ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, R.layout.row, titles);
            this.setListAdapter(adapter);
        }
        catch (Throwable t)
        {
            Log.e("AndroidNews", t.getMessage(), t);
        }
    }

    private String writeXml()
    {
        XmlSerializer serializer = Xml.newSerializer();
        StringWriter writer = new StringWriter();
        try
        {
            serializer.setOutput(writer);
            serializer.startDocument("UTF-8", true);
            serializer.startTag("", "messages");
            serializer.attribute("", "number", String.valueOf(messages.size()));
            for (Message msg : messages)
            {
                serializer.startTag("", "message");
                serializer.attribute("", "date", msg.getDate());
                serializer.startTag("", "title");
                serializer.text(msg.getTitle());
                serializer.endTag("", "title");
                serializer.startTag("", "url");
                serializer.text(msg.getLink().toExternalForm());
                serializer.endTag("", "url");
                serializer.startTag("", "body");
                serializer.text(msg.getDescription());
                serializer.endTag("", "body");
                serializer.endTag("", "message");
            }
            serializer.endTag("", "messages");
            serializer.endDocument();
            return writer.toString();
        }
        catch (Exception e)
        {
            throw new RuntimeException(e);
        }
    }
}

ParserType.java

public enum ParserType
{
    SAX, DOM, ANDROID_SAX, XML_PULL;
}

RssHandler.java

public class RssHandler extends DefaultHandler
{
    private List<Message> messages;
    private Message currentMessage;
    private StringBuilder builder;

    public List<Message> getMessages()
    {
        return this.messages;
    }

    @Override
    public void characters(char[] ch, int start, int length) throws SAXException
    {
        super.characters(ch, start, length);
        builder.append(ch, start, length);
    }

    @Override
    public void endElement(String uri, String localName, String name) throws SAXException
    {
        super.endElement(uri, localName, name);
        if (this.currentMessage != null)
        {
            if (localName.equalsIgnoreCase(TITLE))
            {
                currentMessage.setTitle(builder.toString());
            }
            else if (localName.equalsIgnoreCase(LINK))
            {
                currentMessage.setLink(builder.toString());
            }
            else if (localName.equalsIgnoreCase(DESCRIPTION))
            {
                currentMessage.setDescription(builder.toString());
            }
            else if (localName.equalsIgnoreCase(PUB_DATE))
            {
                currentMessage.setDate(builder.toString());
            }
            else if (localName.equalsIgnoreCase(ITEM))
            {
                messages.add(currentMessage);
            }
            builder.setLength(0);
        }
    }

    @Override
    public void startDocument() throws SAXException
    {
        super.startDocument();
        messages = new ArrayList<Message>();
        builder = new StringBuilder();
    }

    @Override
    public void startElement(String uri, String localName, String name, Attributes attributes) throws SAXException
    {
        super.startElement(uri, localName, name, attributes);
        if (localName.equalsIgnoreCase(ITEM))
        {
            this.currentMessage = new Message();
        }
    }
}

Solution

  • Answer

    I fixed my problem if anyone is interested you can read that question.