AndroidQ Handler消息机制(java层)

本篇文章基于AndroidQ分析Handler消息机制java层的实现
主要从message消息池,handler消息投递,Looper通过loop方法向MessageQueue获取消息,handler消息分发顺序,MessageQueue的同步屏障几方面分析

Handler消息机制一般用于线程间的通信,A线程可以通过消息机制向B线程发送消息,只要获取到B线程的Looper对象,我们用的最多的就是在子线程获取数据之后在主线程更新UI,我们从一个简单的例子开始源码分析:


public class MainActivity extends Activity {
    private Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            Log.d("dongjiao","handleMessage...msg.what = :"+msg.what);
        }
    };
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        new Thread(new Runnable() {
            @Override
            public void run() {
                Message message = Message.obtain();
                message.what = 10;
                mHandler.sendMessage(message);
            }
        }).start();
     }

输出:

02-17 09:04:15.138 15536 15536 D dongjiao: handleMessage...msg.what = :10,
currentThread = : Thread[main,5,main]

这种handler的用法只要学过几天Android应该都会用,我们接下来通过源码来分析它的具体实现。

消息机制主要由四部分组成(Handler,Message,MessageQueue,Looper),Handler:向MessageQueue投递Message,并且进行Message的处理
Message:作为消息的载体,携带具体数据
MessageQueue:一系列Message的容器
Looper:内部对MessageQueue进行无限循环,有消息就取出交给Handler处理,没消息就阻塞

Message消息池

Android系统以消息来驱动,内部会有大量的消息,为了节约内存以及提高效率采用消息池对Message进行复用,从上面例子我们可以看到,Message采用obtain来创建而不是new,来看下源码

Message.java

Message实现了Parcelable接口表明Message不仅能在线程间传递还能跨进程传输

public final class Message implements Parcelable {
	//当Handler发送多个消息时,用作不同消息处理的区分
	public int what;
	//当你传输的数据仅仅是int型时优先使用arg1,arg2
	public int arg1;
    public int arg2;
    //Message携带的数据
	public Object obj;
	//Message执行的延迟时间
	public long when;
	//处理此Message的目标Handler
	Handler target;
	//Message的回调,如果设置了此回调,handler的handleMessage方法
	//将不会被执行
	Runnable callback;
	//Message采用链表的形式进行复用,next指向下一个消息
	Message next;
	//Message消息池的头指针
	private static Message sPool;
	//Message消息池的大小
	private static int sPoolSize = 0;
}

obtain这个方法有很多的重载方法,我们选择最常见的无参方法来分析

Message.obtain

 public static Message obtain() {
        synchronized (sPoolSync) {
        	//如果sPool不为空,则从消息池取一个Message
            if (sPool != null) {
            	//从消息池中取出头部这个消息
                Message m = sPool;
                //sPool继续指向下一个消息
                sPool = m.next;
                //将取出的消息的链断掉
                m.next = null;
                //修改此消息的flag为0
                m.flags = 0; // clear in-use flag
                //消息池大小减1
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }

sPool总是指向消息链表的头部,首次创建Message时sPool为空,就需要通过new Message的方式创建Message,我们看下sPool首次是在哪里赋值的,是在recycleUnchecked中,看名字以及注释我们知道此方法是Message使用完毕之后会调用,也就是说我们创建的第一个消息使用完毕之后,会赋值给sPool

void recycleUnchecked() {
        //当Message使用完成之后会将Message的一系列数据初始化
        flags = FLAG_IN_USE;
        what = 0;
        arg1 = 0;
        arg2 = 0;
        obj = null;
        replyTo = null;
        sendingUid = UID_NONE;
        workSourceUid = UID_NONE;
        when = 0;
        target = null;
        callback = null;
        data = null;

        synchronized (sPoolSync) {
        //MAX_POOL_SIZE为50,作为消息池的大小,当sPoolSize没有到50时,
        //则此消息应该加入消息池
            if (sPoolSize < MAX_POOL_SIZE) {
            	//如果是第一次消息回收next = sPool = null
                next = sPool;
                //将当前这个消息赋值给sPool,我们可以看到sPool总是指向
                //最后调用recycleUnchecked方法的Message
                sPool = this;
                //消息池大小加1
                sPoolSize++;
            }
        }
    }

从obtain和recycleUnchecked方法中我们能够看出来Message内部消息池采用链表的形式,sPool指向链表的头,每次取出消息是从链表头部开始,每次消息回收也是加在链表头部

Message消息的投递

接着我们来看下Handler如何向MessageQueue投递消息,首先看下Handler的构造方法,例子中使用Handler无参构造最终调用到如下这个构造方法中

public Handler(@Nullable Callback callback, boolean async) {
		//获取当前线程的Looper对象,后面分析
        mLooper = Looper.myLooper();
        //如果Looper为空会抛如下这个异常,这个异常相信Android初学者
        //遇见过很多次
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        //获取当前线程Looper的MessageQueue
        mQueue = mLooper.mQueue;
        //Handler内部回调
        mCallback = callback;
        //是否是异步消息,后面分析
        mAsynchronous = async;
    }

Handler.sendMessage

public final boolean sendMessage(@NonNull Message msg) {
        return sendMessageDelayed(msg, 0);
    }
public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {
        if (delayMillis < 0) {
            delayMillis = 0;
        }
        return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
    }
public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {
        MessageQueue queue = mQueue;
        if (queue == null) {
            RuntimeException e = new RuntimeException(
                    this + " sendMessageAtTime() called with no mQueue");
            Log.w("Looper", e.getMessage(), e);
            return false;
        }
        return enqueueMessage(queue, msg, uptimeMillis);
    }
 private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
            long uptimeMillis) {
        //msg.target代表处理此消息的Handler
        msg.target = this;
        msg.workSourceUid = ThreadLocalWorkSource.getUid();
		//是否是异步消息,后面分析
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        //最终调用MessageQueue的enqueueMessage将此Message投递进MessageQueue
        return queue.enqueueMessage(msg, uptimeMillis);
    }

Handler投递消息很简单,就是一系列sendXXX,调到了MessageQueue中,不管是哪个sendXXX方法,最终一定会调到MessageQueue.enqueueMessage中

Looper以及MessageQueue的创建

我们知道UI线程不需要我们手动创建Looper,原因是每个应用程序进程创建时就已经创建了Looper并开启了消息循环,在ActivityThread的main方法中

public static void main(String[] args) {

        Looper.prepareMainLooper();
        .......
        ActivityThread thread = new ActivityThread();
        thread.attach(false, startSeq);
		......
        Looper.loop();
    }

Looper.prepareMainLooper

 public static void prepareMainLooper() {
 		//注意UI线程调用prepare时传递false,代表不允许Looper退出
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }

prepare

此方法很简单,通过ThreadLocal保存当前线程的Looper对象,ThreadLocal用来存储线程私有变量,起到线程隔离的目的,具体原理我们这里不去分析,用法非常简单,到这里我们知道每个线程自己创建的Looper对象一定只能由自己线程访问

 private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        sThreadLocal.set(new Looper(quitAllowed));
    }

Looper构造方法里面创建了MessageQueue,并保存了当前线程

private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

MessageQueue构造方法中调用了nativeInit方法做了很多事,此方法我们在下一篇对消息机制native层的分析中去细看

MessageQueue(boolean quitAllowed) {
        mQuitAllowed = quitAllowed;
        mPtr = nativeInit();
    }

好了,到这里Looper以及MessageQueue的初始化已经完成,MessageQueue已经创建了,我们可以看下MessageQueue内部是如何保存Message的

MessageQueue.enqueueMessage

MessageQueue内部的消息其实也是通过链表的结构存储的,和Message消息池类似

boolean enqueueMessage(Message msg, long when) {
		//如果有message而没有Handler则抛出异常
        if (msg.target == null) {
            throw new IllegalArgumentException("Message must have a target.");
        }
        //如果此Message正在使用中则抛出异常
        if (msg.isInUse()) {
            throw new IllegalStateException(msg + " This message is already in use.");
        }

        synchronized (this) {
        	//如果MessageQueue正在退出则抛出异常
            if (mQuitting) {
                IllegalStateException e = new IllegalStateException(
                        msg.target + " sending message to a Handler on a dead thread");
                Log.w(TAG, e.getMessage(), e);
                msg.recycle();
                return false;
            }
			//将此Message添加flag,代表正在使用
            msg.markInUse();
            //when代表此Message执行的延迟时间
            msg.when = when;
            //mMessages指向Message消息链表的头部
            Message p = mMessages;
            //是否需要唤醒
            boolean needWake;
            //p == null代表此时没有消息,when == 0代表此消息需要立即执行
            //when < p.when代表新增加的消息比此时MessageQueue中的消息头
            //的消息还要先执行(消息队列的顺序是when最小排在头部,when最大排在尾部)
            if (p == null || when == 0 || when < p.when) {
                //所以如果进到此条件则代表增加的这条消息是最先执行的,应该加到
                //消息队列头部
                msg.next = p;
                mMessages = msg;
                //将当前MessageQueue的状态赋值给needWake,以便能在阻塞时唤醒
                //MessageQueue
                needWake = mBlocked;
            } else {
                //p.target == null && msg.isAsynchronous()代表是否开启
                //同步屏障,后面分析
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                //循环整个Message链表,将此新增的消息插入到合适位置
                for (;;) {
                    prev = p;
                    p = p.next;
                    //当找到p == null代表已经到链表尾部,或者下一个msg的when
                    //大于当前新增消息的when
                    if (p == null || when < p.when) {
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                //链表增加元素的常规操作,断开链next,将msg插入进去
                msg.next = p; 
                prev.next = msg;
            }

            //唤醒MessageQueue依靠native层
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }

enqueueMessage方法其实就是一个链表的插入操作,规则是按照msg的执行时间排序,时间越短越靠近头部

我们接着分析Looper对消息的循环取出

Looper.loop

public static void loop() {
		//获取当前线程关联的Looper对象
        final Looper me = myLooper();
        //looper为空则抛出异常
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        //获取Looper关联的MessageQueue
        final MessageQueue queue = me.mQueue;
		//死循环的获取MessageQueue的消息
        for (;;) {
        	//获取消息的核心方法,
            Message msg = queue.next(); // might block
            //如果获取到的msg为空则说明MessageQueue已经退出,则Looper也需要
            //退出循环
            if (msg == null) {
            	
                return;
            }
			//待queue.next()方法分析完之后再接着分析后面代码
           	......
           		......
    }

MessageQueue.next

Message next() {
        //mPtr保存native层MessageQueue的指针
        final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }

        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        //nextPollTimeoutMillis代表当前消息的执行时间,0代表立即执行,-1代表
        //此时MessageQueue没有消息
        int nextPollTimeoutMillis = 0;
        //无限循环的取出消息
        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }
			//此方法是消息机制的核心,会在下一篇native层消息机制进行分析
            nativePollOnce(ptr, nextPollTimeoutMillis);

            synchronized (this) {
                //获取当前时间
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                //前面分析过,mMessages指向消息链表头部
                Message msg = mMessages;
                //如果msg不为空handler却为空说明此时Looper需要获取异步消息
                if (msg != null && msg.target == null) {
                    do {
                    	//典型的链表循环
                        prevMsg = msg;
                        msg = msg.next;
						//退出条件是找到设置了Asynchronous的消息或者
						//直到链表尾部也没找到,说明此链表不含异步消息
                    } while (msg != null && !msg.isAsynchronous());
                }
                //msg不为空代表此消息链表中是有消息的
                if (msg != null) {
                	//还没到消息执行时间
                    if (now < msg.when) {
                    	//则重置nextPollTimeoutMillis为等待执行的时间
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {//需要取出消息开始执行
                        //将mBlocked置为false代表MessageQueue不需要阻塞
                        mBlocked = false;
                        //prevMsg指向异步消息前一个消息,不为空代表有异步消息
                        if (prevMsg != null) {
                        	//链表取出消息的常规操作
                            prevMsg.next = msg.next;
                        } else {
                        	//如果没有异步消息,则取出消息队列头部的消息,并让
                        	//mMessages继续指向下一个消息
                            mMessages = msg.next;
                        }
                        //断开取出消息的链
                        msg.next = null;
                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);					
                        //标记此消息为正在使用
                        msg.markInUse();
                        //返回取出的消息
                        return msg;
                    }
                } else {
                    //消息队列中没有消息
                    nextPollTimeoutMillis = -1;
                }

                //如果消息队列退出
                if (mQuitting) {
                    dispose();
                    return null;
                }

                //省略的代码是关于pendingIdleHandler的处理,这个handler会在
                //MessageQueue为空时执行..不去细看了
                	.....
            pendingIdleHandlerCount = 0;
            nextPollTimeoutMillis = 0;
        }
    }

我们已经分析完了Handler消息机制中最重要的消息循环与获取的流程,做一个总结:

  1. Message在MessageQueue中以链表的形式存在,并且是按when执行时间从小到大排序,mMessages总是指向消息链表头部
  2. Loop会无限循环MessageQueue,有消息就取出,没有消息就阻塞(阻塞的核心实现在native层,我们下一篇进行分析)
  3. MessageQueue中有一种异步消息,它的优先级高于同步消息(后面分析)
  4. 当消息队列为空时会执行pendingIdleHandler

我们继续回到Looper中看去到Message后的处理流程:

public static void loop() {
		......
        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
    
                return;
            }
			//省略了很多log相关代码,主要帮助开发者实现监听UI线程卡顿
           	......
            try {
            	//msg取到之后调用了msg的target也就是handler的dispatchMessage
            	//方法进行消息处理
                msg.target.dispatchMessage(msg);
                }
				.......
				//msg的回收复用,前面已经讲过,此方法会将使用过的msg加入消息池
				//进行复用
            msg.recycleUnchecked();
        }
    }

handler.dispatchMessage

我们看到,handler处理消息有一个优先级,首先msg的callback,其次handler的mCallback,最后才是handler的handleMessage方法

   public void dispatchMessage(@NonNull Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            handleMessage(msg);
        }
    }

看一个实际使用的优先级:

 1. hanlder.post(runnable);

 2. private Handler mHandler2 = new Handler(new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            return false;
        }
    });
    
 3.private Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
        }
    };

handler异步消息

最后我们再来看看异步消息,在Android源码绘制流程中使用了异步消息,目的是尽可能快的完成View的绘制

ViewRootImpl.java

 void scheduleTraversals() {
        ......
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            .....
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
           ......
    }

Choreographer.java

  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) {
            .....
                Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
                msg.arg1 = callbackType;
                msg.setAsynchronous(true);
                mHandler.sendMessageAtTime(msg, dueTime);
            }
        }
    }

先调用postSyncBarrier开启同步屏障,屏蔽同步消息,接着设置setAsynchronous为true,将此消息设置为异步,则优先处理异步消息

同步屏障原理

我们从handler投递msg的分析知道,无论那种投递方式,只要是通过handler的sendXXX方法,最终一定会调用到enqueueMessage方法,

private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
            long uptimeMillis) {
        msg.target = this;
        msg.workSourceUid = ThreadLocalWorkSource.getUid();

        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

此方法第一行代码要做的事就是msg.target = this,而从MessageQueue.next方法中,我们知道要优先处理异步消息有两个条件,一是**(msg != null && msg.target == null),然后才循环消息链表,找到msg.isAsynchronous()为true的msg**,

Message next() {
	......
		for (;;) {
				if (msg != null && msg.target == null) {
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
			}
	......
}

我们可以得出结论,同步屏障就是向MessageQueue投递一个handler为空的msg,有了这个msg,从此msg开始,之后的所有同步消息都会被屏蔽,只会处理isAsynchronous为true的异步消息

msg.setAsynchronous(true)

设置FLAG_ASYNCHRONOUS标示此msg为异步消息

public void setAsynchronous(boolean async) {
        if (async) {
            flags |= FLAG_ASYNCHRONOUS;
        } else {
            flags &= ~FLAG_ASYNCHRONOUS;
        }
    }

开启同步屏障:

MessageQueue.postSyncBarrier

   public int postSyncBarrier() {
        return postSyncBarrier(SystemClock.uptimeMillis());
    }

   private int postSyncBarrier(long when) {
        
        synchronized (this) {
            final int token = mNextBarrierToken++;
            //创建没有handler的msg
            final Message msg = Message.obtain();
            msg.markInUse();
            //时间是当前系统时间
            msg.when = when;
            msg.arg1 = token;

            Message prev = null;
            Message p = mMessages;
            if (when != 0) {
            	//找到晚于当前时间的msg
                while (p != null && p.when <= when) {
                    prev = p;
                    p = p.next;
                }
            }
            //将新创建的handler为空的msg插入到prev后面
            if (prev != null) { // invariant: p == prev.next
                msg.next = p;
                prev.next = msg;
            } else {
            	//说明msg应该插到消息头部
                msg.next = p;
                mMessages = msg;
            }
            return token;
        }
    }

我们总结下使用异步消息,两个条件:

  1. 开启同步屏障即投递一个handler为空的msg
  2. 将需要发送的msg设置为异步即调用setAsynchronous(true)

并且从源码中我们知道同步屏障只能屏蔽消息链表中添加空handler的msg之后的同步消息

好了,到这里本篇Handler消息机制(java层)就已经分析完毕,主要从源码分析了message消息池,handler消息投递,Looper通过loop方法向MessageQueue获取消息,handler消息分发顺序,MessageQueue的同步屏障。
另外handler的核心:阻塞与唤醒依靠native层进行实现我们将在下一篇**handler消息机制(native层)**进行详细分析

发布了34 篇原创文章 · 获赞 48 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_34211365/article/details/104361041
今日推荐