Android消息机制源码分析

it2022-05-05  128

Message

//消息的传递包括内容、标识和Handler对象 public class Message { //消息内容 public Object obj; //消息标识 public int what; //handler对象 public Handler target; }

MessageQueue

//消息队列的功能包含: //1. 存储消息 //2. 获取消息 //3. 阻塞消息 public class MessageQueue { //阻塞队列,最多存放50条 BlockingQueue<Message> blockingQueue = new ArrayBlockingQueue<>(50); //存储一条消息 public void enqueueMessage(Message message) { try { blockingQueue.put(message); } catch (InterruptedException e) { e.printStackTrace(); } } //从消息队列中获取一条消息 public Message next() { try { //返回队列当中的第一条消息 return blockingQueue.take(); } catch (InterruptedException e) { e.printStackTrace(); } return null; } }

Looper

//存在一个全局唯一的消息队列MessageQueue //存在一个ThreadLocal,其中只有一个Looper对象,若没有则new一个 public class Looper { //当前主线程 private static ThreadLocal<Looper> sThredLocal = new ThreadLocal<>(); //消息队列 public final MessageQueue mQueue; //私有的构造方法 private Looper() { //初始化线程的looper的时候去new一个消息队列 mQueue = new MessageQueue(); } //创建一个全局唯一的主线程的Looper public static void prepare() { //当前app的ThreadLocal中只有一个Looper存在 if (sThredLocal.get() != null) { throw new RuntimeException("每个Thread只能创建一个Looper"); } //set一个looper对象 sThredLocal.set(new Looper()); } //获取当前looper对象 public static Looper myLooper() { return sThredLocal.get(); } //从消息队列中获取目标消息 public static void looper() { //从全局ThradLocal中获取唯一的Looper对象 Looper looper = myLooper(); //从Looper中获取消息队列 MessageQueue messageQueue = looper.mQueue; //死循环取消息 for (; ; ) { //消息队列中一直取消息 Message message = messageQueue.next(); if (message != null) { //如果当前消息不为null,则发送消息 message.target.dispatchMessage(message); } } } }

Handler

public class Handler { //全局唯一的Looper private Looper looper; //全局唯一的消息队列 private MessageQueue messageQueue; //初始化工作,得到looper和其中的线程 public Handler() { looper = Looper.myLooper(); messageQueue = looper.mQueue; } //回调发送过来的消息 public void handlerMessage(Message message) { //在实现的Handler中重写该方法可以获得发送过来的消息 } //发送消息 public void sendMessage(Message message) { //……其他操作 //将消息放入消息队列 enqueueMessage(message); } //对message赋值当前handler //往消息队列中存放一条消息 private void enqueueMessage(Message message) { //赋值当前消息的handler message.target = this; //使用messagequeue将消息传入队列中 messageQueue.enqueueMessage(message); } //调用消息 public void dispatchMessage(Message message) { //……其他操作 //调用空方法handlerMessage,如果外部重写则可以接收到该消息 handlerMessage(message); } }

消息机制流程

1. 创建全局唯一的Looper和MessageQueue

app启动

ActivityThread.main()

Looper.prepareMainLooper()

创建全局唯一的Looper对象

//调用 Looper.prepare(); //实现方法 public static void prepare(){ if(sThreadLocal.get()!=null){ //全局的ThreadLocal中只能存在一个looper对象 return; } sThreadLocal.set(new Looper()); }

创建全局唯一的MessageQueue对象

//全局唯一的消息队列 private MessageQueue mQueue; //在looper的构造函数中创建消息队列 private Looper(){ mQueue = new MessageQueue(); }

2. Activity中创建Handler

创建Handler

//activity中 Handler handler = new Handler() { //重写该方法 @Override public void handlerMessage(Message message) { super.handlerMessage(message); System.out.println(message.obj.toString()); } };

3. 消息发送

子线程中发送消息

//创建一个子线程 new Thread(new Runnable() { @Override public void run() { LhMessage message=new LhMessage(); message.what=1; message.obj="luhao message test"; //通过handler发送消息 handler.sendMessage(message); } }).start();

Handler中的发送方法

private void enqueueMessage(LhMessage message) { //赋值当前消息的handler message.lhHandler = this; //使用messagequeue将消息传入队列中 messageQueue.enqueueMessage(message); }

消息队列中将该消息存储起来

//存储一条消息 public void enqueueMessage(LhMessage message) { try { blockingQueue.put(message); } catch (InterruptedException e) { e.printStackTrace(); } }

源码中的写法

//activity中发送消息 mHanlder.sendMessage(message); //handler中发送消息 public boolean sendMessage(Message msg,long uptimeMillis){ MessageQueue queue = mQueue; …… return enqueueMessage(queue,,msg,uptimeMillis); } //handler中将消息放入消息队列 private boolean enqueueMessage(MessageQueue queue,Message msg,long uptimeMillis){ msg.target = this; …… return queue.enqueueMessage(msg,uptimeMillis); } //messageQueue中将消息存储 boolean enqueueMessage(Message msg,long when){ …… mMessage = msg; …… }

4. 消息处理

发送消息

//ActivityThread.main()方法中 ActivityThread.main(){ Looper.loop(); }

Looper.loop()开启消息循环

public static void loop(){ //获得全局的Looper对象 final Looper looper = myLooper(); //获得全局消息变量 final MessageQueue queue = looper.mQueue; …… //开启循环 for(;;){ //从消息队列中不断获取消息 Message msg = queue.next(); …… //调用message中存储的Handler对象的dispatchMessage()方法 msg.target.dispatchMessage(message); …… } …… }

Handler中发送消息

public void dispatchMessage(Message msg){ if(){ …… }else{ …… } handlerMessage(msg); } public void handlerMessage(Message message){ //这里是null方法,在activity中的handler中重写后就能够得到回调 }

消息阻塞和延时

Looper的阻塞主要是靠 MessageQueue来实现的。

在MessageQuese的next()方法中进行阻塞。

在MessageQueue的enqueueMessage()方法中进行唤醒。

主要依赖 native 层的Looper依靠epoll机制进行的。

简单理解阻塞和唤醒

就是在主线程的MessageQueue没有消息时,便阻塞在loop的queue.next()中的nativePollOnce()方法里,此时主线程会释放CPU资源进入休眠状态,直到下个消息到达或者有事务发生,通过往pipe管道写端写入数据来唤醒主线程工作。

这里采用的epoll机制,是一种IO多路复用机制,可以同时监控多个描述符,当某个描述符就绪(读或写就绪),则立刻通知相应程序进行读或写操作,本质同步I/O,即读写是阻塞的。 所以说,主线程大多数时候都是处于休眠状态,并不会消耗大量CPU资源。

阻塞

nextPollTimeOutMillis的意义 ==0//不阻塞 ==-1//一直阻塞直到被唤醒 0;//具体的延时时间,毫秒 源码:MessageQueue.next() public final class MessageQueue { //当前最顶层的Message Message mMessages; //底层方法,非静态的回调 private native void nativePollOnce(long ptr, int timeoutMillis); //本机代码使用 @SuppressWarnings("unused") private long mPtr; //构造方法中会初始化 MessageQueue(boolean quitAllowed) { // 保存native层的MessageQueue引用 mPtr = nativeInit(); } //next方法中做阻塞流程 Message next(){ //消息阻塞时间,默认值0,不阻塞 int nextPollTimeoutMillis=0; //底层处理参数 final long ptr = mPtr; //死循环,一直循环消息队列 for(;;){ //底层方法,判断当前消息是否需要阻塞 nativePollOnce(ptr, nextPollTimeoutMillis); //同步锁 synchronized (this) { //获得当前时间 final long now = SystemClock.uptimeMillis(); //上一个消息为null Message prevMsg = null; //当前消息为全局最顶层的消息 Message msg = mMessages; //如果当前队列里面没有消息,则一直阻塞, //即, nextPollTimeoutMillis = -1; if (msg != null) { //如果消息的入队时间大于当前时间,说明需要延时操作 if (now < msg.when) { //下一条消息还没有准备好。设置一个超时,当它准备好时唤醒。 nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE); } else { //将当前消息拿出去,并且将下一条消息往上移 if (prevMsg != null) { //上一条消息存在,将上一条消息的下一条消息替换成当前消息的下一条消息 prevMsg.next = msg.next; } else { //上一条消息不存在,全局最顶层消息替换成当前消息的下一条 //这里相当于将消息队列往上顶的操作 mMessages = msg.next; } //将当前消息的下一条消息置空,因为这里当前消息的下一条消息已经移动到了队列的最顶,所以当这条消息发送出去的时候,它的下一条消息也应该置空,否则会造成oom msg.next = null; //返回当前消息队列最顶层的这条消息 return msg; } } else { //没有更多的消息了,这里一直阻塞着,不让循环继续下去 nextPollTimeoutMillis = -1; } } } } }

底层方法:nativeInit();

android_os_MessageQueue_nativeInit()

static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) { // 构建MessageQueue NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue(); if (!nativeMessageQueue) { jniThrowRuntimeException(env, "Unable to allocate native queue"); return 0; } nativeMessageQueue->incStrong(env); //返回到Java层 return reinterpret_cast<jlong>(nativeMessageQueue); }

NativeMessageQueue()

NativeMessageQueue::NativeMessageQueue() : mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) { //获取该线程关联的Looper mLooper = Looper::getForThread(); if (mLooper == NULL) { // 创建一个Looper对象 mLooper = new Looper(false); // 将该Looper保存到线程的TLS中 Looper::setForThread(mLooper); } }

Looper()

Looper::Looper(bool allowNonCallbacks) : mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false), mPolling(false), mEpollFd(-1), mEpollRebuildRequired(false), mNextRequestSeq(0), mResponseIndex(0), mNextMessageUptime(LLONG_MAX) { // 构建一个唤醒事件的文件描述符fd mWakeEventFd = eventfd(0, EFD_NONBLOCK); AutoMutex _l(mLock); rebuildEpollLocked();// 重构epoll事件,见2.5 }

底层方法:nativePollOnce(ptr,nextPollTimeoutMillis)

唤醒

MessageQueue.enqueueMessage(Message msg, long when)

boolean enqueueMessage(Message msg, long when) { //消息中的handler不能为null if (msg.target == null) { throw new IllegalArgumentException("Message must have a target."); } //消息也不能使正在使用中的消息 if (msg.isInUse()) { throw new IllegalStateException(msg + " This message is already in use."); } //同步锁 synchronized (this) { //发送消息到一个死线程上处理程序 if (mQuitting) { IllegalStateException e = new IllegalStateException( msg.target + " sending message to a Handler on a dead thread"); msg.recycle(); return false; } //将消息设置成使用中 msg.markInUse(); //设置消息延迟时间 msg.when = when; //赋值全局最顶层的消息 Message p = mMessages; //是否需要唤醒 boolean needWake; //当前消息为null,延迟时间为0,最顶层的消息的还姓时间大于当前消息的延迟时间 if (p == null || when == 0 || when < p.when) { //新的消息,如果阻塞,则唤醒事件队列。 msg.next = p; mMessages = msg; needWake = mBlocked; } else { //插入到队列中间。 //通常我们不需要唤醒事件队列,除非队列的头部有一个屏障,并且消息是队列中最早的异步消息。 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; prev.next = msg; } // 如果需要唤醒,则调用底层方法唤醒 if (needWake) { nativeWake(mPtr); } } return true; }

底层方法:nativeWake(mPtr)

延时入队

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;

延时入队主要指enqueueMessage()消息入列是以上代码对message对象池得重新排序,遵循规则(when从小到大)。

此处for死循环推出情况分两种:

第一种:p==null表示对象池中已经运行到了最后一个,无需再循环。

第二种:碰到下一个消息when小于前一个,立马出循环(不管对象池中所有message是否遍历完),进行从新排序。

常见问题

不能在子线程中更新UI

mThread是UI线程 ,这里检查当前线程是不是UI线程。

//mThread是UI线程 ,这里检查当前线程是不是UI线程。 void checkThread(){ if(mThread != Thread.currentThread()){ throw new CalledFromWrongThreadException("only the original thread that created a view hierarchy can touch its views."); } }

在Activity的生命周期中

onCreate UI处于创建过程,对用户来说界面还不可视。不能算是更新UI,只能说是配置UI,或者是设置UI的属性。ViewRootImpl没被创建,所以这个时候不会调用ViewRootImpl.checkThread()。 onStart 界面可视了。 onResume 界面可以交互了。ViewRootImpl被创建。这个时候去交互界面才算是更新UI。 setContentView 只是建立了View树,并没有进行渲染工作。建立了View树,因此我们可以通过findViewById()来获取到View对象。没有进行渲染视图的工作,也就是没有执行ViewRootImpl.performTransversal()同样View中也不会执行onMeasure() 真正的渲染工作是在onResume之后。 如果直接在onresume中获取View.getHeight()/View.getWidth()得到的结果总是0。

主线程用Looper死循环不会引发ANR

过程:在主线程的MessageQueue没有消息时,便阻塞在loop的queue.next()中的nativePollOnce()方法里。

此时主线程会释放CPU资源进入休眠状态直到下个消息到达或者有事务发生通过往pipe管道写端写入数据来唤醒主线程工作这里采用的epoll机制,是一种IO多路复用机制

Handler中的Looper不能直接new

在Handler构造方法里面new Looper,无法保证保证Looper的唯一性。

只有用Looper.prepare()才能保证唯一性。

具体在Looper.prepare()方法中。

MessageQueue要放在Looper的私有构造方法中初始化

一个线程只绑定一个Looper。

所以在Looper构造方法里面初始化就可以保证MessageQueue是唯一的。

Thread对应一个Looper对应一个MessageQueue。

Handler.post的逻辑在Looper所在的线程中执行

Looper.loop()方法中

从MessageQueue中拿出Message,并且执行其逻辑

这是在Looper中执行的,所以由looper的线程决定

MessageQueue.next()会因为发现了延迟消息,而进行阻塞。后加入的非延迟消息是通过什么方式及时发送的呢?

通过消息的唤醒机制,延时入队排序,将message.when从小到大排列,再去发送消息

有关native层源码可以看这篇博客,很详细


最新回复(0)