【问题标题】:Error: Can't create handler inside thread that has not called Looper.prepare()错误:无法在未调用 Looper.prepare() 的线程内创建处理程序
【发布时间】:2013-07-04 19:10:15
【问题描述】:

我在下面的代码中遇到了著名的“无法在未调用 Looper.prepare() 的线程内创建处理程序”错误。根据我的大部分 Google 搜索结果,当我尝试从我的非 UI 线程更新 UI 时会发生这种情况,但是当我从我的其他帮助程序类调用该方法时,我此时没有更新 UI(仅在地址被回来);所以请咨询为什么会发生这种情况。

package com.parspake.kookojapost;

import android.app.ActionBar;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.database.sqlite.SQLiteDatabase;
import android.location.*;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

public class TextShare extends Activity {


    ConnectionHandler textPageConn;
    TextView dt2;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.text_share);

        textPageConn = new ConnectionHandler(this);
        dt2 = (TextView) findViewById(R.id.show_location_text_page);

        Thread thread1 = new Thread(new Runnable() {

            @Override
            public void run() {

                textPageConn.getLocation();
                do {

                    if (textPageConn.mAddress != null) {
                        dt2.setText(textPageConn.mAddress);
                        break;
                    } else {
                        Log.d("debug","no location");
                    }

                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        Thread.currentThread().interrupt();
                    }
                } while (textPageConn.mAddress == null);
            }

        });
        thread1.start();
    }
}

这是我得到的例外:

07-04 23:59:38.730: E/AndroidRuntime(7149): FATAL EXCEPTION: Thread-8482
07-04 23:59:38.730: E/AndroidRuntime(7149): java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
07-04 23:59:38.730: E/AndroidRuntime(7149):     at android.os.Handler.<init>(Handler.java:121)
07-04 23:59:38.730: E/AndroidRuntime(7149):     at android.location.LocationManager$ListenerTransport$1.<init>(LocationManager.java:183)
07-04 23:59:38.730: E/AndroidRuntime(7149):     at android.location.LocationManager$ListenerTransport.<init>(LocationManager.java:183)
07-04 23:59:38.730: E/AndroidRuntime(7149):     at android.location.LocationManager._requestLocationUpdates(LocationManager.java:661)
07-04 23:59:38.730: E/AndroidRuntime(7149):     at android.location.LocationManager.requestLocationUpdates(LocationManager.java:486)
07-04 23:59:38.730: E/AndroidRuntime(7149):     at com.parspake.kookojapost.ConnectionHandler.getLocation(ConnectionHandler.java:136)
07-04 23:59:38.730: E/AndroidRuntime(7149):     at com.parspake.kookojapost.TextShare$2.run(TextShare.java:69)
07-04 23:59:38.730: E/AndroidRuntime(7149):     at java.lang.Thread.run(Thread.java:856)

所以 ConnectionHandler.java:136 是 -> mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 10000, 50, mlocationListener);

而 TextShare.java:69 是 -> textPageConn.getLocation();

这是我的 ConnectionHandler Helper 类:

package com.parspake.kookojapost;

import android.content.Context;
import android.location.*;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Bundle;
import android.util.Log;
import android.widget.Toast;

import java.io.IOException;
import java.util.List;
import java.util.Locale;

public class ConnectionHandler {

    private Context ctx;
    double mLat;
    double mLong;
    String currCity;
    String currCountry;
    String mAddress;
    LocationManager mLocationManager;
    LocationListener mlocationListener;

    public ConnectionHandler(Context context) {
        this.ctx = context;
    }


    public boolean locationSourceEnabled() {
        mLocationManager = (LocationManager) ctx.getSystemService(Context.LOCATION_SERVICE);
        boolean isInternetLocationAvailable = mLocationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
        boolean isGpsAvailable = mLocationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
        if (isInternetLocationAvailable) {
            return true;
        } else if (isGpsAvailable) {
            return true;
        }
        return false;
    }


    public void getLocation() {

        mlocationListener = new LocationListener() {
            @Override
            public void onLocationChanged(Location location) {

                mLat = location.getLatitude();
                mLong = location.getLongitude();

                Geocoder gcd = new Geocoder(ctx, Locale.getDefault());
                List<Address> addresses;
                try {
                    addresses = gcd.getFromLocation(mLat, mLong, 1);
                    if (addresses.size() > 0) {
                        currCity = addresses.get(0).getLocality();
                        currCountry =  addresses.get(0).getCountryName();
                        mAddress = currCity + " - " + currCountry;
                    }
                } catch (IOException e) {
                    Log.d("omid debug", e.getMessage());
                }
            }

            @Override
            public void onStatusChanged(String s, int i, Bundle bundle) {
            }

            @Override
            public void onProviderEnabled(String s) {
            }

            @Override
            public void onProviderDisabled(String s) {
            }
        };
        mLocationManager = (LocationManager) ctx.getSystemService(Context.LOCATION_SERVICE);
        if (locationSourceEnabled()) {
            if (mLocationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)) {
                mLocationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 10000, 50, mlocationListener);
            }
            if (mLocationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
                mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 10000, 50, mlocationListener);
            }
        }
    }
}

【问题讨论】:

    标签: android multithreading


    【解决方案1】:

    好的,问题是 requestLocationUpdates 是一个异步调用,这意味着您不需要在另一个线程上运行它,也意味着如果您尝试在另一个不允许的线程上有效地创建处理程序。因此,您可以删除线程并在 onCreate() 上运行所有这些(因为您只进行异步调用)

    参考:requestLocationUpdates gives error "Can't create Handler inside thread that has not called Looper.prepare()

    相反,您可以将同步调用放在另一个线程中...

    @Override
    public void onLocationChanged(Location location) {
         new Thread(new Runnable() {
    
            @Override
            public void run() {
                 mLat = location.getLatitude();
                 mLong = location.getLongitude();
    
                 Geocoder gcd = new Geocoder(ctx, Locale.getDefault());
                 List<Address> addresses;
                 try {
                     addresses = gcd.getFromLocation(mLat, mLong, 1);
                     if (addresses.size() > 0) {
                         currCity = addresses.get(0).getLocality();
                         currCountry =  addresses.get(0).getCountryName();
                         mAddress = currCity + " - " + currCountry;
                     }
                 } catch (IOException e) {
                     Log.d("omid debug", e.getMessage());
                 }
            }
        }).start();
    }
    

    【讨论】:

    • 也尝试了您的代码,但仍然得到相同的 Looper.prepare() 异常。我的代码在我的 ConnectionHandler 帮助器类中也引用了这一行: mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 10000, 50, mlocationListener);
    • 当然。添加到原始帖子中。
    • 好吧,我调用 requestLocationUpdates 是正确的,但实际上得到的是同步的地理编码器。
    【解决方案2】:

    您无法从非 UI 线程更新 TextView

    改用runOnUiThread

    runOnUiThread(new Runnable() {
    
                        @Override
                        public void run() {
                            dt2.setText(textPageConn.mAddress);
                        }
                    });
    

    【讨论】:

    • 我做到了。它没有用。在“做”之后,我写道:runOnUiThread(new Runnable() { ... then run() Override ..
    • 我建议您注释掉 dt2.setText() 行,并改写 Log.v("MY_APP", "here")。如果您没有收到Looper.prepare() 异常并在日志中看到“此处”,则问题出在setText()
    • 我确实把它注释掉了,但仍然得到 Looper.prepare() 异常!
    • 我的代码在我的 ConnectionHandler 帮助器类中也引用了这一行:mLLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 10000, 50, mlocationListener);
    • 为什么不把那些LocationManager相关的类放到TextShare类中呢?我确实相信,@RSenApps 指出了正确的答案:您应该在 onCreate() 中这样做。
    【解决方案3】:

    不可能在其他线程中更改ui内容..您可以通过两种方式解决这些问题

    • 使用 runOnUiThread() 方法,简单,但不是最佳实践
    • 另一种方法是使用带有回调的处理程序handler(Callback c),这是推荐的一种

    我会建议使用带有回调方法的处理程序。 你可以在这里得到很棒的教程。 handler with callback in thread

    【讨论】:

      猜你喜欢
      • 2014-09-28
      • 1970-01-01
      • 1970-01-01
      • 2012-08-15
      • 2011-09-06
      • 1970-01-01
      相关资源
      最近更新 更多