android知识点_Handler如何进行线程切换的?

一、背景

Handler相关的知识在面试的时候经常出现,而且问法千奇百怪。Handler相关的知识有经常的给忘记,为此歇息写下这篇文章。主要解决以下问题:

  1. Handler知识点梳理回顾
  2. Handler如何进行线程切换?
二、Handler简单回顾

Handler主要作用就是将一个任务切换到指定的线程中执行。

Handler机制-MessageQueue:

MessageQueue即"消息队列",它内部存储了一组数据,以队列的形式对外提供插入和
删除的操作。
虽然称之为"队列",实际上它的数据机构却采用了单链表的结构来存储消息列表。

MessageQueue主要包含两个操作:

  • 插入(enqueueMessage):

  • 读取(next):
    从消息队列中取出一条数据并将该数据从消息队列中删除。

MessageQueue:同一线程在同一时间只能处理一个消息,同一线程代码执行是不具有并发性,所以需要队列
来保存消息和安排每个消息的处理顺序。

Handler机制-Looper

Looper可以理解为消息循环,因为MessageQueue只是一个存储消息队列,不能去处理消息,所以需要Looper无限循环的去查看是否有新的消息,如果有的话就处理消息,否则就一直等待(阻塞)。**每一个异步线程,都维护着唯一的一个Looper,每一个Looper会初始化(维护)一个MessageQueue,**之后进入一个无限循环一直在读取MessageQueue中存储的消息,如果没有消息那就一直阻塞等待。

  • Prepare();
    • 检查是否实例化了ThreadLocal,确保每个线程的Looper只有一个。
    • 如果已经实例化ThreadLocal,则将所在线程中Looper对象存储进去。
    • 将线程-looper-MessageQueue绑定。
 private static void prepare(boolean quitAllowed) {
        ...
        sThreadLocal.set(new Looper(quitAllowed));
    }
 private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }

线程通过ThreadLocal保存Looper对象,而Looper在初始化的时候创建了MessageQueue

  • Loop()
    • 获取Looper对应的MessageQueue,并从中获取Message,交给消息的target(Handler)属性的dispatchMessage去处理。
    public static void loop() {
        // 获取当前线程对应的Looper
        final Looper me = myLooper();
        // 获取Looper对应的MessageQueue
        final MessageQueue queue = me.mQueue;
        for (;;) {
            // 通过Message获得Message
            Message msg = queue.next(); // might block
            try {
                // 通过Message的target(handler)分发消息
                msg.target.dispatchMessage(msg);
            } finally {
            }
            // 回收消息
            msg.recycleUnchecked();
        }
    }

Handler机制-Handler

通过Handler将当前线程对应的Looper、MessageQueue绑定

  public Handler(Callback callback, boolean async) {
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
    }
小结:

Handler是用于同一个进程的线程间通信。Looper让主线程无限循环地从自己的MessageQueue拿出消息处理,所以我们就可以知道处理消息肯定是在主线程中处理的,那么是怎样在其他的线程往主线程的队列里放入消息呢?

道理其实很简单,我们知道在同一进程中线程和线程之间资源是共享的,也就是对于任何变量在任何线程都是可以访问和修改的,只要考虑并发性做好同步就行了,那么只要拿到MessageQueue 的实例,就可以往主线程的MessageQueue放入消息,主线程在轮询的时候就可以在主线程处理这个消息。

那么是怎么拿到主线程 MessageQueue的实例呢?

当然是可以拿到的(在主线程下mLooper = Looper.myLooper();mQueue = mLooper.mQueue;),Google 为了统一添加消息和消息的回调处理,又专门构建了Handler类,你只要在主线程构建Handler类,那么这个Handler实例就获取主线程MessageQueue实例的引用(获取方式mLooper = Looper.myLooper();mQueue = mLooper.mQueue;),Handler 在sendMessage的时候就通过这个引用往消息队列里插入新消息。

Handler 的另外一个作用,就是能统一处理消息的回调。这样一个Handler发出消息又确保消息处理也是自己来做。具体做法就是在队列里面的Message持有Handler的引用(哪个handler 把它放到队列里,message就持有了这个handler的引用),然后等到主线程轮询到这个message的时候,就来回调我们经常重写的Handler的handleMessage(Message msg)方法。

三、总结

Handler 对象在哪个线程下构建(Handler的构造函数在哪个线程下调用),那么Handler就会持有这个线程的Looper引用和这个线程的消息队列的引用。因为持有这个线程的消息队列的引用,意味着这个Handler对象可以在任意其他线程给该线程的消息队列添加消息,也意味着Handler的handlerMessage 肯定也是在该线程执行的。

同一进程中线程和线程之间资源是共享的,也就是对于任何变量在任何线程都是可以访问和修改的

四、参考

android之handler切换线程终极篇

MessageQueue的插入(enqueueMessage)详细过程?
MessageQueue的读取(next)详细过程?

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值