Android消息机制探析
Android消息机制
消息机制简介
消息机制是Android重要的一种通信方式,其主要使用对应的是Handler,主要是用来处理不同线程之间的通信,通常用在子线程执行耗时任务,当需要回到主线程更新UI时,通过Handler将有关的操作切换到主线程
主要涉及的类有,Handler、Message、MessageQueue、ThreadLocal、ThreadLocalMap、Thread和Looper,相关的UML如下图所示





消息机制中的数据结构
根据uml的相关数据可以推断出所采用的数据结构,主要有以下几种
Message
 Message对象中持有自身的next对象,属于单列表结构
MessageQueue
 从类名可以推断出是存放Message的队列,基本操作有next()出队和enqueueMessage(MessageQueue queue, Message message入队
ThreadLocalMap
 根据名字可以推断出是包含键值对的map结构
使用Handler发送消息
最常用的使用方式就是在Activity中创建一个Handler对象发送空消息了,详细代码如下
Handler handler = new Handler(){
    public void handleMessage(Message msg){
        //do work
    }
};
handler.sendEnptyMessage(0);
刨析源码
Handler的构造函数会对mLooper和mQueue对象进行赋值,最关键的就是mLooper的获取,如果mLooper为null会直接抛出异常终止当前进程(程序),相关源码如下:
//Handler.java
public Handler(){
    this(null,false);
}
public Handler(Callback callback, boolean async){
    mLooper = Looper.myLooper();		//1
    if(mLooper == null){				//2
        throw new RuntimeException("Can't create handler inside thread...");
    }
    mQueue = mLooper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}
步骤1对mLooper初始化,步骤2检查不为null,否则抛出异常,Looper.myLooper()是如何初始化的呢?
Looper的初始化
从Handler的构造函数可以得知Looper.myLooer()是初始化Handler中mLooper对象的,myLooer()的源码如下:
//Looper.java
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>;
public static Looper myLooper(){
    return sThreadLocal.get();
}
//ThreadLocal.java
public T get(){
    Thread t = Thread.currentThread();					//1
    ThreadLocalMap map = getMap(t);
    if(map != null){
        ThreadLocalMap.Entry e = map.getEntry(this);
        if(e != null){
            T result = (T)e.value;
            return result;
        }
    }
    return setInitialValue();							//2
}
public T setInitialValue(){
    T value = initialValue();							//3
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if(map != null)
        map.set(this,value);							//4
    else
        createMap(t,value);								//5
    return value;
}
void createMap(Thread t, T firstValue){
    t.threadLocals = new ThreadLocalMap(this,firstValue);
}
ThreadLocaMap getMap(Thread t){
    return t.threadLocals;								//6
}
protected T initialValue(){
    return null;										//7
}
get()函数中首先获取了当前的Thread对象,然后调用getMap(Thread)函数来获取thread.threadLocals属性,从代码中可以看出此对象是一个ThreadLocalMap对象,接着就检查map是否为null,如果为null就初始化,步骤7实际的初始化对象为null,回到Handler构造函数,Looper.myLooper()在新建的Thread线程中返回值必然是null,正常开发在activity中直接new Handler()并没有出现异常!那么activity所在的线程为什么不会抛出异常呢?activity所在的线程通常称为UI线程,是Android系统在启动app是自动为应用创建的第一个线程也就是UI线程、主线程,接下来进入app启动后的代码看看app启动后的相关初始化操作
ActivityThread.main()
有c/c++基础的同学会知道程序执行首先会执行代码中的main函数,main函数也是整个程序中唯一的,java程序也不例外,都是以main函数为起点运行的,既然Android是用java语言开发的,平时开发怎么没见main函数呢?其实Android是有main函数的,Android应用的每个进程都是系统通过zygote进程fork出来的,zygote进程则是由系统init进程启动的,应用进程通过zygote fork后首先会找到main函数,然后invoke反射执行ActivityThread.mai()函数,main函数具体代码如下:
//ActivityThread.java
public static void main(String[] args){
    Looper.prepareMainLooper();		//1
    ActivityThread thread = new ActivityThread();
    thread.attach(false,startSeq);	//2
    if(sMainHandler == null){
        sMainHandler = thread.getHandler();
    }
    Looper.loop();				//3
    throw new RuntimeException("Main thread loop unexpectedly exited");
}
步骤1实际是准备了设置了当前线程looper步骤2则开始启动应用进程相关的内容,步骤3则是调用loop()等待消息的到来,这里就体现初Android应用是基于消息机制运行的,包含生命周期,输入事件等都是通过消息机制来运行的首先来看l主线程中looper的初始化
主线程中Looer的初始化
Looper.prepareMainLooper()
//Looper.java
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>;
public static void prepareMainLooper(){
    prepare(false);					//1
    synchronized(Looper.class){
        if(sMainLooper != null){	//2
            throw new IllegalStateException("the main looper hsa already been prepared.");
        }
        sMainLooper = myLooper();	//3
    }
}
private static void prepare(boolean quitAllowed){
    if(sThreadLocal.get() != null){	//4
        throw new RuntimeException("Only one Looper may be create per thread");
    }
    sThreadLocal.set(new Looper(quitAllowed));//5
}
public static void prepare(){
    prepare(ture);
}
private Looper(boolean quitAllowed){
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread;
}
通过上述代码分析可以知道prepareMainLooper主要是设置创建了Looper对象并保存在sThreadLocal对象中,步骤2还对sMainLooper对象非空判断,只有第一次执行prepareMainLooper后给sMainLooper赋值,后续再次执行就会抛出异常,而Looper的构造函数是私有的,外部不能直接创建对象,但是Looper提供了静态函数prepare()来创建构造当前线程的Looper对象,上面提到的子线程使用looper的注意点就是这里,由于子线程没有初始化looper,在子线程中使用Handler会由于未初始化Looper抛出异常,所以必须在子线程中执行Looper.prepare()来对当前线程设置looper对象,looper构造函数则是创建了MessageQueue对象和mThread线程对象,其中UI线程创建Looper的参数quitAllowed为false禁止looper退出
Looper.loop()
loop的作用是在当前线程循环等待MessageQueue消息队列,退出等待可以执行Looper.quit(),loop内部通过for死循环遍获取queue.next()消息,获取到消息后开始向handle中分发消息,loop关键代码如下:
//Looper.java
public static void loop(){
    final Looper me = myLooper();	//1
    if(me == null){
        throw new RuntimeException("No looper; Looper.prepare() wasn't called on this thread");
    }
    me.mInLoop = ture;
    final MessageQueue queue = me.mQueue;	//2
    for(;;){
        //might block
        Message msg = quene.next();			//3
        if(msg == null){
            //No message indicates that the message queue is quitting;
            return;
        }
        try{
            msg.tartget.dispatchMessage(msg);	//4
        }catch(Exception e){
            
        }
        msg.recycleUncheched();		//5
    }
}
在loop中首先获取当前线程的loop对象,判断不为null,抛出异常提示需要Looper.prepare()来初始化当前线程的looper对象,然后调用queu.next()获取消息,next()为阻塞方法,直到有消息到来才会返回Message对象,返回Message后就调用msg.target.dispatchMessage(msg)来分发消息,其中target是handler对象,在handler发送消息时把自身handler对象保存在message.target属性中,最终处理完消息后Message对象将被回收利用
handleMessage消息分发
上面提到loop会循环获取消息队列中的消息,获取到消息后就开始调用handler.handleMessage分发消息,分发的流程是什么?具体怎么执行的,下面是Handler.java中的关键代码:
//Handler.java
public void dispatchMessage(Message msg){
    if(msg.callback != null){		//1
        handleCallback(msg);
    }else{
        if(mCallback != null){
            if(mCallback.handleMessage(msg)){	//2
                return;
            }
        }
        handleMessage(msg);			//3
    }
}
private static vodi handleCallback(Message message){
    message.callback.run();
}
public interface Callback{
    boolean handleMessage(Message msg);
}
上述代码分为三个流程
1. msg.callback对象优先级最高,如果不为null则直接调用callback,其中callback是Runnable对象
- 如果handler在初始化的时候设置mCallabck对象,会调用mCallback对象的handleMessage函数,当mCallback对象消费掉此次消息事件后就返回,否则继续
- 最终会调用handler.对象的handleMessage函数分发,日常使用handler也是重写handleMessage来处理message事件的
至此我们大致了解了消息机制传播流程中的重要线索
- 主线程启动后会立马执行Looper.prepareMainLooper()设置主线程的looper对象
- 在Looper的构造函数中创建MessageQueue对象
- 使用Handler时必须要保证当前线程的sThreadLocla对象持有Looper对象
- 使用Looper.prepare()可以设置当前线程的Looper对象,并且只能初始化一次
- 执行loop阻塞等待消息队列内容
- 消息队列返回Message后开始分发Message,分发顺序为message.callback->handle.mCallback->handleMessage,message.callback优先级最高,只要不会null就只会分发给message对象,其次是handle.mCallback对象,如果此对象消费掉此次事件,则分发结束,否则继续分发给handle.handleMessage()函数
消息队列添加消息
使用handle发送消息实际就是往消息队列添加消息,具体代码如下:
//Handle.java
public final boolean sendEmptyMessage(int what){
    return sendEmptyMessageDelayed(what,0);
}
public final boolean sendEmptyMessageDelayed(int what, long delayMillis){
    Message msg = Message.obtain();
    msg.wath = wath;
    return sendMessageDelayed(msg,delayMillis);
}
public final boolean sendMessageDelayed(Message msg, long delayMillis){
    if(delayMillis < 0){
        deleayMillis = 0;
    }
    return sendMessageAtTime(msg,SystemClock.uptimeMillis()+delayMillis);
}
public boolean sendMessageAtTime(Message msg, long uptimeMillis){
    MessageQueue queue = mQueue;
    if(queue == null){
        RuntimException e = new RuntimeExceptin(this + " sendMessageAtTime() called with no mQueue");
        return false;
    }
    return enqueueMessage(queue,msg,uptimeMillis);
}
private final boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis){
    msg.target = this;		//1
    msg.workSourceUid = ThreadLocalWorkSource.getUid();
    if(mAynschronous){
        msg.setAynschronous(ture);	//2
    }
    return queue.enqueueMessage(msg,uptitmeMillis);	//3
}
从源码中可以看出发送消息最终都会执行到sendMessageAtTime函数,最后则会调用到MessageQueue.enqueueMessage函数加入到消息队列,Looper.loop()函数中获取到message对象后就会调用message.target.dispatchMessage(message)函数来分发消息事件,从代码1处可以看到message的target属性就是在入队前设置为当前的handler发送对象的,步骤2是设置异步消息
MessageQueue消息入队
根据类名字以及支持的函数操作可以判断出这个队列结构,支持入队和出队操作,handle发送消息的过程实际上就是加入MessageQueue队列的过程,入队源码如下:
//MessageQueue.java
boolean enqueueMessage(Message msg, long when){
    if(msg.target == null){
        throw new IllegalArgumentException("Message must hava a target.");
    }
    synchronized(this){
        if(mQuitting){//退出队列,回收msg对象,返回
            msg.recycle();
            return false;
        }
        msg.when = when;
        Message p = mMessage;
        boolean needWake;
        //当前消息链表为null或新消息是执行时间在当前消息之前
        if(p == null || when == 0 || when < p.when){ 
            msg.next = p;
            mMessage = msg;		//1 添加到链表头部
            needWake = mBlocked;
        }else{
            needWake = mBlocked && p.target == null && msg.isAsynchronous();
            Message prev;
            for(;;){
                prev = p;
                p = p.next;
                //遍历寻找插入的位置,链表尾部,或者是执行时间比下个msg靠前
                if(p == null || when < p.when){ //2 按照执行时间排序找到插入位置
                    break;
                }
                if(needWake && p.isAsynchronous()){
                    needWake = false;
                }
            }
            msg.next = p; 	//3 添加到链表非头部
            prev.next = msg;
        }
        if(needWake){
            nativaWake(mPtr);		//4唤醒阻塞
        }
    }
    return true;
}
前面分析数据结构中已经得出Message实际上是一个单链表结构,每次handle发送消息,最终都会根据Message.when也就是message的执行时间来排序的,最先执行的在链表的最前面,最重要的操作就是新消息如何插入链表中,步骤1,添加到链表头,步骤2,添加到非头部,判断依据主要是根据 when < p.when 进行时间排序
MessageQueue消息出队
发送消息的本质就是把消息按照执行时间顺序添加到单链表中,然后loop循环等待MessageQueue.next()获取下一个消息,当获取到消息后马上就分发给handler进行消息处理,消息队列的消息是如何出列的呢?详情请看下面源码:
//Message Queue.java
Message next(){
    final long ptr = mPtr; //mPtr为native对象指针,如果指针被释放或者队列退出指针为0
    if(ptr == 0){
        return null;
    }
    int pendingIdleHandlerCount = -1; //没有消息是要执行的任务数
    int nextPollTimeoutMillis = 0;
    for(;;){
        nativePollOnce(ptr,nextPollTimeoutMillis); //1 进入native层阻塞
        synchronized(this){
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessage;
            if(msg != null && msg.target == null){	// 2 msg.target正常是不会为null的
                do{
                   	prevMsg = msg;
                    msg = msg.next;
                }while(msg != null && !msg.isAsynchronous());	//3 直到找到下一个异步消息
            }
            if(msg != null){
                if(now < msg.when){//还没到执行时间
                    //计算阻塞时间
                    nextPollTimoutMillis = (int)Math.min(msg.when - now,Integer.MAX_VALUE);
                }else{ //当前时间大于msg的执行时间,需要执行此msg任务了
                    mBlocked = false;
                    if(prevMsg != null){
                        prevMsg.next = msg.next;
                    }else{
                        mMessage = msg.next;	//把mMessage对象指向next对象
                    }
                    msg.next = null; //打断msg.next链
                    msg.markInUse();//设置use标记
                    return msg;
                }
            }else{
                nextPollTimeoutMillis = -1; //没有消息
            }
            if(mQuitting){
                dispose();//退出队列,销毁native对象
                return null;
            }
            //如果没有消息或者还没到消息执行时间,开始执行idleHandler任务
            if(pendingIdleHandlerCount < 0 &&(mMessage == null || now < mMessage.when)){
                pendingIdleHandlerCount = mIdleHandlers.size();
            }
            if(pendingIdleHandlerCount <=0){
                continue;
            }
            if(mPendingIdelHandlers == null){
                //最多执行4个任务
                mPengdingIdelHandler = new IdleHandler[Math.max(pendingIdleHandlerCount,4)];
            }
            mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
        }//end synchronized
        //执行IdleHandler
        for(int i = 0; i< pendingIdleHandlerCount; i++){
            final IdleHandler idler = mPendingIdleHandlers[i];
            mPendingIdleHandlers[i] = null;
            boolean keep = false;
            try{
                keep = idler.queueIdle();//返回false 只执行一次,返回true会重复执行
            }catch(Throwable t){
                
            }
            if(!keep){
                synchronized(this){
                    mIdleHandlers.remove(idler);
                }
            }
        }
    }//end for(;;)
}//end next
private void dispose(){
    if(mPtr != 0){
        nativeDestory(mPtr); //销毁底层对象
        mPtr = 0;
    }
}
next函数出队的重点就是检查当前消息也就是消息链表的头结点是否满足执行时间message.when参数,如果满足就返回,否则利用当前时间减去message.when计算出阻塞时间,交由native层处理,最终达到满足时间后会native会唤醒阻塞,最终next会返回msg对象,由handler发送消息后进入的入队函数enqueueMessage()函数最终都会执行msg.target=this,把当前的handler对象赋值给message对象的target属性,奇怪的是在next()出队函数中首先执行的就有判断target为null的情况
//MessageQueue.next()节选
Message next(){
    Meesage prevMsg = null;
    Message msg = mMessage;
    if(msg != null && msg.target == null){	// 1
        do{
			prevMsg = msg;
            msg = msg.next;
        }while(msg != null && !msg.isAsynchronous())	//2
    }
}
从前面next代码分析可以看如果满足条件后msg就是next()返回的对象,前面也说了next()函数返回的依据就是Message是以执行时间从近到远排序的单链表结构,最先执行的在链表头部,但是这里判断了msg.target为null后就直接继续向后移动,直到找到一个异步消息,然后根据时间判断是否要执行异步消息,分析next()函数我们得到两条非常重要的结论
- next()函数正常会根据执行时间message.when的先后顺序返回交由Lopper.loop()函数处理
- 如果当前消息链表的头部的message.target为null则会遍历找到异步消息,优先执行异步消息
异步消息
Message分异步消息和同步消息,通过message.flags值记录,FLAG_ASYNCHONOUS是异步标记位,有两种设置方式
1. **通过message.setAsynchronous()**
- 通过handler.mAsynchronous属性,handler把Message压入队列中会根据判断mAsynchronous是否位true,如果为true则设置message.setAsynchronous(true),以此来设置异步消息
注意:不过handler的mAsynchronous属性是私有的,唯一可以设置此值的构造还函数还是@hide的,所以并不能直接使用
消息屏障
实际利用handler发送的消息都会把handler自身赋值给message对象的target属性,唯一能添加target为null的消息只有在MessageQueue中的postSyncBarrier()方法,添加target为null的消息就被称为消息屏障,其作用就是当存在消息屏障时,会优先执行后面的异步消息,直到消息屏障被移除,postSyncBarrier添加message代码如下:
//MessageQueue.java
/*@hide*/
public int postSyncBarrier(){
    return postSyncBarrier(SystemClock.uptimeMillis());
}
//添加消息屏障
private int postSyncBarrier(long when){
    synchronized(this){
        final int token = mNextBarrierToken++;
        final Message msg = Message.obtain();
        msg.markInUse();
        msg.when = when;
        msg.arg1 = token;
        Message prev = null;
        Message p = mMessage;
        if(when !=0 ){
            while(p!=null && p.when <= when){//遍历找插入到合适的位置
                prev = p;
                p = p.next;
            }
        }
        //添加到链表
        if(prev != null){
            msg.next = p;
            prev.next = msg;
        }else{
            msg.next = p;
            mMessage = msg;
        }
        return token;
    }
}
移除消息屏障
移除消息屏障同样是在MessageQueue中操作,值得注意的是需要将添加消息屏障的返回值token传入其中,详细代码如下:
//MessageQueue.java
//@hide
public void removeSyncBarrier(int token){
    synchronized(this){
        Message prev = null;
        Message p = mMessage;
        //找到target为null的消息
        while(p != null && (p.target != null || p.arg1 != token)){
            prev = p;
            p = p.next;
        }
        if(p == null){
            throw new IllegalStateException("the ....");
        }
        final boolean needWake;
        if(prev != null){
            prev.next = p.next;
            needWake = false;
        }else{
            //消息屏障位于链表的第一个
            mMessage = p.next;
            needWake = mMessage == null || mMessage.target != null;
        }
        p.recycleUnchecked();
        if(needWake && !mQuitting){
            nativeWake(mPtr);
        }
    }
}
从while的判断条件来p.target !=null || p.arg1 != token,只要是消息屏障p.target就一定是null,也就是说最先添加的消息屏障会被先移除
源码中消息屏障的使用
源码中最典型的使用就是在ViewRootImpl.java中,在activity Resume时候系统会将contentView添加到PhonwWIndow中的decorView中,然后会将decor添加到WindowManageImpl中,WindowManageImpl会将事件转交给WindowManageGlobal中,并创建ViewRootImpl,最终将decorView添加到ViewRootImpl中,ViewRootImpl经过一系列处理开始执行requestLayout(),在这其中便会检查是否在主线程,最终开始view的绘制流程,由于View在Android系统中的优先级比较高,在绘制view之前就会添加消息屏障,然后再开始执行异步任务绘制view,从而提高View绘制的优先级,详情代码如下:
//ViewRootImpl.java
public void requestLayout(){
    if(!mHandlingLayoutInLayoutRequest){
        checkThread();//检查是否再子线程更新UI
        mLayoutRequested = true;
        scheduleTraversals();//开始绘制
    }
}
//负责检查UI线程抛出线程异常
void checkThread(){
    if(mThread != Thread.currentThread()){
        throw new CalledFromWrongThreadException("Only the original thread that created a view hierarchy can touch its views.");
    }
}
void scheduleTraverrsals(){
    if(!mTraversalScheduled){
        mTraversalScheduled = ture;
        //添加消息屏幕
        mTraveralBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
        mChoreographer.postCallback(Choreogerpher.CALLBACK_TRAVERSAL,mTraversalRunable,null);
    }
}
//Choreographer.java
//@hide
public void postCallback(int callbackType,Runnable action,Object token){
    postCallbackDelayed(callbacktype,action,token,0);
}
public void postCallbackDelayed(int callbackType,
            Runnable action, Object token, long delayMillis) {
    postCallbackDelayedInternal(callbackType, action, token, delayMillis);
}
 private void postCallbackDelayedInternal(int callbackType,
            Object action, Object token, long delayMillis) {
     synchronized(mLock){
         final long now = SystemClock.uptimeMillis();
         final long dueTime = now + delayMillis;
         //保存到callabck队列中
         mCallbackQueue[callbackType].addCallbackLocked(dueTime,action,token);
         if(dueTime <= now){
             scheduleFrameLocked(now);
         }else{
             //发送异步消息
             Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK,action);
             msg.arg1 = callbackType;
             msg.setAynchronous(ture);
             mHandler.sendMessageAtTime(msg,dueTime);
         }
     }
 }//end Choreographer
HandlerThread
HandlerThread从名字可以看出是一个Handler和Thread的组合体,内部有一个mHandler和mLooper对象,mLooper在run()函数中初始化在初始化完mLooper后会执行onLooperPrepared()函数,子类可以通过重载这个函数来实现一些特殊的操作,具体的源码如下
public class HandlerThread 	extends Thread{
    Looper mLooper;
    private Handler mHandler;
    
    public void run(){
        Looper.prepare();//1
        synchronized(this){
            mLooper = Looper.myLooper();//2
            notifyAll();
        }
        onLooperPrepare();//3
        Looper.loop();//4
    }
    public Looper getLooper(){
        if(!isAlive()){
            return null;
        }
        synchronized(this){
            while(isAlive() && mLooper == null){
                try{
                    wait();
                }catch(InterruptedException e){
                    
                }
            }
        }
        return mLooper();
    }
    public Handler getThreadHandler(){
        if(mHandler == null){
            mHandler = new Handler(getLooper());
        }
        return mHandler;
    }
    public boolean quit(){
        Looper looper = getLooper();
        if(looper != null){
            looper.quit();
            return true;
        }
        return false;
    }
}
源码1处Looper.prepare()为当前线程初始化Looper对象;
源码2处获取当前线程的mLooper对象;
源码3处回调通知mLooper准备好HandlerThread真正可以开始工作了,onLooperPrepare函数是一个空函数,子类可以重写;
源码4处调用loop()函数循环等待Message任务的到来
quit()函数用来销毁MessageQueue同时达到结束任务退出Thread
总结
- 消息机制是以MessageQueue为核心,Looper为持有者,Handler为使用接口,Message为通信数据的架构模式
- Android应用是以消息机制为框架运行的
- AndroidUI线程是通过Looper来区分的,应用启动后执行ActivityThread类的main函数,调用Looper.preparMainLooper()初始化当前线程的Looper对象,同时意味着当前线程被当作UI线程,然后执行activity.attach()来加载应用,最后调用Looper.loop()等待任务的到来
- MainLooper与普通looper的区别是创建Looper的quitAllowed参数,这个参数用来标记MessageQueue是否可以退出,而looper.loop()函数会一直等待着下一次任务的到来,除非looper中的MessageQueue主动退出,而主线程/UI线程退出就意味着程序运行结束了,UI线程的Looper也就是MainLooper是不能退出的
- 消息的分发顺序依次是message.callback->handler.callback->handler.handleMessage
- Message是链表结构,sendMessage会按照执行时间的先后顺序添加到MessageQueue队列中
- sendMessage会把Handler对象赋值给message的target对象,在MessageQueue队列中获取到Message后利用target对象来回调消息事件

 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号