android关于Android知识的总结Android应用开发那些事

30分钟带你搞懂Handler

2019-05-30  本文已影响69人  LiChengZe_Blog

Handler是什么?

对于Handler 我们都不太陌生 这是我们Android的消息传输机制 我们都知道它是用来帮我们通过子线程发送消息到主线程 供我们主线程进行更新UI操作的 那他为什么可以完成线程间的通信呢?

Handler 包含了什么?

Handler更新UI界面的机制,也是消息处理的机制,可以发送消息,也可以处理消息
Message 接收处理消息对象
Looper 每个线程只有1个:可读取MessageQueue消息队列,管理此线程里的MessageQueue(消息队列)遵循先进先出读到消息后从消息队列取出交给handler
Message Queue(消息队列):用来存放线程放入的消息

Handler消息机制是什么?

Handler 机制中包含了几个关键的对象,Looper,MessageQueue,Message。在使用 Handler 的时候,在 handler 所创建的线程需要维护一个唯一的 Looper 对象,每个线程对应一个 Looper, 每个线程的 Looper 通过 ThreadLocal 来保证。Looper 对象的内部又维护有唯一的一个MessageQueue 链表,所以一个线程可以有多个 Handler,但是只能有一个 Looper 和一个MessageQueue。Message 在 MessageQueue 不是通过一个列表来存储的,而是将传入的Message 存入到了上一个 Message 的 next 中,在取出的时候通过顶部的 Message 按放入的顺序依次取出 Message。Looper 对象通过 loop()方法开启了一个死循环,不断地从 looper 内的 MessageQueue 中取出 Message,然后通过 handler 将消息分发传回 handler 所在的线程

如果对Handler有了解的朋友会觉得却是就是挺简单的 但是对于初学者来说 这TM是啥都是些什么玩意 哈哈哈 不要着急 我们今天通过源码来给大家讲解一下 关于Handler机制的执行流程

我们先来通过一张流程图来看一下我们是怎么把消息从子线程发送到主线程的:


image.png

Handler源码

首先我们都知道Activity创建以后会首先走到我们ActivityTrhead 的main 函数中(也就是我们的main方法)

ActivityThread.png

这里我们发现 咦 怎么我们main方法中还会帮我们创建Looper的? 当你对java够熟悉的时候 大家就可以进入源码中了解一下 我们就会发现 其实我们对代码的理解过于偏浅 咳咳 跑题了 我们首先去我们的第一步Looper.prepareMainLooper()这个方法中看一看:


image.png

我们发现其实就是调用了Looper中的一个方法 进入方法第一步就执行了一个 本类中的prepare()方法 这个方法是做什么的呢? 我们点进去继续看:


image.png

这里我们发现它给我们做了一个判断 sTreadLocal.get() 获取到的这个对象是否不为空 不为空就给我们抛出异常 否则就帮我们set()一个。 我们这个时候就想问了 这个sTreadLocal是个什么东西?为什么要通过他来获取Looper呢?

image.png

我们发现这个sThreadLocal 就是线程内部的数据存储类的对象 ,通过他可以在指定的线程中存储数据,该数据只有在指定线程中可以获取
我们看一下这个ThreadLocal给我们提供的set() get()方法:

set()方法:


image.png

这里我们发现一个ThreadLocalMap 一个map集合 我们发现这一步吧我们的当前线程和我们当前线程的Looper对象 通过 key value 的形式 绑定到了一个ThreadLocalMap 对象中
我们看一下Looper的构造方法中都创建了什么:

image.png
我们发现它帮我们创建了一个MessageQueue();并把我们的的当前线程对象赋值给了mThread 这里特别重要 我们的Looper只能有一个MessageQueue 而且是在创建Looper时创建了 也就是同步创建的。

有了赋值set() 我们再来看看取值get()方法

get()方法 :


image.png
image.png
image.png

get()方法中代码很多 但是原理很简单 就是有就使用 没有就赋值嘛
我们只需要知道我们通过这个get()方法返回的是我们当前线程的Looper对象就可以啦

弄懂这两个方法以后我们就知道了我们prepare这个方法中都了什么 :判断我们Looper是否存在 不存在的话就帮我们创建 存在就从ThreadLocalMap中取出当前线程对应的Looper对象

我们继续往下看:
prepareMainLooper这个方法:


image.png

我们这里的sMainLooper就是一个默认为空的Loopr对象


image.png

我们看到如果他存在的话 就抛出异常 不存在的话就调用本类的myLooper方法
这个myLooper都干了什么呢?我们点进去看一下 :


image.png

这个地方就通过我们ThreadLocal的get()方法取出我们当前线程的Looper对象

到这里我们的 Looper.prepareMainLooper(); 创建主线程Looper对象就完成了

我们接下来往下看


image.png

我们发现我们这里又调用了Looper.loop() 这个方法, 这个方法就是我们轮询消息的方法


image.png

又是一段长代码 是不是看的让人头大 不要慌 看源码就是很繁琐的 我们要有耐心
我们首先看到 调用了myLooper()这个方法 这个方法是做什么呢?

image.png
我们发现它和我们ThreadLocal.get()这个方法功能是一样的 都是获取到我们保存的Looper对象
我们接下来看它又做了哪些事呢?
image.png
这里把我们Looper对象的MessageQueue取出来
image.png
通过死循环 不断的调用MessageQueue 消息队列 对象的next()方法 获取到Message 直到我们的消息队列中没有消息 返回 这个next()方法里面是怎么把消息给我们取出呢?
这个方法中代码太多 我就把核心给取出展示:
image.png
image.png
这里就是定义一个空的Message 然后把我们的消息队列中的消息 进行取出 把每一个值都赋值给我们空的Message 然后把我们赋值过后的Message返回

如果我们的消息队列中有消息的话 就会一直调用这个next方法进行取值 直到消息队列中的消息为空 next就会堵塞线程 否则loop循环就会无限循环下去 当我们获取到消息以后我们用做了什么事情呢?

image.png

我们发现我们调用了一个msg.target.dispatchMessage()方法 首先我们要知道msg.target是什么


image.png

原来他是一个Handler 的对象 我们接下来再看dispatchMessage这个是干了什么:


image.png

我*** 有没有感觉到最下面那一行代码特别熟悉:
这就是我们创建Handler的时候


image.png

这个handleMessage方法吗 原来就是这个地方把我们的msg传递给我们的Handler了啊 是不是觉得其实也不是那么的困难 看完了以上的代码 我们接下来再来看我们的Handler 哈哈哈哈哈 这里才到我们的Handler 是不是觉得已经很无聊了 马上步入正题:

我们先看一下我们创建Handler的时候都帮我们干了什么:


image.png

继续往里看:


image.png
我们发现它调用了myLooper()这个方法 这个发法我们之前已经看到过了:
是用来获取我们当前线程的Looper对象的 接下来又把我们当前线程Looper对象中的MessageQueue绑定到我们的Handler中的MessageQueue 。接下来就来看我们的发送消息事件吧 image.png

我们今天通过一个简单的发送消息来进行讲解:
我们点进去sendMessage方法发现:


image.png

这里发送了一条默认为0的延迟消息 这个方法又做了什么呢:

image.png

又指定时间发送了一条消息-->

image.png

这个方法定义了一个空的MessageQueue绑定我们Handler的MessageQueue 又往下走enqueueMessage()方法:

image.png

我们发现这里把我们存储到msg.target的这个Handler赋值给了我们当前的Handler 接下来调用了MessageQueue的enqueueMessage() :


image.png

首先对我们的 msg.target 也就是 Hander进行了一次判空 如果为空就给我们抛出异常

         Message p = mMessages;
            boolean needWake;
            if (p == null || when == 0 || when < p.when) {
                // New head, wake up the event queue if blocked.
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
                // Inserted within the middle of the queue.  Usually we don't have to wake
                // up the event queue unless there is a barrier at the head of the queue
                // and the message is the earliest asynchronous message in the queue.
                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;
            }

首先判断我们消息队列中是否有消息 如果没有就直接放到第一个 如果有消息的话就放到他的后面

这个时候我们的消息就完全的存放到了我们MessageQueue中 我们的的ActivityThread 的main 方法中的Looper.loop 就会把我们消息队列中的消息轮询取出 给我们handler使用了

还有一点就是我们Handler的内存泄漏问题:

在使用 Handler 的过程中,需要注意内存泄漏,因为 handler 是用来进行线程间通信的,所
以新开启的线程会持有 Handler 引用的,如果 Activity 中创建 Handler,并且是非静态内部类
的形式,就有可能造成内存泄漏。非静态的内部类是会隐式持有外部类的引用,所以当其他
线程持有了 handler,线程没有被销毁,就意味着 Activity 会一致被 Handler 持有引用而无法导
致回收。同时 MessageQueue 中如果存在未处理完的 Message,Message 的 target 也是对 Acivity
的持有引用,也会造成内存泄漏。
解决的方法,可以使用静态的内部类+弱引用的方式。在外部类对象被销毁时,将
MessageQueue 中 的 消 息 清 空 。 如 Activity 的 onDestroy 时 将 消 息 情 况 , 调 用
handler.removeCallbacksAndMessages(null); 清空消息

以上就是我总结的Handler啦 有没有学到什么呢~
如果您发现了哪些错误可以联系我哦~

上一篇下一篇

猜你喜欢

热点阅读