Android消息机制源码分析

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

  1. app启动

  2. ActivityThread.main()

  3. Looper.prepareMainLooper()

  4. 创建全局唯一的Looper对象

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

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

2. Activity中创建Handler

  1. 创建Handler

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

3. 消息发送

  1. 子线程中发送消息

    //创建一个子线程
            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();
    
  2. Handler中的发送方法

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

    //存储一条消息
    public void enqueueMessage(LhMessage message) {
            try {
                blockingQueue.put(message);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    }
    
  4. 源码中的写法

    //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. 消息处理

  1. 发送消息

    //ActivityThread.main()方法中
    ActivityThread.main(){
    	Looper.loop();
    }
    
  2. 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);
               ……
            }
        ……
    }
    
  3. 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资源。

阻塞

  1. nextPollTimeOutMillis的意义
==0//不阻塞

==-1//一直阻塞直到被唤醒

0;//具体的延时时间,毫秒
  1. 源码: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;
                }
            }
        }
    }
}
  1. 底层方法:nativeInit();

    1. 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);
      }
      
    2. 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);
          }
      }
      
    3. 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
      }
      
  2. 底层方法:nativePollOnce(ptr,nextPollTimeoutMillis)


唤醒

  1. 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;
        }
    
  2. 底层方法: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的生命周期中

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

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

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

  1. 此时主线程会释放CPU资源进入休眠状态
  2. 直到下个消息到达或者有事务发生
  3. 通过往pipe管道写端写入数据来唤醒主线程工作
  4. 这里采用的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
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值