目录
handler消息循环流程图如下
handler消息机制,包括四个部分:
- Message:消息,在子线程传递到主线程,其实是内存共享
- MessagrQueue:消息队列,用来给消息队列中添加和存储message,是单链表实现的优先级队列,通过next()取下一个messgae
- Looper:循环调用looper.loop()方法,从messageQueue中取消息,取到的消息通过dispatchMessage()传递给handler处理,一个线程只能有一个Looper
- Handler:发送和接收处理消息
用户点击应用图标发生了什么:
launcher(桌面进程,其实也是一个app,桌面是它的一个acticity)—> 创建一个新进程zygote —> fork —> 调用ActivityThread.Main() —> 在main方法中
Looper.prepareMainLooper(); //初始化Looper以及MessageQueue
初始化,并调用Looper.loop()
开启循环
1. 创建Looper
public static void main(String[] args) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");
//其他代码省略...
Looper.prepareMainLooper(); //初始化Looper以及MessageQueue
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop(); //开始轮循操作
throw new RuntimeException("Main thread loop unexpectedly exited");
}
Looper.prepareMainLooper()方法中调用prepare方法进行初始化
public static void prepareMainLooper() {
prepare(false);//消息队列不可以quit
synchronized (Looper.class) {
if (sMainLooper != null) {
throw new IllegalStateException("The main Looper has already been prepared.");
}
sMainLooper = myLooper();
}
}
prepare
有两个重载,主要看 prepare(boolean quitAllowed)
,quitAllowed
的作用是在创建MessageQueue时标识消息队列是否可以销毁, 主线程不可被销毁;
其中通过判断是否创建了looper,如果已经创建过,返回错误提示,来保证looper的唯一性
public static void prepare() {
prepare(true);//消息队列可以quit
}
//quitAllowed 主要
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {//不为空表示当前线程已经创建了Looper
throw new RuntimeException("Only one Looper may be created per thread");
//每个线程只能创建一个Looper
}
sThreadLocal.set(new Looper(quitAllowed));//创建Looper并设置给sThreadLocal,这样get的时候就不会为null了
}
2. 创建MessageQueue
new Looper()中,创建MessageQueue,以及与当前线程绑定
private Looper(boolean quitAllowed) {
mQueue = new MessageQueue(quitAllowed);//创建了MessageQueue
mThread = Thread.currentThread(); //当前线程的绑定
}
MessageQueue的构造方法如下:
MessageQueue(boolean quitAllowed) {
//mQuitAllowed决定队列是否可以销毁 主线程的队列不可以被销毁需要传入false, 在MessageQueue的quit()方法就不贴源码了
mQuitAllowed = quitAllowed;
mPtr = nativeInit();
}
2.2 Looper.loop()
Looper.loop()开启循环,只有当msg为null的时候才结束循环,调用quit方法可以结束循环,但是主线程中禁止调用,子线程中自己创建的Looper用完了记得调用quit();
loop方法中,开启for死循环,通过调用queue.next()
遍历messageQueue中的message,通过调用msg.target(也就是message绑定的handler)dispatchMessage发送消息给handler处理
public static void loop() {
final Looper me = myLooper();//里面调用了sThreadLocal.get()获得刚才创建的Looper对象
if (me == null) {
throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
}//如果Looper为空则会抛出异常
final MessageQueue queue = me.mQueue;
// Make sure the identity of this thread is that of the local process,
// and keep track of what that identity token actually is.
Binder.clearCallingIdentity();
final long ident = Binder.clearCallingIdentity();
for (;;) {
//这是一个死循环,从消息队列不断的取消息
Message msg = queue.next(); // might block
if (msg == null) {
//由于刚创建MessageQueue就开始轮询,队列里是没有消息的,等到Handler sendMessage enqueueMessage后
//队列里才有消息
// No message indicates that the message queue is quitting.
return;
}
// This must be in a local variable, in case a UI event sets the logger
Printer logging = me.mLogging;
if (logging != null) {
logging.println(">>>>> Dispatching to " + msg.target + " " +
msg.callback + ": " + msg.what);
}
msg.target.dispatchMessage(msg);//msg.target就是绑定的Handler,详见后面Message的部分,Handler开始
//后面代码省略.....
msg.recycleUnchecked();
}
}
3. 创建Handler
最简单的创建handler方式
Handler handler=new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
}
};
Handler的实现源码
public Handler(Callback callback, boolean async) {
//前面省略
mLooper = Looper.myLooper();//获取Looper,**注意不是创建Looper**!里面调用了sThreadLocal.get()获取looper
if (mLooper == null) {
throw new RuntimeException("Can't create handler inside thread that has not called Looper.prepare()");
}
mQueue = mLooper.mQueue;//得到消息队列MessageQueue
mCallback = callback; //初始化了回调接口
mAsynchronous = async;
}
其中,Looper.myLooper()方法实现如下
//这是Handler中定义的ThreadLocal ThreadLocal主要解多线程并发的问题
// sThreadLocal.get() will return null unless you've called prepare().
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
4. 创建Message
我们可以new Message(),但是有更好的方式Message.obtain()
。因为可以检查是否有可以复用的Message,通过复用避免过多的创建、销毁Message对象达到优化内存和性能的目地。
为什么频繁的new Message()
会造成OOM?
因为new会去申请内存,频繁的new会造成剩余内存不连续,产生内存碎片,造成内存抖动,当其他需要连续的大片内存时,提供不了就会产生OOM内存溢出。所以用obtain方式复用message,享元设计模式,内存复用
public static Message obtain(Handler h) {
Message m = obtain();//调用重载的obtain方法
m.target = h;//并绑定的创建Message对象的handler
return m;
}
public static Message obtain() {
synchronized (sPoolSync) {//sPoolSync是一个Object对象,用来同步保证线程安全
if (sPool != null) {//sPool是就是handler dispatchMessage 后 通过recycleUnchecked 回收用以复用的Message
Message m = sPool;
sPool = m.next;
m.next = null;
m.flags = 0; // clear in-use flag
sPoolSize--;
return m;
}
}
return new Message();
}
5. Message和Handler的绑定
创建Message的时候可以通过 Message.obtain(Handler h) 这个构造方法绑定。当然可以在Handler中的enqueueMessage()也绑定了,所有发送Message的方法都会调用此方法入队,所以在创建Message的时候是可以不绑定的
privite boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
msg.target = this; //绑定
if (mAsynchronous) {
msg.setAsynchronous(true);
}
return queue.enqueueMessage(msg, uptimeMillis);
}
6. Handler发送消息
Handler发送消息的重载方法很多,但是主要只有2种:sendMessage(Message) 方法通过一系列重载方法的调用
sendMessage
调用sendMessageDelayed
,继续调用sendMessageAtTime
,继续调用enqueueMessage
,继续调用messageQueue的enqueueMessage
方法,将消息保存在了消息队列中,而最终由Looper取出,交给Handler的dispatchMessage
进行处理。
我们可以看到在
dispatchMessage
方法中,message中callback是一个Runnable对象,如果callback不为空,则直接调用callback的run方法,否则判断mCallback是否为空,mCallback在Handler构造方法中初始化,在主线程通直接通过无参的构造方法new出来的为null,所以会直接执行后面的handleMessage()
方法
public void dispatchMessage(Message msg) {
if (msg.callback != null) {//callback在message的构造方法中初始化或者使用handler.post(Runnable)时候才不为空
handleCallback(msg);
} else {
if (mCallback != null) {//mCallback是一个Callback对象,通过无参的构造方法创建出来的handler,该属性为null,此段不执行
if (mCallback.handleMessage(msg)) {
return;
}
}
handleMessage(msg);//最终执行handleMessage方法
}
}
private static void handleCallback(Message message) {
message.callback.run();
}
7. Handler处理消息
在handleMessage(Message)
方法中,我们可以拿到message对象,根据不同的需求进行处理,整个Handler机制的流程就结束了
8. 关于Looper.loop()死循环为什么不会阻塞主线程的问题
loop方法内是一个死循环,只有当msg为null的时候才return,那么为什么死循环不会阻塞主线程呢?
因为一个app的运行我们肯定不希望它允许一段时间后退出,所以需要通过死循环的方式来维护应用程序的正常运行.
在loop方法中通过调用MessageQueue.next()
方法来取到msg,而next方法中通过调用nativePollOnce()
方法,基于Linux pipe/epoll机制,当消息队列没有消息的时候,会阻塞在此处,Looper进入空闲状态,并不会消耗CPU;当有消息传入的时候,唤醒继续执行
引用
主线程的死循环一直运行是不是特别消耗CPU资源呢? 其实不然,这里就涉及到Linux pipe/epoll机制,简单说就是在主线程的MessageQueue没有消息时,便阻塞在loop的queue.next()中的nativePollOnce()方法里,此时主线程会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生,通过往pipe管道写端写入数据来唤醒主线程工作。这里采用的epoll机制,是一种IO多路复用机制,可以同时监控多个描述符,当某个描述符就绪(读或写就绪),则立刻通知相应程序进行读或写操作,本质同步I/O,即读写是阻塞的。 所以说,主线程大多数时候都是处于休眠状态,并不会消耗大量CPU资源。 Gityuan–Handler(Native层)
Message next() {
....
int pendingIdleHandlerCount = -1; // 这个表示等待执行的Handler的数量
int nextPollTimeoutMillis = 0;
for (;;) {
....
// 这里调用naive方法操作管道,由nextPollTimeoutMillis决定是否需要阻塞
// nextPollTimeoutMillis为0的时候表示不阻塞,为-1的时候表示一直阻塞直到被唤醒,为数值的时候表示阻塞的时长
nativePollOnce(ptr, nextPollTimeoutMillis);
synchronized (this) {
// Try to retrieve the next message. Return if found.
final long now = SystemClock.uptimeMillis();
Message prevMsg = null;
Message msg = mMessages;
if (msg != null) {
// 如果当前时间 < 消息执行时间,取到阻塞时长
if (now < msg.when) {
// Next message is not ready. Set a timeout to wake up when it is ready.
// nextPollTimeoutMillis 阻塞时长
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.
// 如果消息队列没有消息,赋值-1,开始阻塞
nextPollTimeoutMillis = -1;
}
...
}//synchronized 结束之处
// 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(TAG, "IdleHandler threw exception", t);
}
if (!keep) {
synchronized (this) {
mIdleHandlers.remove(idler);
}
}
}
// Reset the idle handler count to 0 so we do not run them again.
pendingIdleHandlerCount = 0;
// While calling an idle handler, a new message could have been delivered
// so go back and look again for a pending message without waiting.
nextPollTimeoutMillis = 0;
}
}
9. 小结
整个发送消息的流程如下:
Handler.sendMessage()
发送消息到消息队列MessageQueue
的enqueueMessage()
,MessageQueue在该方法中对message进行排序,Looper
调用自己的loop()
函数带动MessageQueue
的next()
方法遍历其中的消息队列得到每个Message,当Message
达到了执行时间的时候执行,就会调用message
绑定的handler
处理消息
handler相关问答
https://www.imooc.com/article/25134?block_id=tuijian_wz
https://zhuanlan.zhihu.com/p/269485733
https://segmentfault.com/a/1190000022221446