Android源码——Handler机制(一)

Android源码——Handler机制(一)

    • Handler机制概述
      • 介绍
      • Handler机制模型
      • Handler机制架构
    • Handler机制源码解析
      • ActivityThread
      • Looper
      • Handler

Handler机制概述

介绍

Handler是Android消息机制的上层接口。Handler可以将一个任务切换到Handler所在的线程中去执行。通常情况下,Handler的使用场景就是更新UI。

如下就是使用消息机制的一个简单实例:

public class BasicActivity extends AppCompatActivity {private static final String TAG = "BasicActivity";private Handler mHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);Log.i(TAG, msg.what);}};@Overridepublic void onCreate(Bundle savedInstanceState, PersistableBundle persistentState) {super.onCreate(savedInstanceState, persistentState);setContentView(R.layout.activity_main);new Thread(new Runnable() {@Overridepublic void run() {// 耗时操作.....Message message = Message.obtain();message.what = 1;mHandler.sendMessage(message);}}).start();}
}

在子线程中,进行耗时操作,执行完操作后,发送消息,通知主线程更新UI。这便是消息机制的典型应用场景。我们通常只会接触到Handler和Message来完成消息机制,其实内部还有两大助手来共同完成消息传递。

Handler机制模型

消息机制主要包含:MessageQueueHandlerLooper这三大部分,以及Message

  • Message: 需要传递的消息,可以传递数据;
  • MessageQueue: 消息队列,但是它的内部实现并不是用的队列,实际上是通过一个单链表的数据结构来维护消息列表,因为单链表在插入和删除上比较有优势。主要功能向消息池投递消息MessageQueue.enqueueMessage和取走消息池的消息MessageQueue.next
  • Handler: 消息辅助类,主要功能向消息池发送各种消息事件Handler.sendMessage和处理相应消息事件Handler.handleMessage
  • Looper: 不断循环执行Looper.loop,从MessageQueue中读取消息,按分发机制将消息分发给目标处理者。

Handler机制架构

运行流程:在子线程执行完耗时操作,当Handler发送Message时,会调用MessageQueue.enqueueMessage,向消息队列中添加Message。当通过Looper.loop开启循环后,会不断从线程池中读取消息,即调用MessageQueue.next,然后调用目标Handler(即发送该消息的Handler)的dispatchMessage方法传递消息,然后返回到Handler所在线程,目标Handler收到消息,调用handleMessage方法,接收处理消息。
在这里插入图片描述

每个线程只能存在一个LooperLooper保存在ThreadLocal线程中。主线程(UI线程)已经创建了一个Looper,所以在主线程中不需要再创建Looper,但是在其他线程中需要创建Looper。每个线程可以有多个Handler,即一个Looper可以处理来自多个Handler的消息。Looper中维护一个MessageQueue,来维护消息队列,消息队列中的Message可以来自不同的Handler

在这里插入图片描述
Looper有一个MessageQueue消息队列,MessageQueue有一组待处理的MessageMessage中记录发送和处理消息的HandlerHandler中有LooperMessageQueue

Handler机制源码解析

ActivityThread

ActivityThread类是用来启动Android的,在main()函数中准备相关环境,创建并启动Looper循环。

主线程中不需要自己创建Looper,这是由于在程序启动的时候,系统已经帮我们自动调用了Looper.prepare()方法。

public static void main(String[] args) {Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");// Install selective syscall interceptionAndroidOs.install();// CloseGuard defaults to true and can be quite spammy.  We// disable it here, but selectively enable it later (via// StrictMode) on debug builds, but using DropBox, not logs.CloseGuard.setEnabled(false);Environment.initForCurrentUser();// Make sure TrustedCertificateStore looks in the right place for CA certificatesfinal File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());TrustedCertificateStore.setDefaultUserDirectory(configDir);// Call per-process mainline module initialization.initializeMainlineModules();Process.setArgV0("<pre-initialized>");// 1. 把主线程和消息队列放进Looper中,同时把Looper放进ThreadLocal(每个线程绑定一个Looper)Looper.prepareMainLooper();// ....// 启动Looper循环,不断循环取出消息Looper.loop();throw new RuntimeException("Main thread loop unexpectedly exited");
}

Looper

Looper不断循环执行loop(),从MessageQueue中读取消息,按分发机制将消息分发给目标处理者。

  1. 初始化Looper
public final class Looper {// sThreadLocal.get() will return null unless you've called prepare().@UnsupportedAppUsagestatic final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();@UnsupportedAppUsageprivate static Looper sMainLooper;  // guarded by Looper.class@UnsupportedAppUsagefinal MessageQueue mQueue;final Thread mThread;/** Initialize the current thread as a looper.* This gives you a chance to create handlers that then reference* this looper, before actually starting the loop. Be sure to call* {@link #loop()} after calling this method, and end it by calling* {@link #quit()}.*/public static void prepare() {prepare(true);}/*** 创建Looper,并保存在ThreadLocal,不能重复创建Looper,只能创建一个。 */private static void prepare(boolean quitAllowed) {// 不能重复创建否则报错if (sThreadLocal.get() != null) {throw new RuntimeException("Only one Looper may be created per thread");}// 4. 实例化自己,封装mQueue和主线程,把自己放入ThreadLocal中sThreadLocal.set(new Looper(quitAllowed));}/*** Initialize the current thread as a looper, marking it as an* application's main looper. See also: {@link #prepare()}** @deprecated The main looper for your application is created by the Android environment,*   so you should never need to call this function yourself.*/@Deprecatedpublic static void prepareMainLooper() {prepare(false);synchronized (Looper.class) {if (sMainLooper != null) {throw new IllegalStateException("The main Looper has already been prepared.");}sMainLooper = myLooper();}}/*** Return the Looper object associated with the current thread.  Returns* null if the calling thread is not associated with a Looper.*/public static @Nullable Looper myLooper() {return sThreadLocal.get();}private Looper(boolean quitAllowed) {// 2. 实例化MessageQueuemQueue = new MessageQueue(quitAllowed);mThread = Thread.currentThread();}}
  1. 开启Looper
    /*** 5. 调用Looper.loop()方法* Run the message queue in this thread. Be sure to call* {@link #quit()} to end the loop.*/public static void loop() {// 获取TLS存储的Looper对象final Looper me = myLooper();if (me == null) {throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");}if (me.mInLoop) {Slog.w(TAG, "Loop again would have the queued messages be executed"+ " before this one completed.");}me.mInLoop = true;// 获取Looper对象中的消息队列final MessageQueue queue = me.mQueue;// Make sure the identity of this thread is that of the local process,// and keep track of what that identity token actually is.Binder.clearCallingIdentity();final long ident = Binder.clearCallingIdentity();// 进入loop()主循环for (;;) {// 消息队列取出一条消息Message msg = queue.next(); // might blockif (msg == null) {// No message indicates that the message queue is quitting.return;}// 默认为null,可通过setMessageLogging()方法来指定输出,用于debug功能// This must be in a local variable, in case a UI event sets the loggerfinal Printer logging = me.mLogging;if (logging != null) {logging.println(">>>>> Dispatching to " + msg.target + " " +msg.callback + ": " + msg.what);}// .....try {// 获取msg的目标Handler,然后用于分发Messagemsg.target.dispatchMessage(msg);// ....} catch (Exception exception) {// ...} finally {// ...}if (logging != null) {logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);}// Make sure that during the course of dispatching the// identity of the thread wasn't corrupted.final long newIdent = Binder.clearCallingIdentity();// 回收可能正在使用的消息msg.recycleUnchecked();}}

loop()进入循环模式,不断重复下面的操作,直到消息为空时退出循环:读取MessageQueue的下一条Message;把Message分发给相应的target

next()取出下一条消息时,队列中已经没有消息时,next()会无限循环,产生阻塞。等待MessageQueue中加入消息,然后重新唤醒。

Handler

消息辅助类,主要功能向消息池发送各种消息事件(Handler.sendMessage)和处理相应消息事件(Handler.handleMessage)。

  1. 创建Handler
public class Handler {@UnsupportedAppUsagefinal Looper mLooper;// 这里并没有实例化,指向Lopper的消息队列final MessageQueue mQueue;@UnsupportedAppUsagefinal Callback mCallback;final boolean mAsynchronous;@UnsupportedAppUsageIMessenger mMessenger;/*** Callback interface you can use when instantiating a Handler to avoid* having to implement your own subclass of Handler.*/public interface Callback {/*** @param msg A {@link android.os.Message Message} object* @return True if no further handling is desired*/boolean handleMessage(@NonNull Message msg);}/*** Subclasses must implement this to receive messages.*/public void handleMessage(@NonNull Message msg) {}}

构造函数:

    @Deprecatedpublic Handler() {this(null, false);}@Deprecatedpublic Handler(@Nullable Callback callback) {this(callback, false);}@UnsupportedAppUsagepublic Handler(boolean async) {this(null, async);}/*** 第一种构造方法:给主线程调用,因为主线程已经调用Looper.prepareMainLooper()*/public Handler(@Nullable Callback callback, boolean async) {if (FIND_POTENTIAL_LEAKS) {final Class<? extends Handler> klass = getClass();if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) &&(klass.getModifiers() & Modifier.STATIC) == 0) {Log.w(TAG, "The following Handler class should be static or leaks might occur: " +klass.getCanonicalName());}}// 获取主线程的LoopermLooper = Looper.myLooper();if (mLooper == null) {throw new RuntimeException("Can't create handler inside thread " + Thread.currentThread()+ " that has not called Looper.prepare()");}// Handler的mQueue指向Looper的mQueuemQueue = mLooper.mQueue;mCallback = callback;mAsynchronous = async;}public Handler(@NonNull Looper looper) {this(looper, null, false);}public Handler(@NonNull Looper looper, @Nullable Callback callback) {this(looper, callback, false);}/*** 第二种构造方法,给在子线程中创建Handler时使用,子线程要自主调用Looper.prepare()*/@UnsupportedAppUsagepublic Handler(@NonNull Looper looper, @Nullable Callback callback, boolean async) {mLooper = looper;mQueue = looper.mQueue;mCallback = callback;mAsynchronous = async;}
  1. 发送消息

发送消息有几种方式,但是归根结底都是调用了sendMessageAtTime()方法。在子线程中通过Handlerpost()方式或send()方式发送消息,最终都是调用了sendMessageAtTime()方法。

post方法:

    public final boolean post(@NonNull Runnable r) {return  sendMessageDelayed(getPostMessage(r), 0);}public final boolean postAtTime(@NonNull Runnable r, long uptimeMillis) {return sendMessageAtTime(getPostMessage(r), uptimeMillis);}public final boolean postAtTime(@NonNull Runnable r, @Nullable Object token, long uptimeMillis) {return sendMessageAtTime(getPostMessage(r, token), uptimeMillis);}public final boolean postDelayed(@NonNull Runnable r, long delayMillis) {return sendMessageDelayed(getPostMessage(r), delayMillis);}public final boolean postDelayed(Runnable r, int what, long delayMillis) {return sendMessageDelayed(getPostMessage(r).setWhat(what), delayMillis);}public final boolean postDelayed(@NonNull Runnable r, @Nullable Object token, long delayMillis) {return sendMessageDelayed(getPostMessage(r, token), delayMillis);}

send方法:

    public final boolean sendMessage(@NonNull Message msg) {return sendMessageDelayed(msg, 0);}public final boolean sendEmptyMessage(int what){return sendEmptyMessageDelayed(what, 0);}public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {Message msg = Message.obtain();msg.what = what;return sendMessageDelayed(msg, delayMillis);}public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis) {Message msg = Message.obtain();msg.what = what;return sendMessageAtTime(msg, uptimeMillis);}public final boolean sendMessageDelayed(@NonNull Message msg, long delayMillis) {if (delayMillis < 0) {delayMillis = 0;}return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);}

就连子线程中调用Activity中的runOnUiThread()更新UI,其实也是发送消息通知主线程更新UI,最终也会调用sendMessageAtTime()方法。

/*** 如果当前的线程不等于UI线程(主线程),就去调用Handler的post()方法,最终会调用sendMessageAtTime()方法。* 否则就直接调用Runnable对象的run()方法。*/
public final void runOnUiThread(Runnable action) {if (Thread.currentThread() != mUiThread) {mHandler.post(action);} else {action.run();}
}

sendMessageAtTime()

    public boolean sendMessageAtTime(@NonNull Message msg, long uptimeMillis) {// 其中mQueue是消息队列,从Looper中获取的MessageQueue queue = mQueue;if (queue == null) {RuntimeException e = new RuntimeException(this + " sendMessageAtTime() called with no mQueue");Log.w("Looper", e.getMessage(), e);return false;}// 调用enqueueMessage方法return enqueueMessage(queue, msg, uptimeMillis);}private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,long uptimeMillis) {// 把自己(Handler)传入消息一并发送,因为Looper需要用Handler来执行dispatchMessage()msg.target = this;msg.workSourceUid = ThreadLocalWorkSource.getUid();if (mAsynchronous) {msg.setAsynchronous(true);}// 调用MessageQueue的enqueueMessage方法return queue.enqueueMessage(msg, uptimeMillis);}

可以看到sendMessageAtTime()方法的作用很简单,就是调用MessageQueueenqueueMessage()方法,往消息队列中添加一个消息。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/36980.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

网络物理隔离后 可以用保密U盘进行数据安全交换吗?

企业用的保密U盘通常被设计用于存储和传输敏感信息&#xff0c;以确保数据的安全和保密性。 在网络之间实现了物理隔离后&#xff0c;使用保密U盘进行数据安全交换是一种常见的做法。物理隔离确保了两个网络之间的完全分离&#xff0c;因此使用保密U盘可以作为一种安全的手段来…

第1章 物联网模式简介---物联网概述

物联网模式简介 物联网&#xff08;IoT&#xff09;在最近几年获得了巨大的吸引力&#xff0c;该领域在未来几年将呈指数级增长。这一增长将跨越所有主要领域/垂直行业&#xff0c;包括消费者、家庭、制造业、健康、旅游和运输。这本书将为那些想了解基本物联网模式以及如何混…

【大模型】大模型微调方法总结(四)

1. P-Tuning v1 1.背景 大模型的Prompt构造方式严重影响下游任务的效果。比如&#xff1a;GPT-3采用人工构造的模版来做上下文学习&#xff08;in context learning&#xff09;&#xff0c;但人工设计的模版的变化特别敏感&#xff0c;加一个词或者少一个词&#xff0c;或者变…

用英文介绍美国总统Trump: Donald J. Trump Twice Impeached (2017 – 2021)

Donald J. Trump: Twice Impeached (2017 – 2021) Link: https://www.youtube.com/watch?vJ7RC2DKf6rs&listPLybg94GvOJ9E-ZM1U6PAjgPUmz-V4-Yja&index45 Summary Summary of Donald Trump’s Rise and Presidency Donald John Trump, originally from Queens, Ne…

网页中如何接入高德地图【静态地图篇】

接入高德地图 登录高德开放平台创建应用添加key创建静态地图文档说明markers 网页应用总结 登录高德开放平台 高德开放平台 创建应用 点击我的应用 -> 创建应用 添加key 调相关接口都需要用到这个key&#xff01; 创建静态地图 静态地图API文档 文档说明 服务地址…

ArmSoM-Sige7/5/1 和树莓派5规格比较

引言 在当今快速发展的嵌入式系统领域&#xff0c;选择一款性能强大、功能丰富的开发板对于项目的成功至关重要。本文将介绍并比较 Sige7、Sige5、Raspberry Pi 5 和 Sige1 这四款开发板的关键规格和特性&#xff0c;帮助开发者和爱好者选择最适合其需求的平台。 ArmSoM-Sige…

使用模板方法设计模式封装 socket 套接字并实现Tcp服务器和客户端 简单工厂模式设计

文章目录 使用模板方法设计模式封装套接字使用封装后的套接字实现Tcp服务器和客户端实现Tcp服务器实现Tcp客户端 工厂模式 使用模板方法设计模式封装套接字 可以使用模块方法设计模式来设计套接字 socket 的封装 模板方法&#xff08;Template Method&#xff09;设计模式是一…

【深度学习】深度学习基础

李宏毅深度学习笔记 局部极小值与鞍点 鞍点其实就是梯度是零且区别于局部极小值和局部极大值的点。 鞍点的叫法是因为其形状像马鞍。鞍点的梯度为零&#xff0c;但它不是局部极小值。我们把梯度为零的点统称为临界点&#xff08;critical point&#xff09;。损失没有办法再下…

Docker Desktop 简易操作指南 (Windows, macOS, Linux)

1. 下载最新版本 Docker Desktop https://www.docker.com/products/docker-desktop/ 2.启动 Docker Desktop 3.常用命令&#xff08;在 cmd 或 Terminal 中执行&#xff09; #列出所有镜像&#xff08;Images&#xff09; docker images #列出所有容器&#xff08;Containers&…

CSS 核心知识点 - grid

思维导图 参考网址: https://developer.mozilla.org/zh-CN/docs/Web/CSS/CSS_grid_layout 一、什么是 grid&#xff1f; CSS Grid布局是在CSS3规范中引入的一种新的布局方式&#xff0c;旨在解决传统布局方法&#xff08;如浮动、定位、表格布局&#xff09;存在的许多问题。C…

DataWhale-吃瓜教程学习笔记(四)

学习视频&#xff1a;第3章-二分类线性判别分析_哔哩哔哩_bilibili 西瓜书对应章节&#xff1a; 3.4 文章目录 - 算法原理- 损失函数推导-- 异类样本中心尽可能远-- 同类样本方差尽可能小-- 综合 知识点补充 - 二范数二范数&#xff08;2-norm&#xff09;详解定义几何意义性质…

OpenHarmony开发实战:HDF驱动开发流程

概述 HDF&#xff08;Hardware Driver Foundation&#xff09;驱动框架&#xff0c;为驱动开发者提供驱动框架能力&#xff0c;包括驱动加载、驱动服务管理、驱动消息机制和配置管理。并以组件化驱动模型作为核心设计思路&#xff0c;让驱动开发和部署更加规范&#xff0c;旨在…

四川赤橙宏海商务信息咨询有限公司抖音开店靠谱吗?

在数字化浪潮席卷全球的今天&#xff0c;电商行业正以前所未有的速度发展。而在这个大潮中&#xff0c;四川赤橙宏海商务信息咨询有限公司凭借其专业的团队和前瞻性的战略眼光&#xff0c;专注于抖音电商服务&#xff0c;为广大商家提供了一站式解决方案&#xff0c;成为了行业…

Ubuntu20.04安装LibTorch并完成高斯溅射环境搭建

0. 简介 最近受到优刻得的使用邀请&#xff0c;正好解决了我在大模型和自动驾驶行业对GPU的使用需求。UCloud云计算旗下的Compshare的GPU算力云平台。他们提供高性价比的4090 GPU&#xff0c;按时收费每卡2.6元&#xff0c;月卡只需要1.7元每小时&#xff0c;并附带200G的免费…

接口自动化测试-项目实战

什么是接口自动化测试&#xff1a;使用工具或代码代替人对接口进行测试 测试项目结构&#xff08;python包&#xff09; 1、接口api包 2、script:业务脚本 3、data:数据 4、config.py :配置文件 5、reporter:报告 错误问题&#xff1a; 1、未打印任何东西。添加pip ins…

C语言 指针——缓冲区溢出与缓冲区溢出攻击

目录 缓冲区溢出攻击 缓冲区溢出攻击实例 字符串的安全输入方法​编辑 防止缓冲区溢出的两个要点 缓冲区溢出攻击 网络黑客常针对系统和程序自身存在的漏洞&#xff0c;编写相应的攻击程序  对缓冲区溢出漏洞的攻击 —— 最常见  几乎占到了网络攻击次数的一半以上…

Android (已解决)Gradle 编译失败 Unsupported class file major version 61

文章目录 一、报错原因二、解决方法 一、报错原因 新版本的 Android Studio 默认使用的是 Java 17 LTS&#xff0c;而这个历史项目的 Gradle 版本很低&#xff0c;不支持高版本的 Java。 具体原因&#xff1a;Java 17 (major version 61) 编译的 class 文件&#xff0c;如果在…

DevEco Studio有时会多出来.js和.map文件,导致项目不能运行

1、问题 在使用DevEco的时候有时候会出现啥都没干&#xff0c;但是在项目的目录下会自动生成和文件同名的.js和.js.map文件&#xff0c;至于为什么会生成目前我也不知道&#xff0c;如果想要更深了解可以到论坛讨论&#xff1a;华为开发者论坛。生成.js和.js.map文件优…

Terraform基础概念一

Terraform基础概念一 1.Infrastructure-as-Code(IaC)概念1.1 IaC优势1.2 IaC工具1.3 IaC的两种方式 2.Terraform基础概念2.1 Terraform工作原理2.2 Terraform 工作流 3.总结 1.Infrastructure-as-Code(IaC)概念 基础设施即代码&#xff08;Infrastructure-as-Code&#xff0c;…

SVN 的忽略(Ignore)和递归(Recursively)以及忽略部分

SVN中忽略大家经常用到&#xff0c;但总是似懂非懂&#xff0c;下面就详细展开说明一下忽略如何设置。 两个忽略 通常设置忽略都是文件夹和里面的文件都忽略&#xff0c;通常只需要鼠标右键点击忽略就可以了&#xff0c;如图&#xff1a; 第一个忽略用的最多&#xff0c;就是…