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

1211
以下异常的意思是什么?我该如何修复它?
这是代码:
Toast toast = Toast.makeText(mContext, "Something", Toast.LENGTH_SHORT);

这是异常情况:

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.<init>(Toast.java:68)
     at android.widget.Toast.makeText(Toast.java:231)

13
请检查这个库 compile 'com.shamanland:xdroid-toaster:0.0.5',它不需要 runOnUiThread()Context 变量,所有例行程序都已消失!只需调用 Toaster.toast(R.string.my_msg); 就可以了。这里有一个示例:https://github.com/shamanland/xdroid-toaster-example - Oleksii K.
181
多么愚蠢的错误消息!它本可以简单明了,比如"不能在非UI线程中调用此函数,就像当视图从非UI线程被触摸时一样。" - Dheeraj Bhaskar
19
对于那些在不同代码中遇到相同异常消息的人:该异常消息的意思是您正在通过未准备好Looper的线程调用代码。通常这意味着您没有从UI线程调用它,但是您应该这样做(在OP的情况下)-普通线程不会准备Looper,但UI线程总是会准备。 - Helin Wang
1
@OleksiiKropachov,你提到的库的实现方式与执行runOnUiThread()非常相似。 - Helin Wang
1
是的,但它是一个非常有用的封装。 - Oleksii K.
@DheerajBhaskar 我不会轻易评判一个专业开发者的决定,当你不了解这个异常还包含其他情况时,这样做是愚蠢的。对我来说,我遇到了与UI线程无关的相同异常,谷歌自己提供的用于应用内购买的IABHandler,在没有调用Looper.getMainLooper()的情况下使用了新的Handler,出于一些“愚蠢”的原因;当我正常使用它时没有任何问题,但当我在Executors线程池中使用它时,就会出现相同的异常,从未在其中使用任何与UI相关的方法。 - Alireza Jamali
30个回答

14

精彩的 Kotlin 解决方案:

runOnUiThread {
    // Add your ui thread code here
}

7
runOnUiThread是Activity的一部分,即 activity?.runOnUiThread { ... } - AtomicStrongForce

14

首先调用Looper.prepare(),然后调用Toast.makeText().show(),最后调用Looper.loop(),如下所示:

Looper.prepare() // to be able to make toast
Toast.makeText(context, "not connected", Toast.LENGTH_LONG).show()
Looper.loop()

1
为什么这个答案被低估了呢? - DkPathak
我不知道当前的使用情况,但是在我使用一个包含内部活动的可运行jar文件时,这个方法帮了我很大的忙。 - John Lord

11

这是因为Toast.makeText()是在工作线程中调用的。它应该像这样从主UI线程调用

runOnUiThread(new Runnable() {
      public void run() {
        Toast toast = Toast.makeText(mContext, "Something", Toast.LENGTH_SHORT);
      }
 });

8
ChicoBird的答案对我起了作用。我所做的唯一更改是在创建UIHandler时,我必须这样做。
HandlerThread uiThread = new HandlerThread("UIHandler");

Eclipse拒绝接受其他任何东西。我想这很有道理。

此外,uiHandler显然是在某个地方定义的类全局变量。我仍然不能声称理解Android是如何实现这一点以及发生了什么,但我很高兴它能够正常工作。现在我将继续研究它,看看是否可以理解Android正在做什么以及为什么必须经过所有这些步骤。感谢ChicoBird的帮助。


6

协程会完美地解决这个问题

CoroutineScope(Job() + Dispatchers.Main).launch {
                        Toast.makeText(context, "yourmessage",Toast.LENGTH_LONG).show()}

5

Java 8

new Handler(Looper.getMainLooper()).post(() -> {
    // Work in the UI thread

}); 

Kotlin

Handler(Looper.getMainLooper()).post{
    // Work in the UI thread
}

GL


5

针对RxJava和RxAndroid用户:

public static void shortToast(String msg) {
    Observable.just(msg)
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(message -> {
                Toast.makeText(App.getInstance(), message, Toast.LENGTH_SHORT).show();
            });
}

这些括号是不必要的。 - Borja

4

当我的回调函数尝试显示对话框时,我遇到了相同的问题。

我通过在活动实例成员级别上使用runOnUiThread(..)来解决这个问题。

public void showAuthProgressDialog() {
    runOnUiThread(new Runnable() {
        @Override
        public void run() {
            mAuthProgressDialog = DialogUtil.getVisibleProgressDialog(SignInActivity.this, "Loading ...");
        }
    });
}

public void dismissAuthProgressDialog() {
    runOnUiThread(new Runnable() {
        @Override
        public void run() {
            if (mAuthProgressDialog == null || ! mAuthProgressDialog.isShowing()) {
                return;
            }
            mAuthProgressDialog.dismiss();
        }
    });
}

2

我遇到了同样的问题,但这段代码现在对我来说运行良好。
以下是我用于在后台和UI线程中执行任务的代码示例。
请注意looper的使用:

new Thread(new Runnable() {
    @Override
    public void run() {
    Looper.prepare();
                                
    // your Background Task here

    runOnUiThread(new Runnable() {
        @Override
        public void run() {

        // update your UI here      
                                
        Looper.loop();
        }
    });
    }
}).start();

2
Handler handler2;  
HandlerThread handlerThread=new HandlerThread("second_thread");
handlerThread.start();
handler2=new Handler(handlerThread.getLooper());

现在,handler2将使用与主线程不同的线程来处理消息。"最初的回答"。

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