My app was reading RSS to an ArrayList
called rssItems
and what I was trying to do is to call the setDataSetChanged
method of the adapter whenever a new item is added to the ArrayList
. I also used AsyncTask to read the RSS from a different thread.
To do this, I passed the AsyncTask
into the RssParseHandler
as a parameter, so the handler can call the publishProgress
method of the AsyncTask
object whenever it finishes reading an RSS item. Since publishProgress
is a protected method, I wrote a public wrapper method called publicPublishProgress
so the handler can call it outside the AsyncTask
object. Below is are some of my code:
public class MainActivity extends Activity {
private MainActivity local;
private List<RssItem> rssItems;
RssItemAdapter<RssItem> adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
this.rssItems = new ArrayList<RssItem>();
this.adapter = new RssItemAdapter<RssItem>(local,
R.layout.rss_row_view, rssItems);
// Get references to the Fragments
FragmentManager fm = getFragmentManager();
// find the fragment
newsFragment newsFragment = (newsFragment) fm
.findFragmentById(R.id.newsFragment);
newsFragment.setListItems(rssItems);
newsFragment.setListAdapter(adapter);
GetRSSDataTask task = new GetRSSDataTask(this.rssItems, this.adapter, "blah.jpg");
task.execute("http://somesite.net/category/news/feed/");
Log.d("RssReader", Thread.currentThread().getName());
}
And this is the GetRSSDataTask extended from AsyncTask:
public class GetRSSDataTask extends AsyncTask<String, Void, Void> {
private String icon;
private RssReader rssReader;
private RssItemAdapter<RssItem> adapter;
private List<RssItem> rssItems;
public GetRSSDataTask(List<RssItem> rssItems, RssItemAdapter<RssItem> adapter, String icon) {
super();
this.icon = icon;
this.adapter = adapter;
this.rssItems = rssItems;
}
@Override
protected void onPreExecute() {
}
@Override
protected Void doInBackground(String... urls) {
// Create a list adapter
this.rssReader = new RssReader(urls[0], this.icon,
this.rssItems, this);
// Debug the task thread name
Log.d("RssReader", Thread.currentThread().getName());
try {
// Parse RSS, get items
rssReader.getItems();
} catch (Exception e) {
Log.e("RssReader", e.getMessage());
}
return null;
}
// I created this method for the rss handler to access the publishProgress method.
public void publicPublishProgress(){
publishProgress();
}
@Override
protected void onProgressUpdate(Void... values) {
// update the UI
this.adapter.notifyDataSetChanged();
}
@Override
protected void onPostExecute(Void result) {
}
}
This is the RssReader class:
public class RssReader {
private String rssUrl;
private String icon;
private List<RssItem> rssItems;
private GetRSSDataTask task;
public RssReader(String rssUrl, String icon, List<RssItem> rssItems, GetRSSDataTask task) {
this.rssUrl = rssUrl;
this.icon = icon;
this.task = task;
}
public List<RssItem> getItems() throws Exception {
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser saxParser = factory.newSAXParser();
RssParseHandler handler = new RssParseHandler(this.icon, this.rssItems, this.task);
saxParser.parse(rssUrl, handler);
return handler.getItems();
}
}
And the RssParseHandler class:
public class RssParseHandler extends DefaultHandler {
private List<RssItem> rssItems;
// We have a local reference to an object which is constructed while parser is working on an item tag
// Used to reference item while parsing
private RssItem currentItem;
// We have two indicators which are used to differentiate whether a tag title or link is being processed by the parser
// Parsing title indicator
private boolean parsingTitle;
// Parsing link indicator
private boolean parsingLink;
private String icon;
private GetRSSDataTask task;
public RssParseHandler(String icon, List<RssItem> rssItems, GetRSSDataTask task) {
this.rssItems = rssItems;
this.task = task;
this.icon = icon;
}
// We have an access method which returns a list of items that are read from the RSS feed. This method will be called when parsing is done.
public List<RssItem> getItems() {
return rssItems;
}
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
if ("item".equals(qName)) {
currentItem = new RssItem();
} else if ("title".equals(qName)) {
parsingTitle = true;
} else if ("link".equals(qName)) {
parsingLink = true;
}
}
// The EndElement method adds the current RssItem to the list when a closing item tag is processed. It sets appropriate indicators to false - when title and link closing tags are processed
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
if ("item".equals(qName)) {
currentItem.setIcon(this.icon);
rssItems.add(currentItem);
// update the progress
this.task.publicPublishProgress();
currentItem = null;
} else if ("title".equals(qName)) {
parsingTitle = false;
} else if ("link".equals(qName)) {
parsingLink = false;
}
}
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
if (parsingTitle) {
if (currentItem != null)
currentItem.setTitle(new String(ch, start, length));
} else if (parsingLink) {
if (currentItem != null) {
currentItem.setLink(new String(ch, start, length));
parsingLink = false;
}
}
}
}
Below are the error messages:
03-08 00:46:08.420: E/AndroidRuntime(1466): FATAL EXCEPTION: AsyncTask #1
03-08 00:46:08.420: E/AndroidRuntime(1466): Process: com.example.bucknellian, PID: 1466
03-08 00:46:08.420: E/AndroidRuntime(1466): java.lang.RuntimeException: An error occured while executing doInBackground()
03-08 00:46:08.420: E/AndroidRuntime(1466): at android.os.AsyncTask$3.done(AsyncTask.java:300)
03-08 00:46:08.420: E/AndroidRuntime(1466): at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:355)
03-08 00:46:08.420: E/AndroidRuntime(1466): at java.util.concurrent.FutureTask.setException(FutureTask.java:222)
03-08 00:46:08.420: E/AndroidRuntime(1466): at java.util.concurrent.FutureTask.run(FutureTask.java:242)
03-08 00:46:08.420: E/AndroidRuntime(1466): at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)
03-08 00:46:08.420: E/AndroidRuntime(1466): at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
03-08 00:46:08.420: E/AndroidRuntime(1466): at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
03-08 00:46:08.420: E/AndroidRuntime(1466): at java.lang.Thread.run(Thread.java:841)
03-08 00:46:08.420: E/AndroidRuntime(1466): Caused by: java.lang.NullPointerException: println needs a message
03-08 00:46:08.420: E/AndroidRuntime(1466): at android.util.Log.println_native(Native Method)
03-08 00:46:08.420: E/AndroidRuntime(1466): at android.util.Log.e(Log.java:232)
03-08 00:46:08.420: E/AndroidRuntime(1466): at com.example.bucknellian.util.GetRSSDataTask.doInBackground(GetRSSDataTask.java:49)
03-08 00:46:08.420: E/AndroidRuntime(1466): at com.example.bucknellian.util.GetRSSDataTask.doInBackground(GetRSSDataTask.java:1)
03-08 00:46:08.420: E/AndroidRuntime(1466): at android.os.AsyncTask$2.call(AsyncTask.java:288)
03-08 00:46:08.420: E/AndroidRuntime(1466): at java.util.concurrent.FutureTask.run(FutureTask.java:237)
03-08 00:46:08.420: E/AndroidRuntime(1466): ... 4 more
03-08 00:46:11.590: I/Process(1466): Sending signal. PID: 1466 SIG: 9
Any help is appreciated!!
After changing e.getMessage() to e.toString():
03-08 01:42:24.240: W/dalvikvm(826): threadid=11: thread exiting with uncaught exception (group=0xb3a32ba8)
03-08 01:42:24.300: E/AndroidRuntime(826): FATAL EXCEPTION: AsyncTask #1
03-08 01:42:24.300: E/AndroidRuntime(826): Process: com.example.bucknellian, PID: 826
03-08 01:42:24.300: E/AndroidRuntime(826): java.lang.RuntimeException: An error occured while executing doInBackground()
03-08 01:42:24.300: E/AndroidRuntime(826): at android.os.AsyncTask$3.done(AsyncTask.java:300)
03-08 01:42:24.300: E/AndroidRuntime(826): at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:355)
03-08 01:42:24.300: E/AndroidRuntime(826): at java.util.concurrent.FutureTask.setException(FutureTask.java:222)
03-08 01:42:24.300: E/AndroidRuntime(826): at java.util.concurrent.FutureTask.run(FutureTask.java:242)
03-08 01:42:24.300: E/AndroidRuntime(826): at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)
03-08 01:42:24.300: E/AndroidRuntime(826): at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
03-08 01:42:24.300: E/AndroidRuntime(826): at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
03-08 01:42:24.300: E/AndroidRuntime(826): at java.lang.Thread.run(Thread.java:841)
03-08 01:42:24.300: E/AndroidRuntime(826): Caused by: java.lang.NullPointerException: println needs a message
03-08 01:42:24.300: E/AndroidRuntime(826): at android.util.Log.println_native(Native Method)
03-08 01:42:24.300: E/AndroidRuntime(826): at android.util.Log.e(Log.java:232)
03-08 01:42:24.300: E/AndroidRuntime(826): at com.example.bucknellian.util.GetRSSDataTask.doInBackground(GetRSSDataTask.java:47)
03-08 01:42:24.300: E/AndroidRuntime(826): at com.example.bucknellian.util.GetRSSDataTask.doInBackground(GetRSSDataTask.java:1)
03-08 01:42:24.300: E/AndroidRuntime(826): at android.os.AsyncTask$2.call(AsyncTask.java:288)
03-08 01:42:24.300: E/AndroidRuntime(826): at java.util.concurrent.FutureTask.run(FutureTask.java:237)
03-08 01:42:24.300: E/AndroidRuntime(826): ... 4 more
03-08 01:42:36.100: D/dalvikvm(947): GC_FOR_ALLOC freed 70K, 5% free 3064K/3212K, paused 31ms, total 34ms
03-08 01:42:36.120: I/dalvikvm-heap(947): Grow heap (frag case) to 3.670MB for 635812-byte allocation
03-08 01:42:36.150: D/dalvikvm(947): GC_FOR_ALLOC freed 4K, 5% free 3680K/3836K, paused 28ms, total 28ms
03-08 01:42:36.230: D/RssReader(947): main
03-08 01:42:36.410: I/Choreographer(947): Skipped 32 frames! The application may be doing too much work on its main thread.
03-08 01:42:36.470: D/gralloc_goldfish(947): Emulator without GPU emulation detected.
03-08 01:42:36.490: D/RssReader(947): AsyncTask #1
03-08 01:42:37.470: I/Choreographer(947): Skipped 36 frames! The application may be doing too much work on its main thread.
03-08 01:42:40.100: E/RssReader(947): java.lang.NullPointerException
After changing to e.printStackTrace():
03-08 01:52:31.850: W/System.err(1201): java.lang.NullPointerException
03-08 01:52:31.850: W/System.err(1201): at com.example.bucknellian.util.RssParseHandler.endElement(RssParseHandler.java:51)
03-08 01:52:31.850: W/System.err(1201): at org.apache.harmony.xml.ExpatParser.endElement(ExpatParser.java:156)
03-08 01:52:31.860: W/System.err(1201): at org.apache.harmony.xml.ExpatParser.appendBytes(Native Method)
03-08 01:52:31.860: W/System.err(1201): at org.apache.harmony.xml.ExpatParser.parseFragment(ExpatParser.java:513)
03-08 01:52:31.860: W/System.err(1201): at org.apache.harmony.xml.ExpatParser.parseDocument(ExpatParser.java:474)
03-08 01:52:31.860: W/System.err(1201): at org.apache.harmony.xml.ExpatReader.parse(ExpatReader.java:316)
03-08 01:52:31.860: W/System.err(1201): at org.apache.harmony.xml.ExpatReader.parse(ExpatReader.java:294)
03-08 01:52:31.860: W/System.err(1201): at javax.xml.parsers.SAXParser.parse(SAXParser.java:390)
03-08 01:52:31.860: W/System.err(1201): at javax.xml.parsers.SAXParser.parse(SAXParser.java:266)
03-08 01:52:31.860: W/System.err(1201): at com.example.bucknellian.util.RssReader.getItems(RssReader.java:30)
03-08 01:52:31.860: W/System.err(1201): at com.example.bucknellian.util.GetRSSDataTask.doInBackground(GetRSSDataTask.java:44)
03-08 01:52:31.860: W/System.err(1201): at com.example.bucknellian.util.GetRSSDataTask.doInBackground(GetRSSDataTask.java:1)
03-08 01:52:31.860: W/System.err(1201): at android.os.AsyncTask$2.call(AsyncTask.java:288)
03-08 01:52:31.860: W/System.err(1201): at java.util.concurrent.FutureTask.run(FutureTask.java:237)
03-08 01:52:31.860: W/System.err(1201): at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231)
03-08 01:52:31.860: W/System.err(1201): at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
03-08 01:52:31.860: W/System.err(1201): at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
03-08 01:52:31.860: W/System.err(1201): at java.lang.Thread.run(Thread.java:841)
After adding rssItems to RssReader:
03-08 20:41:07.210: E/AndroidRuntime(1054): FATAL EXCEPTION: main
03-08 20:41:07.210: E/AndroidRuntime(1054): Process: com.example.bucknellian, PID: 1054
03-08 20:41:07.210: E/AndroidRuntime(1054): java.lang.IllegalStateException: The content of the adapter has changed but ListView did not receive a notification. Make sure the content of your adapter is not modified from a background thread, but only from the UI thread. Make sure your adapter calls notifyDataSetChanged() when its content changes. [in ListView(16908298, class android.widget.ListView) with Adapter(class com.example.bucknellian.util.RssItemAdapter)]
03-08 20:41:07.210: E/AndroidRuntime(1054): at android.widget.ListView.layoutChildren(ListView.java:1555)
03-08 20:41:07.210: E/AndroidRuntime(1054): at android.widget.AbsListView.onLayout(AbsListView.java:2091)
03-08 20:41:07.210: E/AndroidRuntime(1054): at android.view.View.layout(View.java:14817)
03-08 20:41:07.210: E/AndroidRuntime(1054): at android.view.ViewGroup.layout(ViewGroup.java:4631)
03-08 20:41:07.210: E/AndroidRuntime(1054): at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
03-08 20:41:07.210: E/AndroidRuntime(1054): at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
03-08 20:41:07.210: E/AndroidRuntime(1054): at android.view.View.layout(View.java:14817)
03-08 20:41:07.210: E/AndroidRuntime(1054): at android.view.ViewGroup.layout(ViewGroup.java:4631)
03-08 20:41:07.210: E/AndroidRuntime(1054): at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
03-08 20:41:07.210: E/AndroidRuntime(1054): at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
03-08 20:41:07.210: E/AndroidRuntime(1054): at android.view.View.layout(View.java:14817)
03-08 20:41:07.210: E/AndroidRuntime(1054): at android.view.ViewGroup.layout(ViewGroup.java:4631)
03-08 20:41:07.210: E/AndroidRuntime(1054): at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1671)
03-08 20:41:07.210: E/AndroidRuntime(1054): at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1525)
03-08 20:41:07.210: E/AndroidRuntime(1054): at android.widget.LinearLayout.onLayout(LinearLayout.java:1434)
03-08 20:41:07.210: E/AndroidRuntime(1054): at android.view.View.layout(View.java:14817)
03-08 20:41:07.210: E/AndroidRuntime(1054): at android.view.ViewGroup.layout(ViewGroup.java:4631)
03-08 20:41:07.210: E/AndroidRuntime(1054): at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
03-08 20:41:07.210: E/AndroidRuntime(1054): at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
03-08 20:41:07.210: E/AndroidRuntime(1054): at android.view.View.layout(View.java:14817)
03-08 20:41:07.210: E/AndroidRuntime(1054): at android.view.ViewGroup.layout(ViewGroup.java:4631)
03-08 20:41:07.210: E/AndroidRuntime(1054): at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
03-08 20:41:07.210: E/AndroidRuntime(1054): at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
03-08 20:41:07.210: E/AndroidRuntime(1054): at android.view.View.layout(View.java:14817)
03-08 20:41:07.210: E/AndroidRuntime(1054): at android.view.ViewGroup.layout(ViewGroup.java:4631)
03-08 20:41:07.210: E/AndroidRuntime(1054): at com.android.internal.widget.ActionBarOverlayLayout.onLayout(ActionBarOverlayLayout.java:374)
03-08 20:41:07.210: E/AndroidRuntime(1054): at android.view.View.layout(View.java:14817)
03-08 20:41:07.210: E/AndroidRuntime(1054): at android.view.ViewGroup.layout(ViewGroup.java:4631)
03-08 20:41:07.210: E/AndroidRuntime(1054): at android.widget.FrameLayout.layoutChildren(FrameLayout.java:453)
03-08 20:41:07.210: E/AndroidRuntime(1054): at android.widget.FrameLayout.onLayout(FrameLayout.java:388)
03-08 20:41:07.210: E/AndroidRuntime(1054): at android.view.View.layout(View.java:14817)
03-08 20:41:07.210: E/AndroidRuntime(1054): at android.view.ViewGroup.layout(ViewGroup.java:4631)
03-08 20:41:07.210: E/AndroidRuntime(1054): at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:1987)
03-08 20:41:07.210: E/AndroidRuntime(1054): at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1744)
03-08 20:41:07.210: E/AndroidRuntime(1054): at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1000)
03-08 20:41:07.210: E/AndroidRuntime(1054): at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:5670)
03-08 20:41:07.210: E/AndroidRuntime(1054): at android.view.Choreographer$CallbackRecord.run(Choreographer.java:761)
03-08 20:41:07.210: E/AndroidRuntime(1054): at android.view.Choreographer.doCallbacks(Choreographer.java:574)
03-08 20:41:07.210: E/AndroidRuntime(1054): at android.view.Choreographer.doFrame(Choreographer.java:544)
03-08 20:41:07.210: E/AndroidRuntime(1054): at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:747)
03-08 20:41:07.210: E/AndroidRuntime(1054): at android.os.Handler.handleCallback(Handler.java:733)
03-08 20:41:07.210: E/AndroidRuntime(1054): at android.os.Handler.dispatchMessage(Handler.java:95)
03-08 20:41:07.210: E/AndroidRuntime(1054): at android.os.Looper.loop(Looper.java:136)
03-08 20:41:07.210: E/AndroidRuntime(1054): at android.app.ActivityThread.main(ActivityThread.java:5017)
03-08 20:41:07.210: E/AndroidRuntime(1054): at java.lang.reflect.Method.invokeNative(Native Method)
03-08 20:41:07.210: E/AndroidRuntime(1054): at java.lang.reflect.Method.invoke(Method.java:515)
03-08 20:41:07.210: E/AndroidRuntime(1054): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:779)
03-08 20:41:07.210: E/AndroidRuntime(1054): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:595)
03-08 20:41:07.210: E/AndroidRuntime(1054): at dalvik.system.NativeStart.main(Native Method)
I know this is because I tried to change the UI in a thread different form the UI thread. Do you know how to change it?
Try this..
Use .printStackTrace();
to view error message
try {
// Parse RSS, get items
rssReader.getItems();
} catch (Exception e) {
e.printStackTrace();
}
EDIT:
Add this.rssItems = rssItems;
in your RssReader.class
public RssReader(String rssUrl, String icon, List<RssItem> rssItems, GetRSSDataTask task) {
this.rssUrl = rssUrl;
this.icon = icon;
this.task = task;
this.rssItems = rssItems;
}