Android源码学习-----HandlerThread

HandlerThread

1.run()方法

HandlerThread 从继承关系上看, 它继承Thread类, 由此可以得知这个类其实是一个线程类,既然是一个线程类, 那么肯定是要重写Thread中的run()方法, 所以可以浏览下run()方法

从红色箭头的三个方法中, 看到有三个方法, Looper.prepare(), Looper.myLooper(), Looper.loop(),  这三个方法其实是, 在子线程创建Handler时所要调用的三个方法, 在HandlerThreadle类中, 和其它类的区别在于创建了一个Looper, 让它能够在该线程中创建Handler对象

1.1 run()方法中的Synchronized和notifyAll

通常情况下, 只要出现了synchronized关键字, 往往意味着此处会造成线程安全问题, 并且这里调用了Thread中的notifyAll()方法来唤醒其它线程, 此处应该能够推断出, 既然调用了notifyAll方法, 那么很有可能其它地方使用了wait()方法, 具体调用位置请看 getLooper方法

2.getLooper()方法

该方法中有一行注释, 大概意思是, 如果当这个线程已经被创建了, 但是looper还没有创建完成, 此时需要让线程进入wait()状态,  等待looper对象创建完成, 当looper对象在run()方法中通过Looper.myLooper()创建完成后, 会紧接着调用notifyAll()方法来唤醒wait()状态下的线程

3.    quit()方法和quitSafely()方法

3.1 用途

这两个方法的用途其实都是终止looper的, 因为每次创建一个HandlerThread对象, 调用run()方法, 就会创建一个Looper对象, 而Looper是一个死循环, 这样会消耗大量的资源, 所以正确的操作方式为, 当界面销毁的时候, 调用quit()方法或者quitSafely()方法, 将Looper停止掉

3.2 源码

两个方法看起来很类似, 区别在于一个调用了looper对象中的quit()方法, 另一个调用了quitSafely()方法, 跟进源码

图中的mQueue其实是一个MessageQueue对象, 这两个方法最终调用的就是MessageQueue中的quit方法,只不过传入的参数分别是true 和 false

查看MessageQueue中的quit方法

传入的boolean值最终在这里判断, 如果为true ,也就是调用的是quitSafely()方法, 那么执行的是removeAllFutureMessagesLocked()方法, 如果为false, 那么调用的就是quit()方法, 最终执行的就是removeAllMessagesLocked()方法.

继续跟进removeAllFutureMessageLocked()方法, 里面也会调用方法 removeAllMessageLocked(),

这两个方法的有一个共同点就是, 当方法被调用时, Looper不会再接收新的Message消息,

区别在于不带future的方法, 会将消息队列中的所有类型的消息清空, 无论是延迟发送的还是立即执行的, 而带future的方法, 只是清除掉了延迟消息, 对于非延迟消息, 则会将其交给handler去执行.

图片上面是源码的处理,

但是, 为什么n.when > now 就表示后就可以跳出循环,意味着没有立即执行的消息了?

这是因为MessageQueue消息队列处理方式的关系, 简单的可以理解为, MessageQueue消息队列是按照消息的处理时间的从早到晚排序的

4.    MessageQueue插入消息的方式简介

Handler将一个消息发送出去后, 这个消息会被插入到消息队列, 但并不是无脑的将这个消息插入到最末尾, 先看下Message中的部分成员属性

When:是这个消息的执行时间, 也就是判断这个消息是否是一个延迟消息, 以及延迟多久

Data: 是这个消息中携带的数据, 并且可以看出Message是利用Bundle来处理消息的

Next: 是一个消息对象, 上面有一行注释, 大概意识是有的时候需要用next来存储一些链关系, 这其实是类似链表的结构, 链表结构中有头指针 头节点等概念, 这里面假设mMessage永远指向队列中的第一个Message

当一个新的消息发送过来时, 分情况分析,

第一种情况, 满足放在首位的判断条件, 那么就让mMessages来指向这个新插入的消息即可, 那么放在首位的条件是什么? 请看下面的源码

熟悉handler源码的应该知道, Handler发送消息, 无论调用哪个方法, 最终都是由sendMessageAtTime()这个方法来执行, 而Handler的sendMessageAtTime()方法中调用了这个enqueueMessage()方法, 在enqueueMessage()这个方法中,  首条消息对象mMessage赋值给了 一个Message对象 p, 然后对这个p进行了一些列判断,

P == null: 这个判断表示消息队列中mMessage对象是否为null, 如果为null, 那么证明此时整个消息队列都是空的, 所以满足这个条件, 新的消息可以放到第一个位置

When == 0: when就是 handler发送消息时传入过来的消息触发时间, 如果为0, 表示这个消息是个非延迟消息, 需要立刻执行, 此时新的消息也要放到第一条,

When < p.when: 也就是handler新发送的这个消息的触发时间, 小于第一条消息的触发时间, 那么也要放到第一个位置

只要满足上面三种情况, 那么就让mMessage指向新插入的消息即可,

源码

第二种情况

也就是新发送来的消息msg的触发时间, 要晚于mMessage的时间, 那么就会将msg的when和消息队列后面的每个时间when做下对比, 直到符合从早到晚的顺序为止

可以看出源码中有一个死循环, 会一直判断消息队列的后面是否还有消息, 以及触发时间, 当满足了任意一个条件后, 就会停止循环判断, 然后将新消息msg插入到队列中

源码中可以看出声明了一个新的消息对象 prev, 这个对象永远指向的是msg的前一个消息

下面是过程图, 图中0x1010等假设为消息的地址, 仅供理解参考用, 不为真实地址, 所以无论何时, prev永远记录的是p的前一个地址

当新的消息msg找到何时的位置时, 也就是prev.when <  msg.when  < p.when时,  就会将msg插入到prev之后,  p之前这个位置

到此为止, 新的消息msg就插入到了MessageQueue队列中了

如有错误, 敬请指正, 感激不尽!

原文地址:https://www.cnblogs.com/sweep/p/8609984.html

时间: 2024-10-12 04:20:45

Android源码学习-----HandlerThread的相关文章

Android源码学习之装饰模式应用

主要内容: 装饰模式定义 装饰模式优势 装饰模式在Android源码中的应用 一.装饰模式定义 装饰模式定义: Attach additional responsibilities to an object dynamically keeping the same interface. Decoators provide a flexible alternative to subclassing for extending functionality. 动态地给一个对象添加一些额外的职责.就增加

Android源码学习(一) 数据集观察者

查看Android源码发现这个,决定记下下来. 1.在android.database这个包下面,存在这样一个抽象类DataSetObserver,里面包括onChanged()和onInvalidated()这个两个方法,用来监听数据的改变,在方法内要执行的操作. /* * Copyright (C) 2007 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "Lic

Android源码学习之ListView的复用回收机制剖析.

本博客参考了地址:点击打开链接 在刚开始接触学习Android基础的时候,ListView算是一个比较神奇的控件了,因为那时候好多效果都可以用它实现,而且用它就得用到一个设计模式,[适配器].结果昨天遗留下来一个bug,带这个解决这个Bug去翻看了5.0.1 API22的ListView部分源码分析复用. 复用到底有什么用.?简单的举个例子,假如你想要展示一万条item,作为手机不可能一下将一万条同时加载进去,这样肯定会OOM的,所以Google开发者想到了复用,也算是ListView高级的一个

【转】Android源码学习(2)使用Git和Repo进行版本管理

原文网址:http://blog.chinaunix.net/uid-26074270-id-2458828.html Android项目采用Git和Repo进行版本管理.在大多数情况下,Git都可以满足用户的需求.然而,由于Android项目过于庞大,想要简单.高效的管理这一百多个Git库,并不是一件容易的事情.Repo正是基于此需求,对Git命令部分封装,用来简化一些跨网络的操作. 安装Repo 创建repo所在的目录,并将目录加到PATH环境变量中 $ mkdir ~/bin $ PATH

Android源码学习-----Handler机制

Handler 1.为什么要使用Handler 在Android4.0之后,google公司为从系统使用及用户体验方面考虑,如果做一些比较耗时的操作,就不允许直接在主线程中进行,而是要通过handler发送Message对象的方法来修改主线程的UI界面 2.Handler原理简介 在所有的UI操作界面中,都在执行一个死循环(Looper)在不断接收和监听用户发出的指令,一但接受到指令,就立即执行.   当子线程需要修改UI界面时,调用Handler的sendMessage()方法,向主线程发送消

Android源码学习初步

目前,互联网行业正在朝着移动互联网方向强劲地发展,而移动互联网的发展离不开背后的移动平台的支撑.众所周知,如今在移动平台市场上,苹果的iOS.谷歌的Android和微软的Windows Phone系统已经形成了三足鼎立的形势,而Android系统的市场占有率是最高的.Android系统之所以能够在市场上占据着第一的位置,一来是因为它依托着谷歌的品德效应和技术实力,二来是因为它是开放的,任何人都可以得到它的源代码,并且能够自由地使用它.既然Android系统是开放的,作为一个移动平台开发人员来说,

[Android阅读代码]android-async-http源码学习一

android-async-http 下载地址 一个比较常用的Http请求库,基于org.apache.http对http操作进行封装. 特点: 1.每一个HTTP请求发生在UI线程之外,Client通过回调处理HTTP请求的结果,使得Client代码逻辑清晰 2.每一个请求使用线程池管理执行 3.支持gzip , cookie等功能 4.支持自动重试连接功能 [Android阅读代码]android-async-http源码学习一,布布扣,bubuko.com

Android学习进阶路线Android源码分享)

毕业8月有余,同时伴随着从事的Android经验的提升.仔细思量过去的工作内容,掐指算来也是少得可怜---主要维护FM收 音机模块,间或看看Lancher模块的代码.尽管这样,总的来说,在这八个月事件的学习里,我自我感觉收获还是很大的---自学了很多 Android相关方面的知识.虽然里真正的大牛还有很远的一段路程要走,自己也会坚持学习.坚持走下去. 再次,把自己学习过程中总结的一些经验总结出来,帮助那些刚刚步入Android的网友以及有了取得了一些经验但不知如何 继续前行的朋友,希望你们能够早

Android事件分发详解(三)——ViewGroup的dispatchTouchEvent()源码学习

package cc.aa; import android.os.Environment; import android.view.MotionEvent; import android.view.View; public class UnderstandDispatchTouchEvent { /** * dispatchTouchEvent()源码学习及其注释 * 常说事件传递中的流程是:dispatchTouchEvent->onInterceptTouchEvent->onTouchE