【问题标题】:java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare(); [duplicate]java.lang.RuntimeException:无法在未调用 Looper.prepare() 的线程内创建处理程序; [复制]
【发布时间】:2013-06-27 01:41:58
【问题描述】:

我有一个运行线程的 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 消息从线程推送到用户界面?

【问题讨论】:

标签: android multithreading toast


【解决方案1】:

我收到此异常是因为我试图从后台线程弹出 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 消息推送到屏幕上。

【讨论】:

  • 我如何调用该 Activity 中的方法,我正在使用此代码在其中发送 toast 消息?
  • 请不要将后续问题发布为 cmets。评论用于讨论顶部的问题,以及我的答案回答该问题的适用性。我们遵守规则很重要。遵守规则的每个人都是使 stackoverflow 保持良好状态的原因,并防止它变成像 yahoo answers 那样的马戏团。
  • 对于 Xamarin Android,Activity.RunOnUiThread(() => Toast.MakeText(this.Activity.ApplicationContext, "Message Text", ToastLength.Short).Show());
【解决方案2】:

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

处理程序

处理程序允许您发送和处理与线程的 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;
        }
    });

【讨论】:

    【解决方案3】:

    这是我一直在做的事情:

      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 在 Activity 中声明为 ..

    Handler messageHandler = new Handler();
    

    【讨论】:

      【解决方案4】:

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

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

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

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

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

      代码发布后:

      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 是两个不同的东西。

      【讨论】:

      • idleTimer = new IdleTimer(5000, new IIdleCallback() { @Override public void inactivityDetected() { AlertDialog.Builder alertDialogBu​​ilder = new AlertDialog.Builder( getApplicationContext()); Toast.makeText(null, "你迟到了,快点做吧...", Toast.LENGTH_LONG) .show(); } }); if(idleTimer.checkIsTimerRunning()){ idleTimer.stopIdleTimer(); } idleTimer.startIdleTimer();
      • 我只是希望我的 idleTimer 线程中显示在我的主用户界面上的东西应该出现在那个...
      • 对不起,我把所有的东西都编进去了,代码是这样的
      • idleTimer = new IdleTimer(5000, new IIdleCallback() { @Override public void inactivityDetected() { Toast.makeText(getApplicationContext(), "你来晚了,快点...", Toast .LENGTH_LONG) .show(); } }); if(idleTimer.checkIsTimerRunning()){ idleTimer.stopIdleTimer(); } idleTimer.startIdleTimer();
      【解决方案5】:
      runOnUiThread(new Runnable(){
         public void run() {
           Toast.makeText(getApplicationContext(), "Status = " + message.getBody() , Toast.LENGTH_LONG).show();
         }
       });
      

      这对我有用

      【讨论】:

        【解决方案6】:

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

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

        它很简单,非常适合我!

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

        【讨论】:

        • 这不是特定于 Xamarin 的 API,并且与这个关于 Android Native 的问题无关(没有 Xamarin 标签)吗?
        • 是的,你是对的。抱歉,我没注意到。但是使用 Xamarin 寻找错误解决方案的人正在登陆此页面。所以我把帖子放在这里,只是为了 Xamarin。
        • “设备”来自哪个程序集/命名空间?
        • @MJ33 它来自 Xamarin.Forms
        【解决方案7】:

        我在以下代码的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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2019-06-04
          • 1970-01-01
          相关资源
          最近更新 更多