Android消息机制好帮手—Looper、MessageQueue、Handler、Message

基础常识

1、 Android 中只有主线程才能更新UI,Android机制的规定,主要是为了保证UI绘制的流畅,防止并发出问题;

2、 Android 中主线程不允许阻塞超过5s,否则可能会ANR;

3、 Handler机制的核心就是实现线程之间通信

如何涉及一套线程间通信的机制?

Q1:如何涉及线程间通信?

A1:利用面向对象这个概念,创建一个对象obj,然后在子线程中给obj的一个属性赋值,然后在主线程中,操作obj对象,就可以拿到子线程赋值的结果,完成一次线程通信;

Q2:主线程什么时候去操作obj对象?

A2:因为不知道子线程什么时候给obj赋值,所以会开启一个死循环去观察obj对象中是否有值,有就进行操作;

Q3:死循环不会导致OOM吗,不会ANR吗?

A3:正常的死循环操作必然不行,可以利用Android中的阻塞式死循环,有消息就进行处理,没消息时就会休眠,当有消息来时将其唤醒处理消息;

常用的类及其作用
  • Handler:向消息池中发送各种消息事件以及处理相应的消息;

  • Message:需要传递的消息,可以传递消息;

  • MessageQueue:消息队列,内部使用的数据结构为单链表(便于插入与删除),主要功能为投递消息与取消息;

  • Looper:开启阻塞式死循环,遍历MessageQueue对象,从消息队列中取消息,按分发机制分发给目标处理者;

注意:一个线程中只能存在一个Looper,每个Looper维护一个MessageQueue,Looper是保存在ThreadLocal中的;

Q:为什么一个线程不能有多个循环,多个队列呢?

A:第一,性能问题,一个循环一个队列就能完成的,没必要多个;第二,顺序执行,如果在第一个循环中没有消息,线程就会休眠,这样后面的循环根本执行不到;第三,由于顺序执行,多个队列等于一个队列;

Handler消息机制原理

1、开启循环:每个线程只有一个Looper,用来阻塞式循环,每个Looper对应一个MessgeQueue;无限的循环遍历MessageQueue,如果里边有消息就去处理消息,消息处理完继续循环,没消息时进行休眠,这样就一直循环下去,也是我们程序为什么不会退出的原因;

2、发送消息: Handler创建的时候会根据线程去绑定,拿到线程对应的Looper和MessageQueue,发送消息的过程在其他线程把Message放到MessageQueue当中;MessageQueue按照Message的触发事件先后顺序来插入到队列中;

3、回调消息: Handler发送消息的时候会在发送的Message消息打上tag,当Looper遍历到Message对象,这个时候已经到了Handler的目标线程,Message.tag就拿到了Handler对象,然后就可以回调对应的方法handler.handleMessage();

消息机制源码理解
方式一、使用Handler.sendMessage()
1、创建对象:
public static void prepare() {
    prepare(true);
}

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));
}

sThreadLocal这样理解,类似一个和线程有关的HasMap,就是为了保证一个线程只有一个Looper对象,也叫容器单例;当多次调用prepare方法的时候,会抛出一个异常,这样也是为了保证Looper只有一个;如果Looper为空时,就会创建一个,并保存在ThreadLocal中;

private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    // 1. 创建1个消息队列对象(MessageQueue)
    // 即 当创建1个Looper实例时,会自动创建一个与之配对的消息队列对象(MessageQueue)
    
    mRun = true;
    mThread = Thread.currentThread();
}

程序启动的时候,ActivityThread在main方法中会自动调用Looper.prepareMainLooper与loop,代码如下:

public static void main(String[] args) {
    ... 
    
    Looper.prepareMainLooper(); 
    // 1. 为主线程创建1个Looper对象,同时生成1个消息队列对象(MessageQueue)
    // 方法逻辑类似Looper.prepare()
    // 注:prepare():为子线程中创建1个Looper对象
    
    ActivityThread thread = new ActivityThread(); 
    // 2. 创建主线程
    
    Looper.loop(); 
    // 3. 自动开启 消息循环
    
    ...
}

创建主线程时,会自动调用ActivityThread的1个静态的main();而main()内则会调用Looper.prepareMainLooper()为主线程生成1个Looper对象,同时也会生成其对应的MessageQueue对象;

而子线程的Looper对象则需手动通过Looper.prepare()创建,在子线程若不手动创建Looper对象 则无法生成Handler对象;

public static void loop() {   
    ...
    
    // 1. 获取当前Looper的消息队列
    final Looper me = myLooper();
    if (me == null) {
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    }
    // myLooper()作用:返回sThreadLocal存储的Looper实例;若me为null 则抛出异常
    // 即loop()执行前必须执行prepare(),从而创建1个Looper实例
            
    final MessageQueue queue = me.mQueue;
    // 获取Looper实例中的消息队列对象(MessageQueue)

    // 2. 消息循环(通过for循环)
    for (;;) {      
        // 2.1 从消息队列中取出消息
        Message msg = queue.next(); 
        if (msg == null) {
            return;
        }
        // next():取出消息队列里的消息
        // 若取出的消息为空,则线程阻塞 

        // 2.2 派发消息到对应的Handler
        msg.target.dispatchMessage(msg);
        // 把消息Message派发给消息对象msg的target属性
        // target属性实际是1个handler对象

        // 3. 释放消息占据的资源
        msg.recycle();
    }
    
    ...
}

消息循环,即从消息队列中获取消息、分发消息到Handler;

注意:

1、主线程的消息循环不允许退出,即无限循环;

2、子线程的消息循环允许退出:调用消息队列MessageQueue的quit();

Message next() {
        ...

    // 该参数用于确定消息队列中是否还有消息
    // 从而决定消息队列应处于出队消息状态 or 等待状态
    int nextPollTimeoutMillis = 0;

    for (;;) {
        if (nextPollTimeoutMillis != 0) {
        Binder.flushPendingCommands();
    }

    // nativePollOnce方法在native层,若是    nextPollTimeoutMillis为-1,此时消息队列处于等待状态 
    nativePollOnce(ptr, nextPollTimeoutMillis);

    synchronized (this) {
     
        final long now = SystemClock.uptimeMillis();
        Message prevMsg = null;
        Message msg = mMessages;

        // 出队消息,即 从消息队列中取出消息:按创建Message对象的时间顺序
        if (msg != null) {
            if (now < msg.when) {
                nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
            } else {
                // 取出了消息
                mBlocked = false;
                if (prevMsg != null) {
                    prevMsg.next = msg.next;
                } else {
                    mMessages = msg.next;
                }
                msg.next = null;
                if (DEBUG) Log.v(TAG, "Returning message: " + msg);
                msg.markInUse();
                return msg;
            }
        } else {

            // 若 消息队列中已无消息,则将nextPollTimeoutMillis参数设为-1
            // 下次循环时,消息队列则处于等待状态
            nextPollTimeoutMillis = -1;
        }
    }
    
    ...
}
public void dispatchMessage(Message msg) {

    // 1. 若msg.callback属性不为空,则代表使用了post(Runnable r)发送消息
    // 则执行handleCallback(msg),即回调Runnable对象里复写的run()
    if (msg.callback != null) {
        handleCallback(msg);
    } else {
        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }

        // 2. 若msg.callback属性为空,则代表使用了sendMessage(Message msg)发送消息(即此处需讨论的)
        // 则执行handleMessage(msg),即回调复写的handleMessage(msg)
        handleMessage(msg);

    }
}

分发给对应的Handler的过程:根据出队消息的归属者通过dispatchMessage(msg)进行分发,最终回调复写的handleMessage(Message msg),从而实现消息处理的操作;

注意:
1、msg.callback不为空,则代表使用了post(Runnable r)发送消息,则直接回调Runnable对象里复写的run();
2、msg.callback为空,则代表使用了sendMessage(Message msg)发送消息,则回调复写的handleMessage(msg);

2、创建消息对象
public static Message obtain() {

    // Message内部维护了1个Message池,用于Message消息对象的复用
    // 使用obtain()则是直接从池内获取
    synchronized (sPoolSync) {
        if (sPool != null) {
            Message m = sPool;
            sPool = m.next;
            m.next = null;
            m.flags = 0; // clear in-use flag
            sPoolSize--;
            return m;
        }
        // 建议:使用obtain()”创建“消息对象,避免每次都使用new重新分配内存
    }
    // 若池内无消息对象可复用,则还是用关键字new创建
    return new Message();
}
3、发送消息
public final boolean sendMessage(Message msg) {
    return sendMessageDelayed(msg, 0);
}

/** 
* 分析1:sendMessageDelayed(msg, 0)
**/
public final boolean sendMessageDelayed(Message msg, long delayMillis) {
    if (delayMillis < 0) {
        delayMillis = 0;
    }

    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
}

/** 
* 分析2:sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis)
**/
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
    // 1. 获取对应的消息队列对象(MessageQueue)
    MessageQueue queue = mQueue;

    // 2. 调用了enqueueMessage方法
    return enqueueMessage(queue, msg, uptimeMillis);
}

/** 
* 分析3:enqueueMessage(queue, msg, uptimeMillis)
**/
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    // 1. 将msg.target赋值为this
    // 即:把当前的Handler实例对象作为msg的target属性
     msg.target = this;
    // Looper的loop()中消息循环时,会从消息队列中取出每个消息msg,然后执行msg.target.dispatchMessage(msg)去处理消息
    // 实际上则是将该消息派发给对应的Handler实例        

    // 2. 调用消息队列的enqueueMessage()
    // 即:Handler发送的消息,最终是保存到消息队列
    return queue.enqueueMessage(msg, uptimeMillis);
}

/** 
* 分析4:queue.enqueueMessage(msg, uptimeMillis)
* 定义:属于消息队列类(MessageQueue)的方法
* 作用:入队,即将消息 根据时间 放入到消息队列中(Message ->> MessageQueue)
* 采用单链表实现:提高插入消息、删除消息的效率
*/
boolean enqueueMessage(Message msg, long when) {
    ...

    synchronized (this) {
        msg.markInUse();
        msg.when = when;
        Message p = mMessages;
        boolean needWake;

        // 判断消息队列里有无消息
        // a. 若无,则将当前插入的消息 作为队头 & 若此时消息队列处于等待状态,则唤醒
        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;

            // b. 判断消息队列里有消息,则根据 消息(Message)创建的时间 插入到队列中
            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;
}

// 之后,随着Looper对象的无限消息循环
// 不断从消息队列中取出Handler发送的消息 & 分发到对应Handler
// 最终回调Handler.handleMessage()处理消息

Handler发送消息的本质 = 为该消息定义target属性(即本身实例对象) & 将消息入队到绑定线程的消息队列中;

方式二:使用 Handler.post()
1、发送消息到消息队列中
public final boolean post(Runnable r) {
    return  sendMessageDelayed(getPostMessage(r), 0);
    // getPostMessage(r) 的源码分析->>分析1
    // sendMessageDelayed()的源码分析 ->>分析2
}

/** 
* 分析1:getPostMessage(r)
* 作用:将传入的Runable对象封装成1个消息对象
**/
private static Message getPostMessage(Runnable r) {
    // 1. 创建1个消息对象(Message)
    Message m = Message.obtain();
    // 注:创建Message对象可用关键字new 或 Message.obtain()
    // 建议:使用Message.obtain()创建,
    // 原因:因为Message内部维护了1个Message池,用于Message的复用,使用obtain()直接从池内获取,从而避免使用new重新分配内存

    // 2. 将 Runable对象 赋值给消息对象(message)的callback属性
    m.callback = r;
                        
    // 3. 返回该消息对象
    return m;
} 

/** 
* 分析2:sendMessageDelayed(msg, 0)
* 作用:实际上,从此处开始,则类似方式1 = 将消息入队到消息队列,
* 即 最终是调用MessageQueue.enqueueMessage()
**/
public final boolean sendMessageDelayed(Message msg, long delayMillis) {
    if (delayMillis < 0) {
        delayMillis = 0;
    }

    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
// 请看分析3
}

/** 
* 分析3:sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis)
**/
public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
    // 1. 获取对应的消息队列对象(MessageQueue)
    MessageQueue queue = mQueue;

    // 2. 调用了enqueueMessage方法 ->>分析3
    return enqueueMessage(queue, msg, uptimeMillis);
}

/** 
* 分析4:enqueueMessage(queue, msg, uptimeMillis)
**/
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
    // 1. 将msg.target赋值为this
    // 即 :把 当前的Handler实例对象作为msg的target属性
    msg.target = this;
    // 请回忆起上面说的Looper的loop()中消息循环时,会从消息队列中取出每个消息msg,然后执行msg.target.dispatchMessage(msg)去处理消息
    // 实际上则是将该消息派发给对应的Handler实例        

    // 2. 调用消息队列的enqueueMessage()
    // 即:Handler发送的消息,最终是保存到消息队列
    return queue.enqueueMessage(msg, uptimeMillis);
}

    // 注:实际上从分析2开始,源码 与 sendMessage(Message msg)发送方式相同

注意:

1、相比sendMessage(),post()最大的不同在于,更新的UI操作可直接在重写的run()中定义;
2、Runnable并无创建新线程,而是发送 消息 到消息队列中

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值