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


981

以下异常是什么意思;我该如何解决?

这是代码:

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)

8
检查这个库compile 'com.shamanland:xdroid-toaster:0.0.5',它不需要runOnUiThread()Context变量,所有日常不见了!只是Toaster.toast(R.string.my_msg);在这里调用示例:github.com/shamanland/xdroid-toaster-example
Oleksii K.

120
多么愚蠢的错误消息!它本来就很简单-无法像从非UI线程触摸视图时那样,从非UI线程调用它。
Dheeraj Bhaskar 2014年

11
对于那些从不同的代码获得相同的异常消息的人:异常消息的意思是您正在通过尚未准备Looper的线程来调用代码。通常,这意味着您不是从UI线程调用,而是应该(OP的情况)-普通线程不准备Looper,但UI线程总是这样做。
Helin Wang

@OleksiiKropachov您提到的库的实现与执行runOnUiThread()非常相似。
Helin Wang

是的,但这是一个非常有用的包装器
Oleksii K.

Answers:


696

您是从辅助线程调用它的。您需要Toast.makeText()从主线程中调用(以及其他大多数与UI有关的函数)。例如,您可以使用处理程序。

在文档中查找与UI线程进行通信。简而言之:

// Set this up in the UI thread.

mHandler = new Handler(Looper.getMainLooper()) {
    @Override
    public void handleMessage(Message message) {
        // This is where you do your work in the UI thread.
        // Your worker tells you in the message what to do.
    }
};

void workerThread() {
    // And this is how you call it from the worker thread:
    Message message = mHandler.obtainMessage(command, parameter);
    message.sendToTarget();
}

其他选项:

您可以使用AsyncTask,它对于在后台运行的大多数事物都适用。您可以调用它的钩子来指示进度以及完成的时间。

您也可以使用Activity.runOnUiThread()


最初的问题(与AlertDialog无关)怎么办?
伊凡·G.

5
只需在Cleggy所说的话上加上我的两分钱。最好简短地说明您的意思(但作假),因为一个编码示例通常可以说出自己的意思。
cdata

5
一个完整的技术答案看到这个prasanta-paul.blogspot.kr/2013/09/...
tony9099

3
在几乎所有支持GUI的AFAIK编程语言中,如果直接用GUI更新/更改/显示/交互,都应该在程序的主线程上完成。
艾哈迈德(Ahmed)2014年

(and most other functions dealing with the UI)可从后台使用的UI函数的一个示例是android.support.design.widget.Snackbar-当不从UI线程调用时,其功能不会减弱。
S

852

您需要Toast.makeText(...)从UI线程调用:

activity.runOnUiThread(new Runnable() {
  public void run() {
    Toast.makeText(activity, "Hello", Toast.LENGTH_SHORT).show();
  }
});

这是从另一个(重复的)SO答案复制粘贴的。


13
好答案。这让我困惑了一段时间。只是要注意,我不需要该活动。在runOnUiThread之前。
Cen92

447

更新-2016

最好的选择是使用RxAndroid(用于特异性结合RxJava)用于PMVP给负责FO数据。

首先Observable从现有方法返回。

private Observable<PojoObject> getObservableItems() {
    return Observable.create(subscriber -> {

        for (PojoObject pojoObject: pojoObjects) {
            subscriber.onNext(pojoObject);
        }
        subscriber.onCompleted();
    });
}

像这样使用这个Observable-

getObservableItems().
subscribeOn(Schedulers.io()).
observeOn(AndroidSchedulers.mainThread()).
subscribe(new Observer<PojoObject> () {
    @Override
    public void onCompleted() {
        // Print Toast on completion
    }

    @Override
    public void onError(Throwable e) {}

    @Override
    public void onNext(PojoObject pojoObject) {
        // Show Progress
    }
});
}

-------------------------------------------------- -------------------------------------------------- ------------------------------

我知道我来晚了一点,但是可以了。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;
    }
});

7
正是我想要的。特别是第一个例子runOnUiThread
纳文(Navin)2013年

5
谢谢,使用了5年的Android编程,我不知道View还有方法post(Runnable)postDelayed(Runnable, long)!如此众多的处理程序徒劳无功。:)
Fenix Voltres 2015年

对于那些对Handler示例感到困惑的人:“ new Handler(callback)”绑定到的线程是什么?它绑定到创建处理程序的线程。
Helin Wang

1
为什么这是最好的选择
IgorGanapolsky '17

我使用doInBackground,我想找回ArrayList,但总是收到错误:无法在未调用Looper.prepare()的线程内创建处理程序。看到这是我的问题stackoverflow.com/questions/45562615/…但我无法从此处获得解答
WeSt

120

Toast.makeText()只能从Main / UI线程调用。Looper.getMainLooper()可帮助您实现:

new Handler(Looper.getMainLooper()).post(new Runnable() {
    @Override
    public void run() {
        Toast toast = Toast.makeText(mContext, "Something", Toast.LENGTH_SHORT);
    }
});

此方法的优点是您可以在没有活动或上下文的情况下使用它。


2
谢谢其他答案没有为我工作。我正在使用库糖记录来管理持久性。而且在它里面我没有活动。但这效果很好
cabaji99 '17

91

如果由于处理程序之前未准备Looper而看到runtimeException时,请尝试此操作。

Handler handler = new Handler(Looper.getMainLooper()); 

handler.postDelayed(new Runnable() {
  @Override
  public void run() {
  // Run your task here
  }
}, 1000 );

Handler是一个抽象类。这不会编译
Stealth Rabbi 2014年

2
@StealthRabbi从正确的名称空间导入处理程序,即android.os.Handler
NightFury 2015年

这可能不是问题。在调用类,句号中可能不存在弯针。
IgorGanapolsky '17

42

我遇到了同样的问题,这是我如何解决的问题:

private final class UIHandler extends Handler
{
    public static final int DISPLAY_UI_TOAST = 0;
    public static final int DISPLAY_UI_DIALOG = 1;

    public UIHandler(Looper looper)
    {
        super(looper);
    }

    @Override
    public void handleMessage(Message msg)
    {
        switch(msg.what)
        {
        case UIHandler.DISPLAY_UI_TOAST:
        {
            Context context = getApplicationContext();
            Toast t = Toast.makeText(context, (String)msg.obj, Toast.LENGTH_LONG);
            t.show();
        }
        case UIHandler.DISPLAY_UI_DIALOG:
            //TBD
        default:
            break;
        }
    }
}

protected void handleUIRequest(String message)
{
    Message msg = uiHandler.obtainMessage(UIHandler.DISPLAY_UI_TOAST);
    msg.obj = message;
    uiHandler.sendMessage(msg);
}

要创建UIHandler,您需要执行以下操作:

    HandlerThread uiThread = new HandlerThread("UIHandler");
    uiThread.start();
    uiHandler = new UIHandler((HandlerThread) uiThread.getLooper());

希望这可以帮助。


我尝试使用您的代码,但是我迷路了,不确定onCreate method在我的情况下如何从AsyncTask 调用或从AsyncTask 调用,请问您发布整个代码只是为了了解事情是如何工作的?
尼克·卡恩

2
那最后一行不应该读 uiHandler = new UIHandler(uiThread.getLooper()); 吗?
Beer Me

36

错误原因:

工作线程是用于执行后台任务的,除非您调用runOnUiThread之类的方法,否则您不能在工作线程中的UI上显示任何内容。如果您尝试在不调用runOnUiThread的情况下在UI线程上显示任何内容,则会出现一个java.lang.RuntimeException

因此,如果您处于工作线程中,activity但正在Toast.makeText()从中调用,请执行以下操作:

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

上面的代码可确保您在中显示Toast消息,UI thread因为您是在runOnUiThread方法内部调用Toast消息的。所以没有了java.lang.RuntimeException


23

那就是我所做的。

new Handler(Looper.getMainLooper()).post(new Runnable() {
    @Override
    public void run() {
        Toast(...);
    }
});

可视组件被“锁定”为来自外部线程的更改。因此,由于Toast在由主线程管理的主屏幕上显示内容,因此您需要在该线程上运行此代码。希望能有所帮助:)


我用了同样的方法。但是,由于Runnable的匿名内部类将隐含对Activity的引用,这是否会导致泄漏的可能性?
彼得·威廉姆斯

1
没关系:)出于安全考虑,只需使用getApplicationContext()或类似的方法。虽然我从来没有遇到过我所知道的代码有任何问题
伊兰

22

在执行以下操作之前,我一直收到此错误。

public void somethingHappened(final Context context)
{
    Handler handler = new Handler(Looper.getMainLooper());
    handler.post(
        new Runnable()
        {
            @Override
            public void run()
            {
                Toast.makeText(context, "Something happened.", Toast.LENGTH_SHORT).show();
            }
        }
    );
}

并将其设置为单例类:

public enum Toaster {
    INSTANCE;

    private final Handler handler = new Handler(Looper.getMainLooper());

    public void postMessage(final String message) {
        handler.post(
            new Runnable() {
                @Override
                public void run() {
                    Toast.makeText(ApplicationHolder.INSTANCE.getCustomApplication(), message, Toast.LENGTH_SHORT)
                        .show();
                }
            }
        );
    }

}

您在哪里使用Toaster?在您的第一个代码段中,它没有被使用...
IgorGanapolsky'2

1
这是我以前使用过的便利班Toaster.INSTANCE.postMessage(ResourceUtils.getString(R.string.blah));(冗长的我知道!我们稍后
再讲

那么ApplicationHolder.INSTANCE评估结果如何呢?
IgorGanapolsky '17

CustomApplicationset in的静态变量CustomApplication.onCreate(),考虑到应用程序在进程存在时始终存在,因此可以全局使用此上下文
EpicPandaForce

11
 runOnUiThread(new Runnable() {
            public void run() {
                Toast.makeText(mContext, "Message", Toast.LENGTH_SHORT).show();
            }
        });

2
这对我有用,我使用lambdarunOnUiThread(() -> { Toast toast = Toast.makeText(getApplicationContext(), "Message", Toast.LENGTH_SHORT); toast.show(); });
Black_Zerg,

11

出色的Kotlin解决方案:

runOnUiThread {
    // Add your ui thread code here
}

4
runOnUiThread是活动的一部分,即activity?.runOnUiThread { ... }
AtomicStrongForce

9

这是因为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

首先拨打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()

为什么这个答案被低估了?
DkPathak

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

当我的回调试图显示一个对话框时,我遇到了同样的问题。

我通过Activity中的专用方法-在Activity 实例成员级别上解决了该问题,该方法使用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
Handler handler2;  
HandlerThread handlerThread=new HandlerThread("second_thread");
handlerThread.start();
handler2=new Handler(handlerThread.getLooper());

现在handler2将使用与主线程不同的线程来处理消息。


1

要在线程中显示对话框或烤面包机,最简洁的方法是使用Activity对象。

例如:

new Thread(new Runnable() {
    @Override
    public void run() {
        myActivity.runOnUiThread(new Runnable() {
            public void run() {
                myActivity.this.processingWaitDialog = new ProgressDialog(myActivity.this.getContext());
                myActivity.this.processingWaitDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
                myActivity.this.processingWaitDialog.setMessage("abc");
                myActivity.this.processingWaitDialog.setIndeterminate(true);
                myActivity.this.processingWaitDialog.show();
            }
        });
        expenseClassify.serverPost(
                new AsyncOperationCallback() {
                    public void operationCompleted(Object sender) {
                        myActivity.runOnUiThread(new Runnable() {
                            public void run() {
                                if (myActivity.this.processingWaitDialog != null 
                                        && myActivity.this.processingWaitDialog.isShowing()) {
                                    myActivity.this.processingWaitDialog.dismiss();
                                    myActivity.this.processingWaitDialog = null;
                                }
                            }
                        }); // .runOnUiThread(new Runnable()
...

0

Toast,AlertDialogs需要在UI线程上运行,您可以使用Asynctask在Android开发中正确使用它们。但是在某些情况下,我们需要自定义超时,因此我们使用Threads,但是在线程中,我们不能使用Toast,像我们使用Alertdialogs因此我们需要单独的Handler 来弹出它们。

public void onSigned() {
    Thread thread = new Thread(){
        @Override
        public void run() {
            try{
                sleep(3000);
                Message message = new Message();
                message.what = 2;
                handler.sendMessage(message);
            } catch (Exception e){
                e.printStackTrace();
            }
        }
    };
    thread.start();
}

在上面的示例中,我想在3秒内使线程休眠,然后在要显示Toast消息之后,在您的主线程实现处理程序中显示该消息。

handler = new Handler() {
       public void handleMessage(Message msg) {
           switch(msg.what){
              case 1:
              Toast.makeText(getActivity(),"cool",Toast.LENGTH_SHORT).show();
              break;
           }
           super.handleMessage(msg);
       }
};

我在这里使用了切换用例,因为如果您需要以相同的方式显示不同的消息,则可以在Handler类中使用切换用例...希望这对您有帮助


0

当从任何后台线程调用主线程上的某些内容时,通常会发生这种情况。让我们来看一个示例。

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


@Override
protected Void doInBackground(Void... voids) {
        textView.setText("Any Text");
        return null;
    }
}

在上面的示例中,我们通过doInBackground()方法在主UI线程中的textview上设置文本,该方法仅在工作线程上运行。


0

我遇到了同样的问题,我仅通过将Toast放入Asynctask <>的onPostExecute()覆盖函数中就可以解决此问题,并且可以正常工作。


-2

我使用以下代码显示来自非主线程“上下文”的消息,

@FunctionalInterface
public interface IShowMessage {
    Context getContext();

    default void showMessage(String message) {
        final Thread mThread = new Thread() {
            @Override
            public void run() {
                try {
                    Looper.prepare();
                    Toast.makeText(getContext(), message, Toast.LENGTH_LONG).show();
                    Looper.loop();
                } catch (Exception error) {
                    error.printStackTrace();
                    Log.e("IShowMessage", error.getMessage());
                }
            }
        };
        mThread.start();
    }
}

然后使用以下内容:

class myClass implements IShowMessage{

  showMessage("your message!");
 @Override
    public Context getContext() {
        return getApplicationContext();
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.