FileDocCategorySizeDatePackage
RssReader.javaAPI DocAndroid 1.5 API20528Wed May 06 22:41:08 BST 2009com.example.android.rssreader

RssReader

public class RssReader extends android.app.ListActivity
The RssReader example demonstrates forking off a thread to download rss data in the background and post the results to a ListView in the UI. It also shows how to display custom data in a ListView with a ArrayAdapter subclass.
  • We own a ListView
  • The ListView uses our custom RSSListAdapter which
    • The adapter feeds data to the ListView
    • Override of getView() in the adapter provides the display view used for selected list items
  • Override of onListItemClick() creates an intent to open the url for that RssItem in the browser.
  • Download = fork off a worker thread
  • The worker thread opens a network connection for the rss data
  • Uses XmlPullParser to extract the rss item data
  • Uses mHandler.post() to send new RssItems to the UI
  • Supports onSaveInstanceState()/onRestoreInstanceState() to save list/selection state on app pause, so can resume seamlessly

Fields Summary
private RSSListAdapter
mAdapter
Custom list adapter that fits our rss data into the list.
private android.widget.EditText
mUrlText
Url edit text field.
private android.widget.TextView
mStatusText
Status text field.
private android.os.Handler
mHandler
Handler used to post runnables to the UI thread.
private RSSWorker
mWorker
Currently running background network thread.
public static final int
SNIPPET_LENGTH
public static final String
STRINGS_KEY
public static final String
SELECTION_KEY
public static final String
URL_KEY
public static final String
STATUS_KEY
Constructors Summary
Methods Summary
private voiddoRSS(java.lang.CharSequence rssUrl)
Given an rss url string, starts the rss-download-thread going.

param
rssUrl

        RSSWorker worker = new RSSWorker(rssUrl);
        setCurrentWorker(worker);

        resetUI();
        mStatusText.setText("Downloading\u2026");

        worker.start();
    
public synchronized booleanisCurrentWorker(com.example.android.rssreader.RssReader$RSSWorker worker)
Is the given worker the currently active one.

param
worker
return

        return (mWorker == worker);
    
protected voidonCreate(android.os.Bundle savedInstanceState)
Called when the activity starts up. Do activity initialization here, not in a constructor.

see
Activity#onCreate


                          
    
        
        super.onCreate(savedInstanceState);
        
        setContentView(R.layout.rss_layout);
        // The above layout contains a list id "android:list"
        // which ListActivity adopts as its list -- we can
        // access it with getListView().

        // Install our custom RSSListAdapter.
        List<RssItem> items = new ArrayList<RssItem>();
        mAdapter = new RSSListAdapter(this, items);
        getListView().setAdapter(mAdapter);

        // Get pointers to the UI elements in the rss_layout
        mUrlText = (EditText)findViewById(R.id.urltext);
        mStatusText = (TextView)findViewById(R.id.statustext);
        
        Button download = (Button)findViewById(R.id.download);
        download.setOnClickListener(new OnClickListener() {
            public void onClick(View v) {
                doRSS(mUrlText.getText());
            }
        });

        // Need one of these to post things back to the UI thread.
        mHandler = new Handler();
        
        // NOTE: this could use the icicle as done in
        // onRestoreInstanceState().
    
public booleanonCreateOptionsMenu(android.view.Menu menu)
Populates the menu.

        super.onCreateOptionsMenu(menu);

        menu.add(0, 0, 0, "Slashdot")
            .setOnMenuItemClickListener(new RSSMenu("http://rss.slashdot.org/Slashdot/slashdot"));

        menu.add(0, 0, 0, "Google News")
            .setOnMenuItemClickListener(new RSSMenu("http://news.google.com/?output=rss"));
        
        menu.add(0, 0, 0, "News.com")
            .setOnMenuItemClickListener(new RSSMenu("http://news.com.com/2547-1_3-0-20.xml"));

        menu.add(0, 0, 0, "Bad Url")
            .setOnMenuItemClickListener(new RSSMenu("http://nifty.stanford.edu:8080"));

        menu.add(0, 0, 0, "Reset")
                .setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
            public boolean onMenuItemClick(MenuItem item) {
                resetUI();
                return true;
            }
        });

        return true;
    
protected voidonListItemClick(android.widget.ListView l, android.view.View v, int position, long id)
Called when user clicks an item in the list. Starts an activity to open the url for that item.

        RssItem item = mAdapter.getItem(position);

        // Creates and starts an intent to open the item.link url.
        Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(item.getLink().toString()));
        startActivity(intent);
    
protected voidonRestoreInstanceState(android.os.Bundle state)
Called to "thaw" re-animate the app from a previous onSaveInstanceState().

see
android.app.Activity#onRestoreInstanceState

        super.onRestoreInstanceState(state);

        // Note: null is a legal value for onRestoreInstanceState.
        if (state == null) return;

        // Restore items from the big list of CharSequence objects
        List<CharSequence> strings = (ArrayList<CharSequence>)state.getSerializable(STRINGS_KEY);
        List<RssItem> items = new ArrayList<RssItem>();
        for (int i = 0; i < strings.size(); i += 3) {
            items.add(new RssItem(strings.get(i), strings.get(i + 1), strings.get(i + 2)));
        }

        // Reset the list view to show this data.
        mAdapter = new RSSListAdapter(this, items);
        getListView().setAdapter(mAdapter);

        // Restore selection
        if (state.containsKey(SELECTION_KEY)) {
            getListView().requestFocus(View.FOCUS_FORWARD);
            // todo: is above right? needed it to work
            getListView().setSelection(state.getInt(SELECTION_KEY));
        }
        
        // Restore url
        mUrlText.setText(state.getCharSequence(URL_KEY));
        
        // Restore status
        mStatusText.setText(state.getCharSequence(STATUS_KEY));
    
protected voidonSaveInstanceState(android.os.Bundle outState)
Called for us to save out our current state before we are paused, such a for example if the user switches to another app and memory gets scarce. The given outState is a Bundle to which we can save objects, such as Strings, Integers or lists of Strings. In this case, we save out the list of currently downloaded rss data, (so we don't have to re-do all the networking just because the user goes back and forth between aps) which item is currently selected, and the data for the text views. In onRestoreInstanceState() we look at the map to reconstruct the run-state of the application, so returning to the activity looks seamlessly correct. TODO: the Activity javadoc should give more detail about what sort of data can go in the outState map.

see
android.app.Activity#onSaveInstanceState

        super.onSaveInstanceState(outState);

        // Make a List of all the RssItem data for saving
        // NOTE: there may be a way to save the RSSItems directly,
        // rather than their string data.
        int count = mAdapter.getCount();

        // Save out the items as a flat list of CharSequence objects --
        // title0, link0, descr0, title1, link1, ...
        ArrayList<CharSequence> strings = new ArrayList<CharSequence>();
        for (int i = 0; i < count; i++) {
            RssItem item = mAdapter.getItem(i);
            strings.add(item.getTitle());
            strings.add(item.getLink());
            strings.add(item.getDescription());
        }
        outState.putSerializable(STRINGS_KEY, strings);

        // Save current selection index (if focussed)
        if (getListView().hasFocus()) {
            outState.putInt(SELECTION_KEY, Integer.valueOf(getListView().getSelectedItemPosition()));
        }

        // Save url
        outState.putString(URL_KEY, mUrlText.getText().toString());
        
        // Save status
        outState.putCharSequence(STATUS_KEY, mStatusText.getText());
    
voidparseRSS(java.io.InputStream in, com.example.android.rssreader.RssReader$RSSListAdapter adapter)
Does rudimentary RSS parsing on the given stream and posts rss items to the UI as they are found. Uses Android's XmlPullParser facility. This is not a production quality RSS parser -- it just does a basic job of it.

param
in stream to read
param
adapter adapter for ui events

        // TODO: switch to sax

        XmlPullParser xpp = Xml.newPullParser();
        xpp.setInput(in, null);  // null = parser figures out encoding

        int eventType;
        String title = "";
        String link = "";
        String description = "";
        eventType = xpp.getEventType();
        while (eventType != XmlPullParser.END_DOCUMENT) {
            if (eventType == XmlPullParser.START_TAG) {
                String tag = xpp.getName();
                if (tag.equals("item")) {
                    title = link = description = "";
                } else if (tag.equals("title")) {
                    xpp.next(); // Skip to next element -- assume text is directly inside the tag
                    title = xpp.getText();
                } else if (tag.equals("link")) {
                    xpp.next();
                    link = xpp.getText();
                } else if (tag.equals("description")) {
                    xpp.next();
                    description = xpp.getText();
                }
            } else if (eventType == XmlPullParser.END_TAG) {
                // We have a comlete item -- post it back to the UI
                // using the mHandler (necessary because we are not
                // running on the UI thread).
                String tag = xpp.getName();
                if (tag.equals("item")) {
                    RssItem item = new RssItem(title, link, description);
                    mHandler.post(new ItemAdder(item));
                }
            }
            eventType = xpp.next();
        }
    
public java.lang.StringremoveTags(java.lang.String str)
Simple code to strip out s -- primitive way to sortof display HTML as plain text.

        str = str.replaceAll("<.*?>", " ");
        str = str.replaceAll("\\s+", " ");
        return str;
    
public voidresetUI()
Resets the output UI -- list and status text empty.

        // Reset the list to be empty.
        List<RssItem> items = new ArrayList<RssItem>();
        mAdapter = new RSSListAdapter(this, items);
        getListView().setAdapter(mAdapter);

        mStatusText.setText("");
        mUrlText.requestFocus();
    
public synchronized voidsetCurrentWorker(com.example.android.rssreader.RssReader$RSSWorker worker)
Sets the currently active running worker. Interrupts any earlier worker, so we only have one at a time.

param
worker the new worker

        if (mWorker != null) mWorker.interrupt();
        mWorker = worker;