【问题标题】:How to get XML using AsyncTask and Timer?如何使用 AsyncTask 和 Timer 获取 XML?
【发布时间】:2011-01-02 13:46:36
【问题描述】:

为了重复从服务器获取 XML 数据,我尝试按照 Mark Murphy's suggestion 使用 AsyncTask 和 Timer。

我收到以下错误:

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()

我在 Windows 上使用带有 Eclipse 的 SDK 1.5。

我查看了文档、StackOverflow 和 Android 开发人员组,但我不清楚是什么导致了错误或如何修复它。

我可以一次获取数据——即没有异步和计时器——并通过 SAX 解析它而不会出现问题。

完整的应用代码如下。

请原谅任何幼稚的错误:我对 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);
            }
        }
    }

}

【问题讨论】:

    标签: android android-asynctask


    【解决方案1】:

    问题出在 TimerTask 的使用上。 TimerTask 运行应该发布到处理程序,如下所示:

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

    当然这有点难看,所以建议去掉匿名类。

    【讨论】:

    • 非常感谢,詹姆斯 - 这很有效。我添加了一个使用这个的答案。
    【解决方案2】:

    感谢詹姆斯的回答,我得到了这个工作。

    我已经包含了下面的代码,以防它对任何人有用。

    警告开发者!该代码适用于我,但可能包含错误。

    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);
            }
        }
    }
    

    【讨论】:

      【解决方案3】:

      问题似乎是你没有打电话给Looper.prepare()。你可以看看documentation for Looper

      Handler 需要一个消息循环来处理消息,而 Thread 默认情况下没有。

      似乎AsyncTask uses a Handler internally 所以在MyTimerTaskrun() 方法的顶部添加Looper.prepare() 应该可以解决您的问题。

      【讨论】:

      • 试过这个,但仍然得到同样的异常。如果我在 MyTimerTask 中将 Looper.prepare() 添加到 run() 的顶部,doInBackground() 会被调用一次,无一例外,但永远不会调用 onPostExecute()。
      • Sam 的 MyHandler 似乎不是 android Handler 的子类 不???否则如何将它作为 XmlReader 的参数传递?您的应用程序中是否还有另一个线程创建了 android.os.Handler
      • 我的错误,对不起。问题是AsyncTask 使用了Handler,因此您需要在TimerTask 线程中执行Looper.prepare()
      • 伙计们,它是一个 SAX 内容处理程序,而不是一个处理程序。 :\
      • @James - 一开始我没有意识到这一点,因为我没有仔细阅读代码。正如我上面所说,问题是AsyncTask 在内部使用Handler,因此您需要在TimerTask 线程中创建一个消息循环。
      【解决方案4】:

      我会将该服务与警报管理器一起使用

         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);
      

      【讨论】:

      • AlarmManager 绝对是这样的任务。警报管理器的 javadoc 说:“警报管理器适用于您希望在特定时间运行应用程序代码的情况,即使您的应用程序当前没有运行。对于正常的计时操作(滴答声、超时等),它是使用 Handler 更容易、更高效。” developer.android.com/reference/android/app/AlarmManager.html
      • 重申一下,根据 Dave Webb 的评论:我需要重复执行后台任务,而不是在特定时间。
      • 我认为我的要求非常明确:“重复从服务器获取 XML 数据”——并且代码显示我打算每秒获取更新。我同意你的观点,这可以通过 AlarmManager 完成,但我同意 Dave Webb 的观点,这可能不是最好的方法。
      【解决方案5】:

      问题是你不能在 doInBackground() 运行的线程中创建 Handler 对象。我会实现 onPreExecute() 方法并将你的设置代码放在那里。尝试将这些行移到 onPreExecute() 方法中:

                  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);
      

      【讨论】:

      • onPreExecute() 在 UI 线程中运行,旨在更新 UI 而不是处理。
      • 我认为仅仅因为它在 UI 线程上运行并不意味着您应该只将它用于 UI 更新。在我看来,在 onPreExecute() 中做一些快速设置就可以了。
      猜你喜欢
      • 2011-06-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多