导航号,我的单页导航
文章目录

前言

对于Android消息机制源码分析已经烂大街了,之前跟网上大佬走了一遍,还记录了一下(《Android消息机制之一基础简介(1)》)。

我们知道消息机制涉及如下几个类

  1. Message消息

  2. MessageQueue消息队列

  3. Handler消息的分发者和处理者

  4. Looper消息循环的泵

涉及文件

frameworks\base\core\java\android\os\Looper.java
frameworks\base\core\java\android\os\MessageQueue.java
frameworks\base\core\java\android\os\Message.java
frameworks\base\core\java\android\os\Handler.java

正文

Android应用开发时,如果想在子线程里创建自己的Handler消息处理,需要如下步骤:

private class MyThread extends Thread {
    @Override
    public void run() {
        super.run();
        //1. Looper准备
        Looper.prepare();
        
        //2. Handler的创建
        # 方式一
        mThreadHandler = new Handler(Looper.myLooper(), new Handler.Callback() {
            @Override
            public boolean handleMessage(Message msg) {
                return false;
            }
        });
        # 方式
        mThreadHandler = new Handler(Looper.myLooper()){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
            }
        };
        
       // PS:此时(2和3之间)也可以让Handler发现消息,但真正处理是在Looper进入循环之后。
       
       //3.进入死循环,如果不是退出,此句之后的代码不会执行。
        Looper.loop();
    }
}
#线程启动,启动之后mThreadHandler可以发送消息
MyThread mMyThread = new MyThread();
mMyThread.start();

Looper.java

先看Looper的构造函数,然看看常用的方法。

Looper的构造函数

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

带一个参数quitAllowed,最终是传入了MessageQueue(),表示是否可以退出。

ActivityThread中的主线程Looper,是不允许退出的(quitAllowed=false);而对于其他子线程创建的,默认是可以退出的。

准备
prepare()

不是应用主线程时调用这个,主要调用prepare(true),true表示运行退出。

public static void prepare() {
    prepare(true);
}

调用的另外一个prepare(boolean quitAllowed),传入的参数是true。

prepareMainLooper()

应用主线程时调用这个,主要调用prepare(false),false表示不允许退出,也就是调用quit()后会抛出异常。

public static void prepareMainLooper() {
    prepare(false);
    synchronized (Looper.class) {
        //判断是否之前初始化过,这是确保只能初始化一次
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
}
prepare(boolean)
private static void prepare(boolean quitAllowed) {
    //从线程本地变量中获取looper
    if (sThreadLocal.get() != null) {
        throw new RuntimeException("Only one Looper may be created per thread");
    }
    //保存Looper对象到线程本地变量中
    sThreadLocal.set(new Looper(quitAllowed));
}

ThreadLocal是线程的局部变量,每个线程中的值都是独立存在、互不影响。因此,一个线程内的Looper都是独一份的!

static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
循环
loop()

主要是获取到Looper中创建的MessageQueue对象,然后进入死循环,不断的从MessageQueue对象中拿取消息。如果没有消息就阻塞在queue.next()。

public static void loop() {
    //获取当前的Looper,通过sThreadLocal保存的
    final Looper me = myLooper();
    //获取Looper中的消息队列
    final MessageQueue queue = me.mQueue;
    //略
    //进入死循环
    for (;;) {
        //从消息队列中拿去消息,存在阻塞
        Message msg = queue.next();
        //一般情况下不会为null的,具体看消息队列中的next()
        if (msg == null) {
            return;
        }
        //略
        try {
           //msg.target就是Handler
            msg.target.dispatchMessage(msg);
            dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
        } finally {
        }
        //回收消息
        msg.recycleUnchecked();
    }
}
退出
quit()
public void quit() {
    //调用消息队列的quit
    mQueue.quit(false);
}
quitSafely()
public void quitSafely() {
    mQueue.quit(true);
}

真正干活的是MessageQueue,只不过传入的值不一样,一个false一个true。至于有啥区别,看后面MessageQueue的介绍。

获取Looper
getMainLooper()

获取主线程的sMainLooper,这个也是在初始化是通过sMainLooper=myLooper()获取的。

public static Looper getMainLooper() {
    synchronized (Looper.class) {
        return sMainLooper;
    }
}
myLooper()

从sThreadLocal中获取Looper

public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}
获取消息队列
getQueue()

返回消息队列

public @NonNull MessageQueue getQueue() {
    return mQueue;
}

上面方法是Looper中比较常用的。

Handler.java

Handler消息的分发者和处理者,主要涉及消息的分发,消息的移除和消息的处理。

Handler的构造函数好多个,这里只是介绍常用的几个。

# 下面两个比较常用
public Handler(Looper looper) {
    this(looper, null, false);
}
public Handler(Looper looper, Callback callback) {
    this(looper, callback, false);
}

# 但最终都进入下面这种构造函数
public Handler(Looper looper, Callback callback, boolean async) {
    mLooper = looper;
    mQueue = looper.mQueue;
    mCallback = callback;
    mAsynchronous = async;
}

也就是这里传入了Looper,并且让Handler获取Looper中对的MessageQueue,后面消息的分发需要通过这个的。

如果传入了mCallback,那就是消息的处理可能是通过这个接口处理。

消息发送

Handler是消息的发送者,里面提供了很多丰富的接口。有postxx()和sendxx()两种。

下面就简单的附上常用的方法

post()
public final boolean post(Runnable r){
   return  sendMessageDelayed(getPostMessage(r), 0);
}
postDelayed()
public final boolean postDelayed(Runnable r, long delayMillis){
    return sendMessageDelayed(getPostMessage(r), delayMillis);
}

上面post中需要注意的是,这里传入的是Runnable,因此需要通过getPostMessage()转换一下成为Message。

private static Message getPostMessage(Runnable r) {
    Message m = Message.obtain();
    //对callback赋值,消息处理时会对此进行判断
    m.callback = r;
    return m;
}
sendEmptyMessage()
public final boolean sendEmptyMessage(int what){
    return sendEmptyMessageDelayed(what, 0);
}
sendEmptyMessageDelayed()
public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
    Message msg = Message.obtain();
    msg.what = what;
    return sendMessageDelayed(msg, delayMillis);
}
sendMessageDelayed()
public final boolean sendMessageDelayed(Message msg, long delayMillis){
    if (delayMillis < 0) {
        delayMillis = 0;
    }
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

等等好几个send,其实最终都是通过enqueueMessage()分发。

enqueueMessage()
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    //传入当前发送消息的Handler
    msg.target = this;
    //Handler初始化时传入的,默认是false
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    //最终进入消息队列中
    return queue.enqueueMessage(msg, uptimeMillis);
}
消息移除

消息移除就是把定时的消息从消息队列中移除掉。

下面几个是比较常用的方法

removeCallbacks()
public final void removeCallbacks(Runnable r){
    mQueue.removeMessages(this, r, null);
}
removeMessages()
public final void removeMessages(int what) {
    mQueue.removeMessages(this, what, null);
}
removeCallbacksAndMessages()
public final void removeCallbacksAndMessages(Object token) {
    mQueue.removeCallbacksAndMessages(this, token);
}

最终消息的移除都是进入了消息队列中。

消息处理

上面Looper中介绍到Looper.loop()中会一直循环,如果有消息就会进入消息的分发

msg.target.dispatchMessage(msg);

这里的msg.target就是Handler发送消息是带上的this,也就是发送者Handler。

dispatchMessage()
public void dispatchMessage(Message msg) {
	//callback就是Runnable
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
    	//如果Handler中创建带入了Callback,就走Callback的回调。
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        //默认走这,也就是创建是需要重写
        handleMessage(msg);
    }
}

msg.callback的赋值一般是post时封装Runnable的,可以看上面getPostMessage()方法。

private static void handleCallback(Message message) {
	//也就是调用run()方法
    message.callback.run();
}
消息存在

判断是否有某个消息,这个也很常见。

不过最终处理依旧是消息队列。看后面介绍。

hasMessages()
public final boolean hasMessages(int what) {
    return mQueue.hasMessages(this, what, null);
}
hasCallbacks()
public final boolean hasCallbacks(Runnable r) {
    return mQueue.hasMessages(this, r, null);
}

Message.java

Message也就是我们常说的信使,用于封装传递的信息。

消息中存储了很多有用的信息,比如我们常用的

what       消息类型code
target     记录Handler发送的
when       什么时候处理
obj        用于传输对象
arg1       存储int类型值1
arg2       存储int类型值2
callback   Runnable类型

创建信息的方式主要有

# 1
Message message = new Message();
# 2 
Message message = mHandler.obtainMessage();
Message message = obtainMessage(int what)

一般推荐后面的方式,这里会重复利用创建的Message,也就不用每次都new一个新的。

我们看看为啥推荐mHandler.obtainMessage()。

# Handler.java
public final Message obtainMessage(){
    return Message.obtain(this);
}

我们看看obtain(this)是怎么处理的。

obtain(Handler)
# Message.java
public static Message obtain(Handler h) {
    Message m = obtain();
    //target是Handler
    m.target = h;
    return m;
}
obtain()
private static Message sPool;

public static Message obtain() {
    synchronized (sPoolSync) {
        if (sPool != null) {
            Message m = sPool;
            //获取下一个Message,感觉像链表
            sPool = m.next;
            m.next = null;
            m.flags = 0; // clear in-use flag
            sPoolSize--;
            return m;
        }
    }
    return new Message();
}

上面就判断sPool是否为null,是就创建新的Message,否则就返回之前创建过的,同时清除使用过的flags。

用上面方式,不用每次都创建Message

至于回收,是在recycleUnchecked()中处理的。

recycleUnchecked()

回收使用过的Message

void recycleUnchecked() {
	//设置FLAG_IN_USE标签
    flags = FLAG_IN_USE;
    //清除Message中所以信息
    what = 0;
    arg1 = 0;
    arg2 = 0;
    obj = null;
    replyTo = null;
    sendingUid = -1;
    when = 0;
    target = null;
    callback = null;
    data = null;
    synchronized (sPoolSync) {
    	//如果没有达到MAX_POOL_SIZE(50)限制,就保存。
        if (sPoolSize < MAX_POOL_SIZE) {
            next = sPool;//有点链表的意思
            sPool = this;
            sPoolSize++;
        }
    }
}

MessageQueue.java

MessageQueue消息队列,消息存储,消息获取,以及推出消息循环都在这。

退出

上面说过,退出Looper调用的是MessageQueue.quit()。

void quit(boolean safe) {
	//先判断是否可以退出,上面Looper.prepar()中传入的
    if (!mQuitAllowed) {
        throw new IllegalStateException("Main thread not allowed to quit.");
    }
    synchronized (this) {
    	//判断是否正在退出。防止多次进入
        if (mQuitting) {
            return;
        }
        //设置退出flag
        mQuitting = true;
        if (safe) {
            removeAllFutureMessagesLocked();
        } else {
            removeAllMessagesLocked();
        }
        //nativeWake用于唤醒功能
        nativeWake(mPtr);
    }
}

PS : mQuitting为true,next会进行判断然后是否退出

由于safe的值不同,退出会进入两个不一样的循环。

  1. safe为true时,清空延迟消息(安全)

  2. safe为false时,清空所有消息

removeAllFutureMessagesLocked()

为啥说这个是安全的?因为这里有条件判断,如果正在处理的消息,会等待其处理完。

(1)如果Message还没处理(p.when > now),就全部移除,调用的是removeAllMessagesLocked()

(2)如果Message正在处理(p.when == now),就等待其处理完,然后定位到消息队列中还没处理的消息并移除。

具体看下面,有注释

private void removeAllFutureMessagesLocked() {
    final long now = SystemClock.uptimeMillis();
    Message p = mMessages;
    if (p != null) {
    	//查看当前消息p是否在处理中
        if (p.when > now) {
        	//没有处理,清空所有消息
            removeAllMessagesLocked();
        } else {
        	//表示p消息正在处理,就查询其他未处理的消息
            Message n;
            //定位到到消息队列中还没处理的消息
            for (;;) {
            	//获取下一个消息
                n = p.next;
                if (n == null) {
                	//如果下一个消息为null,表示后面没有了,直接返回
                    return;
                }
                //如果下一个还没开始处理,退出循环,
                if (n.when > now) {
                    break;
                }
                //如果上面下个消息也在处理,继续查询下一个,p赋值为n,循环再次获取n.next.
                p = n;
            }
            p.next = null;
            do {
            	//遍历还没处理的消息并清除
                p = n;
                n = p.next;
                p.recycleUnchecked();
            } while (n != null);
        }
    }
}
removeAllMessagesLocked()

这里不安全,是因为不管三七二一,消息队列中的所有消息都移除。有点暴力!

private void removeAllMessagesLocked() {
    Message p = mMessages;
    while (p != null) {
        Message n = p.next;
        p.recycleUnchecked();
        p = n;
    }
    mMessages = null;
}
消息存储

把消息存储到消息队列,上面Handler中介绍过posetxx()和sendxx(),最终都进入了消息队列中的enqueueMessage()。

enqueueMessage()

这里主要如下功能:

  1. 根据需求把Message放于队列第一个

  2. 如果没有需求就按照时间排序

  3. 根据needWake值看是否需要唤醒,通知消息分发

boolean enqueueMessage(Message msg, long when) {
    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;
		//队列中第一个消息,可能为null
        Message p = mMessages;
        boolean needWake;
		//如果p为nul,表示队列中没有消息
		// when = 0 表示当前消息需要放在消息队列前。
		// when < p.when 表示当前消息处理时间比上一个消息p更早
        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; // invariant: p == prev.next
            prev.next = msg;
        }
        if (needWake) {
			//根据是否需要换线,然后通过nativeWake去通知唤醒
            nativeWake(mPtr);
        }
    }
    return true;
}
消息获取

上面介绍过,Looper进入loop()循环是通过消息队列拿去消息的。

Message msg = queue.next();
next()

next()就是从消息队列中获取消息。

  1. 如果没有消息,就阻塞

  2. 如果有消息(消息队列的第一个,下面也是一样),但msg.target=null就查询异步消息

  3. 如果有消息,但还没到处理时间,就等待

  4. 如果有消息,到了处理时间,就返回需要处理的消息

    这里部分还不太懂。

Message next() {
    final long ptr = mPtr;
    if (ptr == 0) {
        return null;
    }
    int pendingIdleHandlerCount = -1; // -1 only during first iteration
    int nextPollTimeoutMillis = 0;
	//这里也进入了死循环,要么退出要么获取到消息
    for (;;) {
        if (nextPollTimeoutMillis != 0) {
            Binder.flushPendingCommands();
        }
	    //阻塞操作,等待nextPollTimeoutMillis时长,或者消息队列被唤醒,都会返回
        nativePollOnce(ptr, nextPollTimeoutMillis);
        synchronized (this) {
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages;
		   //当消息的Handler(msg.target)为空时,则查询异步消息
            if (msg != null && msg.target == null) {
                do {
                    prevMsg = msg;
                    msg = msg.next;
					//当查询到异步消息,则立刻退出循环
                } while (msg != null && !msg.isAsynchronous());
            }
            if (msg != null) {
			   //当消息触发时间大于当前时间,延迟
                if (now < msg.when) {
                    nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                } else {
                   // 获取一条消息,因为要处理,不阻塞
                    mBlocked = false;
                    //msg.target == null时才会对这个赋值,一般为null
                    if (prevMsg != null) {
                        prevMsg.next = msg.next;
                    } else {
                        mMessages = msg.next;//指向下一个消息
                    }
                    msg.next = null;
				  //设置消息的使用状态
                    msg.markInUse();
                    return msg;
                }
            } else {
                //没有消息
                nextPollTimeoutMillis = -1;
            }
			//消息正在退出,返回null
            if (mQuitting) {
                dispose();
                return null;
            }
            //消息队列的第一个消息时(pendingIdleHandlerCount默认-1)
            //并且(1)当消息队列为空(2)消息还没到处理时间
            if (pendingIdleHandlerCount < 0
                    && (mMessages == null || now < mMessages.when)) {
                pendingIdleHandlerCount = mIdleHandlers.size();
            }
            if (pendingIdleHandlerCount <= 0) {
				//没有idle handlers 需要运行,则循环并等待。
                mBlocked = true;
                continue;
            }
            if (mPendingIdleHandlers == null) {
                mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
            }
            mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
        }
        //会运行idle handlers,执行完成,重置pendingIdleHandlerCount
        for (int i = 0; i < pendingIdleHandlerCount; i++) {
            final IdleHandler idler = mPendingIdleHandlers[i];
			//去掉handler的引用
            mPendingIdleHandlers[i] = null; // release the reference to the handler
            boolean keep = false;
            try {
				//idle时执行的方法
                keep = idler.queueIdle();
            } catch (Throwable t) {
                Log.wtf(TAG, "IdleHandler threw exception", t);
            }
            if (!keep) {
                synchronized (this) {
                    mIdleHandlers.remove(idler);
                }
            }
        }
		//重置idle handler个数为0,以保证不会再次重复运行
        pendingIdleHandlerCount = 0;
		//当调用一个空闲handler时,一个新message能够被分发,因此无需等待可以直接查询pending message.
        nextPollTimeoutMillis = 0;
    }
}
消息移除

就是Handler中调用的方法,主要有如下

removeMessages()

removeMessages()这个方法有两个,只是传入的参数不一样,这里只介绍一个。

void removeMessages(Handler h, Runnable r, Object object) {
    if (h == null || r == null) {
        return;
    }
    synchronized (this) {
    	//mMessages是消息头,也是消息队列中的第一个消息
        Message p = mMessages;
        /*
		这里写法有点怪
		1. p != null 必要条件
		2. p.target == h 必要条件
		3. p.callback == r 必要条件
		4. (object == null || p.obj == object) 必要条件
		也就是上面4个条件都为true,才可以进入while中的循环。要不然一次都不执行。

		写成这样
		设计者可能考虑第一个第二个第三个消息都满足条件,直到不满足条件的消息就停止。
		*/
        while (p != null && p.target == h && p.callback == r
               && (object == null || p.obj == object)) {
            Message n = p.next;
            mMessages = n;
            p.recycleUnchecked();//清空并回收
            p = n;
        }
        while (p != null) {
            Message n = p.next;
		   //遍历后面符合条件的,如果符合就移除
            if (n != null) {
                if (n.target == h && n.callback == r
                    && (object == null || n.obj == object)) {
                    Message nn = n.next;
                    n.recycleUnchecked();
                    p.next = nn;
                    continue;
                }
            }
            p = n;
        }
    }
}
removeCallbacksAndMessages()

哈哈,这个很熟悉吧,网上推荐Handler在Activity退出时通过这个移除所有消息,防止内存泄漏。

 mHandler.removeCallbacksAndMessages(null);

今天我们就看一下其中是怎么处理的。

void removeCallbacksAndMessages(Handler h, Object object) {
    if (h == null) {
        return;
    }
    synchronized (this) {
        Message p = mMessages;
		/**
		跟removeMessages()一样,也是先从头消息开始。
		1. p != null 必要条件
		2. p.target == h 必要条件,只移除当前Handler
		3. (object == null || p.obj == object) 必要条件
		
		也就是上面3个条件都为true,才可以进入while中的循环。要不然一次都不执行。
		
		写成这样
		设计者可能考虑第一个第二个第三个消息都满足条件,直到不满足条件的消息就停止。
		*/
        while (p != null && p.target == h
                && (object == null || p.obj == object)) {
            Message n = p.next;
            mMessages = n;
            p.recycleUnchecked();
            p = n;
        }
        while (p != null) {
			//遍历后面符合条件的,如果符合就移除
            Message n = p.next;
            if (n != null) {
                if (n.target == h && (object == null || n.obj == object)) {
                    Message nn = n.next;
                    n.recycleUnchecked();
                    p.next = nn;
                    continue;
                }
            }
            p = n;
        }
    }
}
消息存在

这个也有两个,只是参数不一样。

原理都一样,都是遍历消息队列,根据条件查询。这里只介绍一个。

hasMessages()
boolean hasMessages(Handler h, int what, Object object) {
    if (h == null) {
        return false;
    }
    synchronized (this) {
        Message p = mMessages;
        while (p != null) {
        	//对比查询条件
            if (p.target == h && p.what == what && (object == null || p.obj == object)) {
                return true;
            }
            p = p.next;
        }
        return false;
    }
}

参考文章

  1. Android消息机制1-Handler(Java层)

  2. Android 消息机制系列(3)——终止消息循环

  3. Android消息机制之一基础简介(1)

  4. HandlerThread 的使用及源码解析

© 版权声明
导航号,我的单页导航

暂无评论

暂无评论...