95

我有一个运行线程的 Android 应用程序。我想要一条 Toast 消息与一条消息一起显示。

当我这样做时,我得到以下异常:

Logcat跟踪:

FATAL EXCEPTION: Timer-0 
 java.lang.RuntimeException: Can't create handler inside thread that has not 
    called Looper.prepare()

 at android.os.Handler.<init>(Handler.java:121)
 at android.widget.Toast$TN.<init>(Toast.java:322)
 at android.widget.Toast.<init>(Toast.java:91)
 at android.widget.Toast.makeText(Toast.java:238) 

是否有解决方法将 Toast 消息从线程推送到用户界面?

4

7 回答 7

125

我得到了这个例外,因为我试图从后台线程中弹出一个 Toast 弹出窗口。
Toast 需要一个 Activity 来推送到用户界面,而线程没有。
因此,一种解决方法是为线程提供指向父 Activity 的链接,并为其提供 Toast。

将此代码放在要发送 Toast 消息的线程中:

parent.runOnUiThread(new Runnable() {
    public void run() {
        Toast.makeText(parent.getBaseContext(), "Hello", Toast.LENGTH_LONG).show();
    }
});

在创建此线程的后台线程中保留指向父 Activity 的链接。在线程类中使用父变量:

private static YourActivity parent;

创建线程时,通过构造函数将父 Activity 作为参数传递,如下所示:

public YourBackgroundThread(YourActivity parent) {
    this.parent = parent;
}

现在后台线程可以将 Toast 消息推送到屏幕上。

于 2013-09-22T21:15:42.517 回答
33

Android 基本上适用于两种线程类型,即 UI 线程和后台线程。根据android文档-

不要从 UI 线程外部访问 Android UI 工具包来解决此问题,Android 提供了几种从其他线程访问 UI 线程的方法。以下是可以提供帮助的方法列表:

Activity.runOnUiThread(Runnable)  
View.post(Runnable)  
View.postDelayed(Runnable, long)

现在有各种方法可以解决这个问题。我将通过代码示例进行解释

runOnUiThread

new Thread()
{
    public void run()
    {
        myactivity.this.runOnUiThread(new runnable()
        {
            public void run()
            {
                //Do your UI operations like dialog opening or Toast here
            }
        });
    }
}.start();

循环器

用于为线程运行消息循环的类。默认情况下,线程没有与之关联的消息循环;要创建一个,请在要运行循环的线程中调用 prepare(),然后 loop() 让它处理消息,直到循环停止。

class LooperThread extends Thread {
    public Handler mHandler;

    public void run() {
        Looper.prepare();

        mHandler = new Handler() {
            public void handleMessage(Message msg) {
                // process incoming messages here
            }
        };

        Looper.loop();
    }

异步任务

AsyncTask 允许您在用户界面上执行异步工作。它在工作线程中执行阻塞操作,然后在 UI 线程上发布结果,而无需您自己处理线程和/或处理程序。

public void onClick(View v) {
    new CustomTask().execute((Void[])null);
}


private class CustomTask extends AsyncTask<Void, Void, Void> {

    protected Void doInBackground(Void... param) {
        //Do some work
        return null;
    }

    protected void onPostExecute(Void param) {
        //Print Toast or open dialog
    }
}

处理程序

Handler 允许您发送和处理与线程的 MessageQueue 关联的 Message 和 Runnable 对象。

Message msg = new Message();


    new Thread()
    {
        public void run()
        {
            msg.arg1=1;
            handler.sendMessage(msg);
        }
    }.start();



    Handler handler = new Handler(new Handler.Callback() {

        @Override
        public boolean handleMessage(Message msg) {
            if(msg.arg1==1)
            {
                //Print Toast or open dialog        
            }
            return false;
        }
    });
于 2015-06-06T09:19:52.063 回答
10

这是我一直在做的事情:

  public void displayError(final String errorText) {
    Runnable doDisplayError = new Runnable() {
        public void run() {
            Toast.makeText(getApplicationContext(), errorText, Toast.LENGTH_LONG).show();
        }
    };
    messageHandler.post(doDisplayError);
}

这应该允许从任一线程调用该方法。

其中 messageHandler 在活动中声明为 ..

Handler messageHandler = new Handler();
于 2015-05-06T16:32:34.930 回答
6

http://developer.android.com/guide/components/processes-and-threads.html

此外,Android UI 工具包不是线程安全的。因此,您不能从工作线程操作您的 UI — 您必须从 UI 线程对您的用户界面进行所有操作。因此,Android 的单线程模型只有两条规则:

  1. 不要阻塞 UI 线程
  2. 不要从 UI 线程外部访问 Android UI 工具包

您必须检测工作线程中的空闲情况并在主线程中显示祝酒词。

如果您想要更详细的答案,请发布一些代码。

代码发布后:

strings.xml

<string name="idleness_toast">"You are getting late do it fast"</string>

YourWorkerThread.java

Toast.makeText(getApplicationContext(), getString(R.string.idleness_toast), 
    Toast.LENGTH_LONG).show();

不要使用 AlertDialog,做出选择。AlertDialog 和 Toast 是两个不同的东西。

于 2013-06-29T10:13:49.313 回答
4
runOnUiThread(new Runnable(){
   public void run() {
     Toast.makeText(getApplicationContext(), "Status = " + message.getBody() , Toast.LENGTH_LONG).show();
   }
 });

这对我有用

于 2019-03-09T13:02:26.620 回答
1

您可以简单地使用 BeginInvokeOnMainThread()。它在设备主 (UI) 线程上调用 Action。

Device.BeginInvokeOnMainThread(() => { displayToast("text to display"); });

它很简单,非常适合我!

编辑:如果您使用的是 C# Xamarin,则可以使用

于 2016-08-28T13:04:01.537 回答
0

JobService从以下代码中得到了这个错误:

    BluetoothLeScanner bluetoothLeScanner = getBluetoothLeScanner();
    if (BluetoothAdapter.STATE_ON == getBluetoothAdapter().getState() && null != bluetoothLeScanner) {
        // ...
    } else {
        Logger.debug(TAG, "BluetoothAdapter isn't on so will attempting to turn on and will retry starting scanning in a few seconds");
        getBluetoothAdapter().enable();
        (new Handler()).postDelayed(new Runnable() {
            @Override
            public void run() {
                startScanningBluetooth();
            }
        }, 5000);
    }

服务崩溃:

2019-11-21 11:49:45.550 729-763/? D/BluetoothManagerService: MESSAGE_ENABLE(0): mBluetooth = null

    --------- beginning of crash
2019-11-21 11:49:45.556 8629-8856/com.locuslabs.android.sdk E/AndroidRuntime: FATAL EXCEPTION: Timer-1
    Process: com.locuslabs.android.sdk, PID: 8629
    java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
        at android.os.Handler.<init>(Handler.java:203)
        at android.os.Handler.<init>(Handler.java:117)
        at com.locuslabs.sdk.ibeacon.BeaconScannerJobService.startScanningBluetoothAndBroadcastAnyBeaconsFoundAndUpdatePersistentNotification(BeaconScannerJobService.java:120)
        at com.locuslabs.sdk.ibeacon.BeaconScannerJobService.access$500(BeaconScannerJobService.java:36)
        at com.locuslabs.sdk.ibeacon.BeaconScannerJobService$2$1.run(BeaconScannerJobService.java:96)
        at java.util.TimerThread.mainLoop(Timer.java:555)
        at java.util.TimerThread.run(Timer.java:505)

所以我从Handler改为Timer如下:

   (new Timer()).schedule(new TimerTask() {
                @Override
                public void run() {
                    startScanningBluetooth();
                }
            }, 5000);

现在代码不再抛出RuntimeException了。

于 2019-11-21T20:10:26.353 回答