Android - 无法在未调用Looper.prepare()的线程中创建处理程序

21

我正在使用Google Maps V2 API开发一个简单的应用程序,只是为了获得基础知识,但我遇到了这个错误:

09-09 21:21:41.154: E/AndroidRuntime(3796): FATAL EXCEPTION: Thread-441
09-09 21:21:41.154: E/AndroidRuntime(3796): java.lang.ExceptionInInitializerError
09-09 21:21:41.154: E/AndroidRuntime(3796):  at cz.vongrad.dataSourceLayer.DataMapper.loadLocation(DataMapper.java:111)
09-09 21:21:41.154: E/AndroidRuntime(3796):  at cz.vongrad.dataSourceLayer.DBFacade.loadLocations(DBFacade.java:32)
09-09 21:21:41.154: E/AndroidRuntime(3796):  at cz.vongrad.domainLayer.Controller.loadLocations(Controller.java:94)
09-09 21:21:41.154: E/AndroidRuntime(3796):  at cz.vongrad.locator.MapActivity$1.run(MapActivity.java:199)
09-09 21:21:41.154: E/AndroidRuntime(3796): Caused by: java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
09-09 21:21:41.154: E/AndroidRuntime(3796):  at android.os.Handler.<init>(Handler.java:121)
09-09 21:21:41.154: E/AndroidRuntime(3796):  at android.os.AsyncTask$InternalHandler.<init>(AsyncTask.java:607)
09-09 21:21:41.154: E/AndroidRuntime(3796):  at android.os.AsyncTask$InternalHandler.<init>(AsyncTask.java:607)
09-09 21:21:41.154: E/AndroidRuntime(3796):  at android.os.AsyncTask.<clinit>(AsyncTask.java:190)
09-09 21:21:41.154: E/AndroidRuntime(3796):  ... 4 more
09-09 21:21:41.164: E/EmbeddedLogger(239): App crashed!

DataMapper:

class LoadLocations extends AsyncTask<String, Void, ArrayList<Friend>>{

        private JSONObject jSONObject;


        @Override
        protected ArrayList<Friend> doInBackground(String... args) {

            List<NameValuePair> params = new ArrayList<NameValuePair>();
            params.add(new BasicNameValuePair("user_name", args[1]));

            jSONObject = jSONParser.makeHttpRequest(args[0], "POST", params);
            Log.d(TAG, jSONObject.toString());

            ArrayList<Friend> tempFriends = new ArrayList<Friend>();

            try {
                if(jSONObject.getInt("success") == 1){

                    JSONArray jsonArray = jSONObject.getJSONArray("users");


                    for (int i = 0; i < jsonArray.length(); i++) {
                        JSONObject o = jsonArray.getJSONObject(i);

                        tempFriends.add(new Friend(o.getString("user_name"), new LatLng(o.getDouble("loc_y"), o.getDouble("loc_x")), false));
                    }

                }
            } catch (JSONException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

            return tempFriends;
        }

    }

public ArrayList<Friend> loadLocation(String URL, String userName) throws InterruptedException, ExecutionException {
        return new LoadLocations().execute(URL, userName).get();

    }

地图活动:

public class MapActivity extends Activity implements LocationSource, LocationListener {
@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
}
private Thread loadFriendLocations = new Thread()
    {

        @Override
        public void run() {
            Log.d(TAG, "LastKnownLocation: " + locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER));
            try {
                while(true) {
                    controller.loadLocations(LOAD_LOCATIONS);
                    Log.d(TAG, "Loading new locations");
                    Message msg = uIHandler.obtainMessage();
                    msg.what = ADD_MAP_PIN;
                    uIHandler.sendMessage(msg);

                    sleep(10000);                   
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    };

    final Handler uIHandler = new Handler(){
      @Override
      public void handleMessage(Message msg) {

        if(msg.what==ADD_MAP_PIN){

            for (Friend friend : controller.getFriends()) {

                    addMapPin(friend);


            }
        }
        super.handleMessage(msg);
      }
    };

奇怪的是,我可以在模拟器(API 16)上运行该应用程序,并且它运行得非常好,但是当我尝试在物理设备(API 15)上运行它时,出现了这个错误。 AsyncTask 在单独的类中运行,因此可能会引起此问题。希望能得到任何建议。先谢谢了。


如果您需要一个如何在Android中使用Google Maps API的示例,我这里有一个:https://github.com/ChristopherTulip/MapHomeTest。 - chris-tulip
1
可能是重复问题 - mihirjoshi
3个回答

37

我遇到了类似的问题,问题在于我从主线程创建了一系列线程。实际上,主 UI 线程启动了一个运行在服务中的服务,然后又启动了另一个线程,最终拥有一个 FileObserver(另一个线程),我需要通过弹出一个 toast 消息与 UI 线程通信。我尝试了几个小时,尝试了以下方法,它像魔法一样起作用。

//Let this be the code in your n'th level thread from main UI thread
Handler h = new Handler(Looper.getMainLooper());
h.post(new Runnable() {
  public void run() {
    Toast.makeText(context, "Your message to main thread", Toast.LENGTH_SHORT).show();
  }
});

这里的主要关键在于Looper类的getMainLooper()函数,它将为您提供关于主UI线程的Looper。


1
我的代码放在run方法中时,它没有被执行。我该怎么办? - Neri

10

你不能从后台线程执行 AsyncTask。请参阅 AsyncTask 文档 中的 "线程规则" 部分。


那么,您认为如果我在MapActivity中使用处理程序来执行AsyncTask的线程,它会起作用吗?还是这是一个不好的想法,我应该完全以不同的方式处理它...? - Husky
3
@Husky说:坦白说,整个加载朋友位置的事情有点可怕。话虽如此,既然你已经在后台线程上了,你不需要一个AsyncTask -- 直接做就可以了。 我的翻译:@Husky说:“老实说,整个加载朋友位置的东西有点吓人。不过话说回来,既然你已经在后台线程上了,那么你不需要一个AsyncTask——直接做就行了。” - CommonsWare
我想使用AsyncTask的原因是我执行了一个无法在主线程中执行的网络操作,所以我必须在AsyncTask中执行。顺便问一下,有没有不使用AsyncTask的方法可以做到这一点? - Husky
@Husky:我再说一遍:你已经有一个后台线程了。与其从后台线程启动AsyncTask不如直接在现有的后台线程上完成工作。如果出于某种原因你真的想要在其他后台线程上完成工作,那就使用Thread - CommonsWare
我创建了几个层,并希望在最低层执行从服务器加载数据(目前由AsyncTask完成),然后使用return语句将数据传回。问题是,什么更有效率,将当前位于MapActivity中的线程放置到最低层并从那里执行常量加载,这将根据我的结构导致更高的耦合(保持对存储所有来自服务器的数据的Controller的引用)?当我只是使用MapActivity中的线程进行工作时,它会抛出一个错误:NetworkOnMainThreadException。 - Husky

4

在后台执行此操作...

runOnUiThread(new Runnable() {
    public void run() 
    {
        if (items.equals("null")) 
        {
            Toast.makeText(getApplicationContext(), "No Event Found",Toast.LENGTH_LONG).show();
        }                   
        else {                       
        }   
    }
});

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接