Tutorial :How to get XML using AsyncTask and Timer?



Question:

In order to get XML data from a server repeatedly, I'm attempting to use AsyncTask and Timer as per Mark Murphy's suggestion.

I get the following error:

01-07 16:11:26.705: ERROR/AndroidRuntime(729):   Caused by: java.lang.RuntimeException:   Can't create handler inside thread that has not   called Looper.prepare()  

I'm using SDK 1.5 with Eclipse on Windows.

I've looked in documentation, on StackOverflow and in the Android Developers group, but I'm not clear what's causing the error or how to fix it.

I can get the data once -- i.e. without Async and Timer -- and parse it via SAX without problems.

Full app code below.

Please excuse any naive errors: I'm quite new to Android.

package com.foo.bar.myactivity;    import java.net.URL;  import java.util.Timer;  import java.util.TimerTask;    import javax.xml.parsers.SAXParser;  import javax.xml.parsers.SAXParserFactory;    import org.xml.sax.InputSource;  import org.xml.sax.XMLReader;    import android.app.Activity;  import android.os.AsyncTask;  import android.os.Bundle;  import android.util.Log;    public class MyActivity extends Activity {        @Override      public void onCreate(Bundle savedInstanceState) {          super.onCreate(savedInstanceState);            setContentView(R.layout.main);            Timer timer;          timer = new Timer();          timer.schedule(new MyTimerTask(), 0, 1000);       }        public class MyAsyncTask extends AsyncTask<String, Integer, MyData> {          protected MyData doInBackground(String... string) {              MyData myData = new MyData();              try {                  URL url = new URL("http://www.example.com/my.xml");                  SAXParserFactory spf = SAXParserFactory.newInstance();                  SAXParser sp = spf.newSAXParser();                    XMLReader xr = sp.getXMLReader();                    MyHandler myHandler = new MyHandler();                  xr.setContentHandler(myHandler);                    System.setProperty("http.proxyHost", "www-cache.example.com");                  System.setProperty("http.proxyPort", "80");                    xr.parse(new InputSource(url.openStream()));                    myData = myHandler.getParsedData();                  return myData;                } catch (Exception e) {                  Log.e(">>>>>>>>>>>> Error getting myData: ", e.getMessage(), e);                  return myData;              }            }            protected void onProgressUpdate(Integer... progress) {              // setProgressPercent(progress[0]);          }            protected void onPostExecute(MyData myData) {              Log.d(">>>>>>>>>>>>>My data: ", myData.toString());          }      }        public class MyTimerTask extends TimerTask {          public void run() {              try {                  new MyAsyncTask().execute("");              } catch (Exception e) {                  Log.e(">>>>>>>>>>>> Error executing MyAsyncTask: ", e.getMessage(), e);              }          }      }    }  


Solution:1

The problem is in the use of TimerTask. TimerTask run should post to a handler, something like this:

private Handler mHandler = new Handler();     public class MyTimerTask extends TimerTask {      public void run() {          mHandler.post(              new Runnable() {                   public void run() {                       new MyAsyncTask().execute("");                  }               };               )      }  }  

Of course this is getting a bit ugly, so would recommend taking out the anonymous class.


Solution:2

I got this to work thanks to James's answer.

I've included the code below in case it's useful to anyone.

Caveat developer! The code works for me, but may contain errors.

package com.example.test;    import java.net.URL;  import java.util.Timer;  import java.util.TimerTask;    import javax.xml.parsers.SAXParser;  import javax.xml.parsers.SAXParserFactory;    import org.xml.sax.InputSource;  import org.xml.sax.XMLReader;    import android.app.Activity;  import android.os.AsyncTask;  import android.os.Bundle;  import android.os.Handler;  import android.util.Log;    public class MyXmlPoller extends Activity {        private Handler handler = new Handler();        @Override      public void onCreate(Bundle savedInstanceState) {          super.onCreate(savedInstanceState);          setContentView(R.layout.main);            new Timer().schedule(new MyTimerTask(), 0, 1000);      }        private class MyAsyncTask extends AsyncTask<Integer, Integer, MyData> {            protected MyData doInBackground(Integer... counter) {              MyData myData = new MyData();                try {                  URL url = new URL("http://www.example.com/my.xml");                  SAXParserFactory spf = SAXParserFactory.newInstance();                  SAXParser sp = spf.newSAXParser();                    XMLReader xr = sp.getXMLReader();                    MySAXHandler mySAXHandler = new Handler();                  xr.setContentHandler(mySAXHandler);                    xr.parse(new InputSource(url.openStream()));                    myData = mySAXHandler.getParsedData();                    return myData;                } catch (Exception e) {                  Log.e("!!!!!!!!!! MyAsyncTask doInBackground error", e.getMessage(), e);                  return myData;              }            }            protected void onPostExecute(MyData myData) {              Log.d("+++++++++++++ MyAsyncTask onPostExecute", myData.toString());          }      } // MyAsyncTask        public class MyTimerTask extends TimerTask {          private Runnable runnable = new Runnable() {              public void run() {                  new MyAsyncTask().execute();              }          };            public void run() {              handler.post(runnable);          }      }  }  


Solution:3

The problem seems to be that you haven't called Looper.prepare(). You could have a look at the documentation for Looper.

A Handler needs a message loop in order to process messages and a Thread by default doesn't have one.

It seems that AsyncTask uses a Handler internally so add Looper.prepare() at the top of your run() method in MyTimerTask and that should solve your problem.


Solution:4

I'd use the service with alarm manager

   I    Intent updateIntent = new Intent(ACTION_UPDATE_ALL);          updateIntent.setClass(this, UpdateService.class);          PendingIntent pendingIntent = PendingIntent.getService(this, 0, updateIntent, 0);          AlarmManager alarmManager = (AlarmManager)getSystemService(Context.ALARM_SERVICE);          alarmManager.set(AlarmManager.RTC, nextUpdate, pendingIntent);  


Solution:5

The problem is you can't create the Handler object in the thread that doInBackground() runs in. I would implement the onPreExecute() method and put your setup code in there. Try moving these lines into the onPreExecute() method:

            URL url = new URL("http://www.example.com/my.xml");              SAXParserFactory spf = SAXParserFactory.newInstance();              SAXParser sp = spf.newSAXParser();                XMLReader xr = sp.getXMLReader();                MyHandler myHandler = new MyHandler();              xr.setContentHandler(myHandler);  

Note:If u also have question or solution just comment us below or mail us on toontricks1994@gmail.com
Previous
Next Post »