Handler

  1. 作用
  2. 运行流程
  3. 组成
  4. 同步屏障
  5. 内存泄漏
  6. Handler的生产者消费者模式
  7. 子线程创建的Looper消息队列无消息的时候的处理方案是什么

1.作用
Handler是用于线程间通信的,但是它产生的根本并不只是用于UI处理,而更多的是handler是整个app通信的框架

可以在ActivityThread里面感受到,整个App都是用它来进行线程间的协调

2.运行流程
在这里插入图片描述

handler.sendMessage()			【handler发送消息】			->	
messasgeQueue.enqueueMessage()	【消息队列队列的插入节点】	->
looper.loop()					【从消息队列中取消息】		->
messasgeQueue.next() 			【从消息队列中取消息】		->
handler.dispatchMessage()		【handler分发消息】			->
handler.handleMessage()		【handler处理消息】			->

引用关系
在这里插入图片描述
在这里插入图片描述

3.组成
(1)Looper
1.构造方法,每一个线程都有自己独有的Looper,独有的消息队列

    private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }
在Looper的构造方法中new了一个MessageQueue实现了唯一
并存入了创建Looper的线程

2.每一个Looper都需要执行prepare()方法

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

每一个线程都持有一个static final ThreadLocal sThreadLocal = new ThreadLocal()

在prepare()方法中实现了一个线程只有唯一的Looper	->
key为ThreadLocal,value为Looper
sThreadLocal.get() != null,说明当前线程已经有唯一的Looper
sThreadLocal.get() == null,就为当前线程创建一个Looper

3.主线程和子线程创建Looper
主线程在ActivityThread的main()函数中调用了Looper.prepareMainLooper()和Looper.loop()方法
而子线程如果需要创建自己的Looper需要手动调用Looper.prepare()和Loop.loop()方法
在这里插入图片描述

4.loop()方法
用一个for死循环不断从MessageQueue中取消息

for (;;) {
	Message msg = queue.next();
	...
}

在for循环里调用handler.dispatchMessage()方法分发消息

msg.target.dispatchMessage(msg);

(2)Handler
1.构造方法
让Handler持有Looper和Looper里的MessageQueue

    public Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }

2.sendMessageAtTime()发送消息
在这里插入图片描述

所有的发送消息最终都会调用sendMessageAtTime()方法
在sendMessageAtTime()中再调用Handler.enqueueMessage()方法
    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);
    }

3.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);
    }
在Handler.enqueueMessage()方法中
会让要发送的Message持有当前Handler的引用	->	msg.target = this

(3)MessageQueue

1.public boolean enqueueMessage()

在这里插入图片描述

简单的插入操作
优先级队列	->	根据Message的when来判断插入的位置

2.Message next()

在这里插入图片描述

从MessageQueue队列中取Message

(4)Message
1.public static Message obtain()

可以直接new Message 但是有更好的方式 Message.obtain
因为可以检查是否有可以复用的Message,用过复用避免过多的创建、
销毁Message对象达到优化内存和性能的目地

在这里插入图片描述

也可以调用Handler.obtainMessage()方法

在这里插入图片描述

在这里插入图片描述

2.recycle()
当Message被处理掉时,应该调用recycle()方法回收Message,以便下次使用

在这里插入图片描述
在这里插入图片描述

4.同步屏障
1作用
同步屏障就是阻碍同步消息,只让异步消息通过

线程的消息都是放到同一个MessageQueue里面,取消息的时候是互斥取消息
而且只能从头部取消息,而添加消息是按照消息的执行的先后顺序进行的排序

解决,同一个时间范围内的消息,如果它是需要立刻执行的,开启绿色通道

2.开启同步屏障
MessageQueue.private int postSyncBarrier(long when)方法
在这里插入图片描述

postSyncBarrier()方法往MessageQueue中插入了一个target=null
即没有持有Handler的Message插入到合适的位置

3.从消息队列拿消息时Message,next()
在这里插入图片描述

拿到MessageQueue的头结点,如果他的target=null,说明它就是屏障
于是do-while循环拿到第一个异步消息进行处理

4.移除屏障 removeSyncBarrier(int token)
在这里插入图片描述
5.实例ViewRootImpl
在这里插入图片描述
在postCallback中用Handler发送了一个异步消息
在这里插入图片描述

在这里插入图片描述
5.内存泄漏
(1)原因
在这里插入图片描述

如果,循环体中有消息未处理(Message 排队中、Message延迟处理)
那么 Handler 会一直存在
根据可达性分析无法回收Message
自然Activity.onDestory()也无法执行

(2)解决方法
参考

static + 弱引用

在这里插入图片描述
在这里插入图片描述

在工作线程中执行

在这里插入图片描述
6.Handler的生产者消费者模式

MessageQueue是一个优先级队列
但是Handler中并没有对MessageQueue满的阻塞操作
所以可以无限制的往MessageQueue里放入Message	->	但是会导致OOM
是因为系统间也要通过主线程的Handler发消息
如果限制Message有满的话会导致系统消息不能存入,卡死
但是对于MessageQueue取消息时有阻塞状态
(1)取出来的Message没有到执行时间	->	有限唤醒,自动唤醒
(2)取不出消息						->	无限等待,等待唤醒

在这里插入图片描述
(1)取出来的Message没有到执行时间 -> 有限唤醒,自动唤醒

Looper.loop()取消息	
调用MessageQueue.next()方法时
会计算需要等待的时间,到时间自动唤醒

在这里插入图片描述

(2)取不出消息 -> 无限等待,等待唤醒

会进入无限等待

在这里插入图片描述

直到handler.enqueueMessage()插入新消息时
调用MessageQueue.enqueueMessage()时唤醒

在这里插入图片描述

让MessaheQueue.next()方法继续执行

在这里插入图片描述

对于主线程来说:

如果消息队列没消息
那就直到handler.enqueueMessage()插入新消息时
调用MessageQueue.enqueueMessage()时唤醒

对于子线程来说

如果消息队列没消息
调用Looper.que()方法

在这里插入图片描述

再调用MessageQueue.quit()方法
唤醒MessageQueue.next()方法
并给mQuitting = true

在这里插入图片描述

在MessageQueue.next()方法中
返回一个空

在这里插入图片描述

接着在Looper.loop()中取到一个空Message
结束loop循环

在这里插入图片描述

7.子线程创建的Looper消息队列无消息的时候的处理方案是什么

Looper里有quit()和quitSafely()方法

在这里插入图片描述

最终调用到MessageQueue.quit()方法
quit()清空MessageQueue所有消息
quitSafely()清空所有延迟消息

在这里插入图片描述

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值