初级程序员使用handler时有时会出现异常。Can’t create handler inside thread that has not called Looper.prepare()
为什么呢。
在core/java/android/os/Handler.java 中
@hide*/publicHandler(@Nullable Callback callback,boolean async){if(FIND_POTENTIAL_LEAKS){final Class<?extendsHandler> klass =getClass();if((klass.isAnonymousClass()|| klass.isMemberClass()|| klass.isLocalClass())&&(klass.getModifiers()& Modifier.STATIC)==0){
Log.w(TAG,"The following Handler class should be static or leaks might occur: "+
klass.getCanonicalName());}}
mLooper = Looper.myLooper();if(mLooper == null){thrownewRuntimeException("Can't create handler inside thread "+ Thread.currentThread()+" that has not called Looper.prepare()");}
mQueue = mLooper.mQueue;
mCallback = callback;
mAsynchronous = async;}
/**
* Returns a new {@link android.os.Message Message} from the global message pool. More efficient than
* creating and allocating new instances. The retrieved message has its handler set to this instance (Message.target == this).
* If you don't want that facility, just call Message.obtain() instead.
*/@NonNullpublicfinal Message obtainMessage(){return Message.obtain(this);}
在Handler源码中我们看到其Looper是用Looper.myLooper获取的,这里looper相当于消息的传送带,不断的循环检测是否有消息发过来。
在Looper类文件的开头我们就看到了谷歌给我们的注释和一个例子,清楚的说明了普通现场默认是没有looper的,需要手动创建,然后创建的方法就是Looper.prepare(),然后下面的例子也清楚的解释了上节中报错信息如何防止,Can’t create handler inside thread that has not called Looper.prepare()。然后启动looper循环用Looper.loop(),以启动消息循环,直到loop被停止。
/**
* 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>
*/
/** Initialize the current thread as a looper.
* This gives you a chance to create handlers that then reference
* this looper, before actually starting the loop. Be sure to call
* {@link #loop()} after calling this method, and end it by calling
* {@link #quit()}.
*/publicstaticvoidprepare(){prepare(true);}privatestaticvoidprepare(boolean quitAllowed){if(sThreadLocal.get()!= null){thrownewRuntimeException("Only one Looper may be created per thread");}
sThreadLocal.set(newLooper(quitAllowed));}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
如果新建线程,第一次创建looper,当然会走到set那,然后看变量初始化
ThreadLocal sThreadLocal = new ThreadLocal()
通过网络查询我们知道,ThreadLocal类提供了线程局部 (thread-local) 变量,多线程数据不能共享。
而其set方法源码为
/**
* Return the Looper object associated with the current thread. Returns
* null if the calling thread is not associated with a Looper.
*/publicstatic@Nullable Looper myLooper(){return sThreadLocal.get();}
/**
* Run the message queue in this thread. Be sure to call
* {@link #quit()} to end the loop.
*/publicstaticvoidloop(){final Looper me =myLooper();if(me == null){thrownewRuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");}final MessageQueue queue = me.mQueue;.......for(;;){
Message msg = queue.next();// might blockif(msg == null){// No message indicates that the message queue is quitting.return;}........long origWorkSource = ThreadLocalWorkSource.setUid(msg.workSourceUid);try{
msg.target.dispatchMessage(msg);if(observer != null){
observer.messageDispatched(token, msg);}
dispatchEnd = needEndTime ? SystemClock.uptimeMillis():0;}catch(Exception exception){.........
/**
* Low-level class holding the list of messages to be dispatched by a
* {@link Looper}. Messages are not added directly to a MessageQueue,
* but rather through {@link Handler} objects associated with the Looper.
*
* <p>You can retrieve the MessageQueue for the current thread with
* {@link Looper#myQueue() Looper.myQueue()}.
*/publicfinalclassMessageQueue{
1
2
3
4
5
6
7
8
9
看源码的开始部分,没那么好看,很多和native code交互的方法和变量,
@UnsupportedAppUsage
Message mMessages;@UnsupportedAppUsageprivatefinal ArrayList<IdleHandler> mIdleHandlers =newArrayList<IdleHandler>();private SparseArray<FileDescriptorRecord> mFileDescriptorRecords;private IdleHandler[] mPendingIdleHandlers;privateboolean mQuitting;// Indicates whether next() is blocked waiting in pollOnce() with a non-zero timeout.privateboolean mBlocked;.....privatenativestaticlongnativeInit();privatenativestaticvoidnativeDestroy(long ptr);@UnsupportedAppUsageprivatenativevoidnativePollOnce(long ptr,int timeoutMillis);/*non-static for callbacks*/privatenativestaticvoidnativeWake(long ptr);privatenativestaticbooleannativeIsPolling(long ptr);privatenativestaticvoidnativeSetFileDescriptorEvents(long ptr,int fd,int events);
@UnsupportedAppUsage
Message next(){// Return here if the message loop has already quit and been disposed.// This can happen if the application tries to restart a looper after quit// which is not supported.finallong ptr = mPtr;if(ptr ==0){return null;}int pendingIdleHandlerCount =-1;// -1 only during first iterationint nextPollTimeoutMillis =0;for(;;){if(nextPollTimeoutMillis !=0){
Binder.flushPendingCommands();}nativePollOnce(ptr, nextPollTimeoutMillis);synchronized(this){// Try to retrieve the next message. Return if found.finallong now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;if(msg != null && msg.target == null){// Stalled by a barrier. Find the next asynchronous message in the queue.do{
prevMsg = msg;
msg = msg.next;}while(msg != null &&!msg.isAsynchronous());}if(msg != null){if(now < msg.when){// Next message is not ready. Set a timeout to wake up when it is ready.
nextPollTimeoutMillis =(int) Math.min(msg.when - now, Integer.MAX_VALUE);}else{// Got a message.
mBlocked =false;if(prevMsg != null){
prevMsg.next = msg.next;}else{
mMessages = msg.next;}
msg.next = null;if(DEBUG) Log.v(TAG,"Returning message: "+ msg);
msg.markInUse();return msg;}}else{// No more messages.
nextPollTimeoutMillis =-1;}// Process the quit message now that all pending messages have been handled.if(mQuitting){dispose();return null;}...........}}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
首先,在其 synchronized (this) 代码块中,保证messagequeue的队列不被多线程更改。然后获取下一条消息时,先获取当前时间final long now = SystemClock.uptimeMillis();我们可以想到前边handler中各种sendattime或者postdelay等消息也带着时间参数。
这里看到检测如果msg不为空,并且msg.target也不为空,这里又出现了target变量,什么东西,我们继续看。
if(msg != null && msg.target == null){// Stalled by a barrier. Find the next asynchronous message in the queue.do{
prevMsg = msg;
msg = msg.next;}while(msg != null &&!msg.isAsynchronous());}
booleanenqueueMessage(Message msg,long when){if(msg.target == null){thrownewIllegalArgumentException("Message must have a target.");}if(msg.isInUse()){thrownewIllegalStateException(msg +" This message is already in use.");}synchronized(this){if(mQuitting){
IllegalStateException e =newIllegalStateException(
msg.target +" sending message to a Handler on a dead thread");
Log.w(TAG, e.getMessage(), e);
msg.recycle();returnfalse;}
msg.markInUse();
msg.when = when;
Message p = mMessages;boolean needWake;if(p == null || when ==0|| when < p.when){// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;}else{// Inserted within the middle of the queue. Usually we don't have to wake// up the event queue unless there is a barrier at the head of the queue// and the message is the earliest asynchronous message in the queue.
needWake = mBlocked && p.target == null && msg.isAsynchronous();
Message prev;for(;;){
prev = p;
p = p.next;if(p == null || when < p.when){break;}if(needWake && p.isAsynchronous()){
needWake =false;}}
msg.next = p;// invariant: p == prev.next
prev.next = msg;}// We can assume mPtr != 0 because mQuitting is false.if(needWake){nativeWake(mPtr);}}returntrue;}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
一开始又检测了msg.target为空,会报异常,又一次出现target,一会儿一定要找下是干啥的。
第二句就是 if (msg.isInUse()) {
throw new IllegalStateException(msg + " This message is already in use.");
}
有些同学应该出现过这个异常,即在handleMessage时,收到的消息再次发送,会报这个错误,就是因为这个标记。
然后在类的同步块中,检测是否当前线程looper在quit状态中,是的话则报异常并回收msg。
之后msg.when = when, 发送消息时传过来的时间参数放到了msg里。
然后如果当前mMessages变量为空或者延时时间为0,或者新插入的msg消息的延时时间小于当前消息的时间,则把新来的消息插入到当前消息的前面作为队列头部。
if(p == null || when ==0|| when < p.when){// New head, wake up the event queue if blocked.
msg.next = p;
mMessages = msg;
needWake = mBlocked;
** Defines a message containing a description and arbitrary data object that can be
* sent to a {@link Handler}. This object contains two extra int fields and an
* extra object field that allow you to not do allocations in many cases.**<p class="note">While the constructor of Message is public, the best way to get
* one of these is to call {@link #obtain Message.obtain()} or one of the
*{@link Handler#obtainMessage Handler.obtainMessage()} methods, which will pull
* them from a pool of recycled objects.</p>*/publicfinalclassMessageimplementsParcelable