Can’t create handler inside thread that has not called Looper.prepare()
毕业后在深圳一家公司工作,目前公司在做的是与android相关的项目,有Android源码。
这几周,出现几次同事在使用Handler的时候,在调用时抛出RuntimeException的错误,异常消息如下:Can’t create handler inside thread that has not called Looper.prepare() ,代码是在Handler handler = new Handler()的这一行。
以下内容是我的理解,如果理解有误,还请指出以便修正,以免我误导到他人。
那么这个问题要怎么解决呢?
从异常信息来看,是说在线程里创建Handler对象前没有调用Looper.prepare()方法。
首先,我们得知道,Looper是用来为线程创建一个消息循环的。而在Thread对象当中,默认是不与Looper绑定的,也就是默认是没有Looper对象的。
Android当中,对线程和消息队列进行了封装,即Handler类。它是一个可以用来处理循环消息队列的线程对象,在其构造方法中,部分代码如下(正好公司项目里有android源码,就看下代码吧,在frameworkd/base/core/java/android/os/下):
110 public Handler() {
111 this(null, false);
112 }
...
144 public Handler(Looper looper, Callback callback) {
145 this(looper, callback, false);
146 }
...
185 public Handler(Callback callback, boolean async) {
186 if (FIND_POTENTIAL_LEAKS) {
187 final Class<? extends Handler> klass = getClass();
188 if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&
189 (klass.getModifiers() & Modifier.STATIC) == 0) {
190 Log.w(TAG, "The following Handler class should be static or leaks might occur: " +
191 klass.getCanonicalName());
192 }
193 }
194
195 mLooper = Looper.myLooper();
196 if (mLooper == null) {
197 throw new RuntimeException(
198 "Can't create handler inside thread that has not called Looper.prepare()");
199 }
200 mQueue = mLooper.mQueue;
201 mCallback = callback;
202 mAsynchronous = async;
203 }
它通过调用Looper.myLooper()来获取当前线程的Looper对象,如果获取的Looper对象为null,就抛出这个异常了。这里顺便说一下一个误区,我发现有一些人会以为Handler就是一个多了消息队列的Thread,以为Handler handler = new Handler(),会创建一个新线程。其实并不是的,上面已经提到,Handler通过无参构造方法构造出来的对象,使用的是当前线程的Looper对象,所以通过它执行的代码,其实是在当前进程执行。
既然如此,那么我就想到解决的方法了。就是通过HandlerThread类。代码如下:
HandlerThread t = new HandlerThread("test");
t.start();
Handler handler = new Handler(t.getLooper());
果然问题解决。这里构造的Handler对象,它是在一个新的线程当中执行的。
现在再来看看,Looper.myLooper()方法,代码如下:
162 public static Looper myLooper() {
163 return sThreadLocal.get();
164 }
通过调用ThreadLocal对象的get()方法来返回Looper对象。
声明代码在Looper类当中,如下:
56 // sThreadLocal.get() will return null unless you've called prepare().
57 static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
上面提到,Looper对象是用来为线程执行一个消息循环的,而本身具备消息队列的Handler需要有一个Looper对象,所以它会提示你要在创建Handler对象前调用Looper.prepare()方法。该方法代码如下:
66 /** Initialize the current thread as a looper.
67 * This gives you a chance to create handlers that then reference
68 * this looper, before actually starting the loop. Be sure to call
69 * {@link #loop()} after calling this method, and end it by calling
70 * {@link #quit()}.
71 */
72 public static void prepare() {
73 prepare(true);
74 }
75
76 private static void prepare(boolean quitAllowed) {
77 if (sThreadLocal.get() != null) {
78 throw new RuntimeException("Only one Looper may be created per thread");
79 }
80 sThreadLocal.set(new Looper(quitAllowed));
81 }
该方法创建一个Looper对象,再存入ThreadLocal对象。调用它之后,再在Thread中创建Handler对象,也就不会有获取到的Looper对象为null的异常了。
但是在这里我还有疑问,疑问如下:
1,ThreadLocal是什么?
2,Handler和Looper如何实现消息循环队列?
3,HandlerThread在什么时候创建一个Looper对象?
于是继续探索。
查java api可知,ThreadLocal提供了线程局部变量,所以通过它来保存当前线程的消息循环队列Looper对象是再合适不过的了。
对于第二个问题,我们可以看Looper类的声明,代码如下:
53 public class Looper {
54 private static final String TAG = "Looper";
55
56 // sThreadLocal.get() will return null unless you've called prepare().
57 static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
58 private static Looper sMainLooper; // guarded by Looper.class
59
60 final MessageQueue mQueue;
61 final Thread mThread;
62 volatile boolean mRun;
63
64 private Printer mLogging;
65
...
}
里面有一个消息队列,要发送的消息都通过Handler被加入在里面。而在Looper类当中,则通过loop方法里的死循环,不断地去读这个消息队列,代码如下:
107 /**
108 * Run the message queue in this thread. Be sure to call
109 * {@link #quit()} to end the loop.
110 */
111 public static void loop() {
112 final Looper me = myLooper();
113 if (me == null) {
114 throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
115 }
116 final MessageQueue queue = me.mQueue;
117
118 // Make sure the identity of this thread is that of the local process,
119 // and keep track of what that identity token actually is.
120 Binder.clearCallingIdentity();
121 final long ident = Binder.clearCallingIdentity();
122
123 for (;;) {
124 Message msg = queue.next(); // might block
125 if (msg == null) {
126 // No message indicates that the message queue is quitting.
127 return;
128 }
129
130 // This must be in a local variable, in case a UI event sets the logger
131 Printer logging = me.mLogging;
132 if (logging != null) {
133 logging.println(">>>>> Dispatching to " + msg.target + " " +
134 msg.callback + ": " + msg.what);
135 }
136
137 msg.target.dispatchMessage(msg);
138
139 if (logging != null) {
140 logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
141 }
142
143 // Make sure that during the course of dispatching the
144 // identity of the thread wasn't corrupted.
145 final long newIdent = Binder.clearCallingIdentity();
146 if (ident != newIdent) {
147 Log.wtf(TAG, "Thread identity changed from 0x"
148 + Long.toHexString(ident) + " to 0x"
149 + Long.toHexString(newIdent) + " while dispatching to "
150 + msg.target.getClass().getName() + " "
151 + msg.callback + " what=" + msg.what);
152 }
153
154 msg.recycle();
155 }
156 }
那么,哪里会调用Looper.loop()方法呢?我在frameworks/base/core/java/android/os目录下搜了一下,结果如下:
phoenix@rtkPhoenix:~/branch/workspace/kernel/android/JB/frameworks/base/core/java/android$ jgrep "loop()"
./app/ActivityThread.java:5039: Looper.loop();
./os/Looper.java:49: * Looper.loop();
./os/Looper.java:69: * {@link #loop()} after calling this method, and end it by calling
./os/Looper.java:111: public static void loop() {
./os/HandlerThread.java:60: Looper.loop();
./webkit/WebSyncManager.java:90: Looper.loop();
./webkit/WebCoreThreadWatchdog.java:224: Looper.loop();
./webkit/WebViewCore.java:812: Looper.loop();
主线程的Looper对象的loop()通过ActivityThread来调用,ActivityThread是程序的入口,其提供了main静态方法。当loop()被调用之后,它便通过一个死循环不断读取消息队列中的对象(调用 MessageQueue.next()方法),直到它读取到的Message对象为null。通过调用Looper.quit()方法可以退出,因为该方法会调用消息队列的quit()方法,而消息队列MessageQueue对象当退出时会在next()方法中返回null。
而对于HandlerThread调用loop()方法,将在下面谈。
下面说一下HandlerThread类。HandlerThread类用来开启一个含Looper对象的线程。它继承自Thread,在run()方法中调用Looper.prepare()来初始化Looper对象。代码如下:
51 public void run() {
52 mTid = Process.myTid();
53 Looper.prepare();
54 synchronized (this) {
55 mLooper = Looper.myLooper();
56 notifyAll();
57 }
58 Process.setThreadPriority(mPriority);
59 onLooperPrepared();
60 Looper.loop();
61 mTid = -1;
62 }
在run方法中调用Looper.prepare(),所以这也就是在上面代码中,为什么对HandlerThread对象调用start()方法之后,才去获得它的looper来创建Handler对象。而HandlerThread对象也在run方法结束前,调用Looper.loop()方法,开始消息循环。
分享到:
相关推荐
在Android开发中,Looper、Handler和HandlerThread是三个非常重要的组件,它们构成了Android消息处理机制的基础,用于在主线程中处理来自其他线程的消息。理解并熟练运用这三个组件,对于编写高效、响应迅速的...
在Android开发中,理解核心组件如Looper、Handler和HandlerThread对于构建高效且响应式的用户界面至关重要。这些组件共同协作,确保UI线程不被阻塞,从而提供流畅的用户体验。 首先,主线程(也称为UI线程)是...
Android 多线程 Looper Handler Android 操作系统中,多线程是一种常见的编程技术,通过使用多线程,可以提高应用程序的响应速度和效率。在 Android 中,多线程可以分为两种:有消息循环的线程和没有消息循环的线程...
使用HandlerThread的Looper创建Handler实例,这样处理的消息会在这个HandlerThread中执行: ```java Handler handler = new Handler(looper) { @Override public void handleMessage(Message msg) { // 处理...
在主线程即UI线程外,新建一个Looper线程,并用Messenger和Handler来处理message和posted runnable。程序中,在负线程中默认加了一个3s的线程等来,来帮助理解sent message和post runnable之间的同步机制。所以在按...
总之,`Looper`是Android系统中的核心组件之一,它与`Handler`和`Message`共同构建了强大的异步处理机制。理解和熟练运用`Looper`,对于开发高效、流畅的Android应用程序至关重要。通过阅读指定的博客文章,你可以更...
本文将深入探讨Android中的三种主要线程模式:Handler、Thread以及Looper,并结合源码分析它们的工作原理。 首先,我们来理解一下Android应用的基本运行环境。Android系统默认在主线程(UI线程)中执行所有的用户...
在Android应用开发中,Handler、Looper和Message是实现线程间通信的重要机制,尤其是在主线程与工作线程之间同步数据和执行UI更新时。Handler、Looper和Message三者结合使用,构建了一个消息处理系统,使得非UI线程...
`HandlerThread`是Android中的一个内置类,它继承自`Thread`,并添加了`Looper`和`Handler`的支持。通常,当我们需要创建一个新的工作线程来处理耗时任务时,需要手动初始化`Looper`和`Handler`,而`HandlerThread`...
`HandlerThread`是一个内置`Looper`的线程,简化了在新线程中使用`Handler`的流程。只需创建`HandlerThread`并启动,然后在其`Looper`上创建`Handler`即可。 7. **MessageQueue和Message** `MessageQueue`是`...
在Android系统中,`Handler`、`Looper` 和 `Message` 三者一起构成了消息传递机制,使得我们可以在线程间安全地交换数据和执行异步操作。 首先,我们来看一下`HandlerThread`的基本用法: ```java // 创建...
,这个题目有点意思,对于很多人来说,可能对Thread和Handler很熟悉,主要涉及到Android的消息机制(Handler、Message、Looper、MessageQueue),详见《 从Handler.post(Runnable r)再一次梳理Android的消息机制(以及...
`Looper` 是 Android 中用于消息处理的核心类,它维护了一个消息队列(MessageQueue)并负责不断地从队列中取出消息并分发给相应的`Handler`进行处理。因此,`HandlerThread` 可以看作是一个拥有自己消息循环的线程...
本篇文章将深入探讨Android中消息处理的核心组件——Looper、MessageQueue、Handler以及消息循环的工作原理。理解这些概念对于构建响应迅速、用户友好的应用程序至关重要。 #### 二、核心概念 ##### 1. Looper ...
Handler和Looper是Android系统中两个非常重要的组件,它们之间相互关联,共同实现了Android系统中的消息处理机制。在本文中,我们将对Handler和Looper进行详细的解释,并探讨它们在Android系统中的作用。 一、...
Looper用于封装了android线程中的消息循环,默认情况下一个线程是不存在消息循环(message loop)的,需要调用Looper.prepare()来给线程创建一个消息循环,调用Looper.loop()来使消息循环起作用,使用Looper....
综上所述,Message、MessageQueue、Looper和Handler这四个组件共同构成了Android应用程序中处理消息的基本机制。通过它们之间的相互协作,使得应用程序能够在不同的线程间高效地传递和处理消息,从而实现了复杂的...
在Android系统中,主线程默认已经有一个运行的Looper,但如果我们需要在自定义线程中使用Handler进行消息通信,就需要手动创建并启动Looper。这是通过调用Looper.prepare()初始化Looper,然后调用Looper.loop()进入...
Android Looper 是 Android 操作系统中的一种机制,用于在单线程中创建消息循环,以便实现线程之间的通信和消息处理。Looper 对象通过 MessageQueue 来存放消息和事件,每个线程只能有一个 Looper 对象,-...
Handler是Android中处理消息和调度任务的对象,它通常与Looper和Message配合使用,实现线程间的通信。主线程中默认有一个Looper对象,负责接收和分发Message,而Handler则用来发送Message到Looper,从而实现主线程的...