Handler创建,必须有个一个Looper,主线程自己创建了。其他线程需要自己创建,默认是没有的。创建方法
1. 这种方法是先创建一个系统定义好的HandlerThread,这个是跑在非UI线程中的,已经创建好了looper,直接用就可以了.创建handler有多个构造函数,具体看源码
HandlerThread threadhandler = new HandlerThread("1111");
threadhandler.start();
myhandler = new myHandler(threadhandler.getLooper());
2. 自己创建一个新的线程,在线程中调用Looper.prepare();然后写处理message回调方法,接着还要调用looper.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();
}
}
为什么一定要有一个Looper呢?
因为handler的无参数构造函数里需要looper,如果为空就抛异常。
如何设置looper?
调用Looper.prepare();这里会判断当前的线程是不是已经有looper了,有的话会抛异常,这就是说一个线程只能有一个looper。
Message是什么,有什么用?
存放消息的用的,它有很多属性。特别说明target,表示的就是处理它的handler,next属性,这就是利用了链表结构来构造的消息队列,message.next表示着当前消息的下一个,是按照时间来的,只能顺序不能反过来,他没有prev属性。
这里需要说一下他的Spool静态属性,这是一个消息池,Spool自己就是一个Message对象,刚开始的时候他是null的,什么时候它有东西的呢?
void recycleUnchecked() {
// Mark the message as in use while it remains in the recycled object pool.
// Clear out all other details.
flags = FLAG_IN_USE;
what = 0;
arg1 = 0;
arg2 = 0;
obj = null;
replyTo = null;
sendingUid = -1;
when = 0;
target = null;
callback = null;
data = null;
synchronized (sPoolSync) {
if (sPoolSize < MAX_POOL_SIZE) {
next = sPool;
sPool = this;
sPoolSize++;
}
}
}
看这个方法,这个方法是looper中循环处理完一个message之后做的事情,它回收了这个message,并且将这个被回收的message和已有的Spool链接起来,再将Spool指向这个回收的message,利用这种方法将回收的message都给串了起来。
message -> spool -> null
spool(被回收的message)-> message(旧的spool) -> null
消息池怎么被使用的?
public static Message obtain() {
synchronized (sPoolSync) {
if (sPool != null) {
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
利用obtain的方法得到一个消息池中的第一个空message,并且spool指向第二个元素,剩余的空消息数-1;
MessageQueue是什么,有什么用?
用来操作messages,例如handler发送消息过来,他需要把消息按照顺序串起来。还有处理完消息后他需要把消息从消息队列中删除等等。
handler有什么用?
发送和处理消息。handler通过sendmessage等方法发送消息,最终都是通过sendMessageAtTime(这里最终是调用了enqueueMessage方法,其中uptimeMillis参数是SystemClock.uptimeMillis() + delayMillis,所以想要优先处理runable,就是要让时间戳在所有message之前,利用
sendMessageAtFrontOfQueue方法,这个参数会传递0,这样就会优先处理了)来处理发送给MessageQueue。然后通过looper回调函数msg.target.dispatchMessage(msg)-->handler的handleMessage()方法具体处理。
handler还可以使用post方法将runnable传给消息队列message有callback属性,在looper的dispatchMessage()方法中,如果是runnable就直接run()了;
最后有个疑问:到底是什么样的机制可以让looper循环去处理message?
while(true){
Messagemsg=queue.next();//mightblock
if(msg!=null){
if(msg.target==null){
return;
}
if(me.mLogging!=null)me.mLogging.println( ">>>>>Dispatchingto"+msg.target+""
+msg.callback+":"+msg.what );
msg.target.dispatchMessage(msg);
if(me.mLogging!=null)me.mLogging.println( "<<<<<Finishedto"+msg.target+""
+msg.callback);
msg.recycle();
}
看到Messagemsg=queue.next();//mightblock 这句话,获得待处理的message,这里可能会堵塞,没有message的时候就循环等待,直到messageQueue退出
现在再看看queue.next方法
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());
}
这部分代码进入条件是target是null,那么什么时候target会是null呢,首先我们要明确,我们只有2种方法往messageQueue的mMessages里插入message---enqueueMessage(sendMessage等最终都是走的它)和enqueueBarrier。我们用handler在setMessage的时候enqueueMessage方法已经
msg.target = this;
而且如果我们使用的是带参数的Message中的obtain方法也会设置target。
那么无疑就是enqueueBarrier方法,这个方法只有一个入口,就是Looper的postSyncBarrier(),它会返回一个int值,用于之后的removeSyncBarrier(int)。
int enqueueSyncBarrier(long when) {
// Enqueue a new sync barrier token.
// We don't need to wake the queue because the purpose of a barrier is to stall it.
synchronized (this) {
final int token = mNextBarrierToken++;
final Message msg = Message.obtain();//这里返回一个target为null的Message
msg.markInUse();
msg.when = when;
msg.arg1 = token;
.....
}
}
然后接着看queue.next,如果是阻断msg,那么就一直找到msg是isAsynchronous的,然后接着处理,这个isAsynchronous有2种设置方法,一个是handler构造参数里面传的,那么所有的sendMessage都会打上异步标签。还有一种就是单独调用一个message的setAsynchronous。
这样的话,当阻断的时候就会跳过普通的message,找到异步message去执行。
接着往下看next();
// Got a message.
mBlocked = false;
if (prevMsg != null) {
prevMsg.next = msg.next;//如果阻断的话,这里prevMsg就不是空,那么就会直接把标签是异步的message的上一个message的next直接指向标签是异步的message的下一个message,其实就是把异步message从消息队列中给抽出去了
} else {
mMessages = msg.next;//这里就是普通message的处理步骤,消息队列少了第一个元素
}
msg.next = null;
if (false) Log.v("MessageQueue", "Returning message: " + msg);
return msg;
到这里,阻断message还是在消息队列中,下次循环还是依照这个顺序执行。除非我们手动调用removeSyncBarrier方法才能正常处理普通的message。
这里得到个结论,mMessages的第一个元素不一定就是本次待处理的message。
这个阻断在源码中有地方使用:scheduleTraversals这个方法,这个方法是view的measure,layout,draw三部曲的入口。
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().postSyncBarrier();//这里,使用了阻断,此时只有message是异步的才可以被执行。
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
}
}
mTraversalRunnable这个Runnable执行的代码是
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().removeSyncBarrier(mTraversalBarrier);//已经开始执行了,就把阻断remove掉,view展示完毕后继续正常处理普通的message
if (mProfile) {
....
}
}
}
优点快速展示,想了半天,为啥他不使用sendMessageAtFrontOfQueue这个方法优先执行mTraversalRunnable呢?可能原因,我们也可以主动调用改方法,那么岂不是就有可能我们的runnable优先与系统展示view了?使用阻断,他内部还是按照时间戳顺序执行的,就保证了view展示优先执行~!!!!!
接着看next();
// Run the idle handlers.
// We only ever reach this code block during the first iteration.
for (int i = 0; i < pendingIdleHandlerCount; i++) {
final IdleHandler idler = mPendingIdleHandlers[i];
mPendingIdleHandlers[i] = null; // release the reference to the handler
boolean keep = false;
try {
keep = idler.queueIdle();
} catch (Throwable t) {
Log.wtf("MessageQueue", "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
这里还有代码,这是干嘛的?这这个就是执行idle handlers的,当之前的message,一个都找不到的时候,说明空闲,这个时候就可以执行IdleHandler了
使用方法就是调用MessageQueue的addIdleHandler方法。