【Android】報錯:Can't create handler inside thread that has not called Looper.prepare()

Toast.makeText(mContext, "msg", Toast.LENGTH_SHORT).show();

在子線程裏創建 Toast 時出現報錯:

java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare().
在沒有調用 Looper.prepare() 的線程裏,無法創建 handler

原因是修改 UI 需要在主線程裏。

1. Thread

因爲線程需要 Looper 來循環處理消息,但是子線程中默認是沒有 Looper 的,查看一下 Looper 的源碼,類註釋如下:

/**
  * Class used to run a message loop for a thread.  Threads by default do
  * not have a message loop associated with them; to create one, call
  * {@link #prepare} in the thread that is to run the loop, and then
  * {@link #loop} to have it process messages until the loop is stopped.
  *
  * <p>Most interaction with a message loop is through the
  * {@link Handler} class.
  *
  * <p>This is a typical example of the implementation of a Looper thread,
  * using the separation of {@link #prepare} and {@link #loop} to create an
  * initial Handler to communicate with the Looper.
  *
  * <pre>
  *  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();
  *      }
  *  }</pre>
  */

註釋裏說明了線程默認是沒有 Looper 的,需要在線程裏手動調用 Looper.prepare() 來創建 Looper,並且調用 Looper.loop() 來遍歷消息。並且還附帶了一個例子。

所以我們直接按照官方給的例子調用即可:

new Thread(new Runnable() {
    @Override
    public void run() {
        Looper.prepare();
        Toast.makeText(mContext, "msg", Toast.LENGTH_SHORT).show();
        Looper.loop();
    }
}).start();

2. Handler

如果直接在子線程裏創建 new Handler(),也會報同樣的錯誤,因爲子線程默認是沒有 Looper 的。
可以使用 public Handler(Looper looper) 構造方法創建 Handler,傳遞主線程的 Looper

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

3. runOnUiThread

如果是在 Activity 裏,可以使用 runOnUiThread

runOnUiThread(new Runnable() {
    @Override
    public void run() {
        Toast.makeText(mContext, "msg", Toast.LENGTH_SHORT).show();
    }
});

或者

if (mContext instanceof Activity) {
    ((Activity) mContext).runOnUiThread(new Runnable() {
        @Override
        public void run() {
            Toast.makeText(mContext, "msg", Toast.LENGTH_SHORT).show();
        }
    });

參考

  1. Can’t create handler inside thread that has not called Looper.prepare()
  2. Android 子線程創建handler
  3. Android 帶你徹底理解跨線程通信之Handler機制
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章