安卓:为什么我不能在新线程中创建一个handler

31

我在创建新线程中的处理程序时遇到了问题。这是我的代码:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    new Thread(new Runnable() {
        public void run() {
            Handler handler = new Handler();
        }
    }).start();
}

但是它报错了!能否有人解释一下这个错误?非常感谢!

以下是我错误的详细信息:

09-17 18:05:29.484: E/AndroidRuntime(810): FATAL EXCEPTION: Thread-75
09-17 18:05:29.484: E/AndroidRuntime(810): java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
09-17 18:05:29.484: E/AndroidRuntime(810):  at android.os.Handler.<init>(Handler.java:197)
09-17 18:05:29.484: E/AndroidRuntime(810):  at android.os.Handler.<init>(Handler.java:111)
09-17 18:05:29.484: E/AndroidRuntime(810):  at com.example.handler.MainActivity$1.run(MainActivity.java:57)
09-17 18:05:29.484: E/AndroidRuntime(810):  at java.lang.Thread.run(Thread.java:856)

你收到了什么错误? - Prmths
你在你的线程中实例化了一个 Handler,但你从来没有调用它的任何方法。你确定这是你想要的吗? - Gray
我想在Handler中编写一些方法,但是在实例化Handler时出现了错误,因此为了简单起见,我只在这里发布了出错的代码。当我运行这段代码时,应用程序崩溃了。 - nguyenbkcse
不要创建一个线程并为该线程设置Looper,而是阅读HandlerThread类的相关内容。 - pskink
@user2781314 崩溃了!请发布堆栈跟踪。 - Raghunandan
4个回答

89

您也可以像这样使用HandlerThread

HandlerThread thread = new HandlerThread("MyHandlerThread");
thread.start();
Handler handler = new Handler(thread.getLooper());

HandlerThread拥有与其相关联的Looper,因此这不会抛出异常。


10
不要忘记在工作线程中停止Looper,否则它会占用该线程并阻止其关闭。完成后,请使用thread.quit()或thread.quitSafely()。 - Kirill Karmazin

23

run方法返回后,线程的生命周期就结束了。但是由于您正在此线程中创建一个Handler,因此Handler需要该线程保持运行状态以接收消息并处理它们。

为了实现这一点,run方法不应退出。因此,您需要一个Looper来无限期地等待并处理到达Handler的消息。

new Thread(new Runnable() {
        public void run() {
            Looper.prepare();
            Handler handler = new Handler();
            Looper.loop();
        }
    }).start();

15

简短回答:因为您尝试附加处理程序的线程没有Looper。因此,Handler类的构造函数会抛出异常。相反,您可以使用HandlerThread类,这是Android框架提供的一个方便的类。

请阅读以下内容了解底层发生的情况。

首先让我们分别讨论所有部分。

  1. 线程:

a. 线程只是一种执行流。默认情况下,线程应该仅执行其可运行项(如果提供)或调用其run方法。调用new Thread.start()后,当run方法执行所有语句时,线程将死亡并被垃圾回收。

b. Android中有一个名为Looper的概念。它基本上使线程成为阻塞线程。简单地说,它只是不允许线程死亡。它进入阻塞状态并等待更多消息以便再次恢复其执行。

以下是如何设置阻塞线程即带有looper的线程。

  new Thread(new Runnable() {
    public void run() {
        Looper.prepare();
        Looper.loop();
    }
}).start();

这里创建了一个线程,它不会在执行Looper.loop()语句后立即停止。相反,它会循环并进入阻塞状态。现在您一定会问什么是阻塞状态,线程如何走出阻塞状态?我现在该如何释放这个线程?这就是Handler的作用

  1. Handler:

a. 它总是附加到创建它的线程的Looper上。

b. 然后处理它所附加的线程的那些消息。

将线程和Handler结合起来。

new Thread(new Runnable() {
        public void run() {
            Looper.prepare();
            handler = new Handler();
            Looper.loop();
        }
    }).start();

a. 如何将处理程序附加到这个新创建的线程。 b. 按照阻塞循环线程的方式设置了该线程,所以它不会终止。

现在,我们可以进行以下操作: 1. 在此处理程序上发送消息。 2. 在此处理程序上发布可运行项。

两者都将在附加的线程上执行。

您可以选择扩展Handler类并实现handleMessage(Message msg)方法,或者只需在处理程序类的构造函数中提供Handler.Callback的实现。在任一方式中,handleMessage(Messages msg)将在附加的线程上调用。

要退出线程,您可以发送特定类型的消息类型,并在接收到该消息时仅调用Looper.myLooper()。quit()

class LooperThread extends Thread {
   public Handler mHandler;

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

      mHandler = new Handler() {
          public void handleMessage(Message msg) {
              // process incoming messages here
              if(msg.what == -1){
              Looper.myLooper().quit();
              }
          }
      };

      Looper.loop();
   }
}

我希望它有助于理解整体概念。


5

处理程序需要在 Looper 线程中初始化,或者在给它传入 Looper。

根据您想要的操作,您可以设置线程为 Looper,方法如下:

new Thread(new Runnable() {
    public void run() {
        Looper.prepare();
        mHandler = new Handler();
        Looper.loop();
    }
}).start();

由于Looper位于后台线程中,所以您无法更新用户界面。您可以选择从另一个线程中为Handler提供Looper - 在此示例中,可使用Handler更新用户界面:

new Thread(new Runnable() {
    public void run() {
        Handler handler = new Handler(Looper.getMainLooper());
    }
}).start();

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