news 2026/8/27 12:47:18

浅谈 Android Handler

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
浅谈 Android Handler

handler是什么?

handler是Android提供用来更新UI的一套消息机制,也是一套消息处理的机制(发送和处理消息)

handler原理

handler负责消息发送,looper负责接收handler发送过来的消息,并把消息发送给handler,messageQueue存储消息的容器

这里先说明一下ThreadLocal,主要在线程中保存变量信息,主要有两个比较重要的方法,一个是get方法,一个是set方法

publicvoidset(Tvalue){Thread currentThread=Thread.currentThread();Values values=values(currentThread);if(values==null){values=initializeValues(currentThread);}values.put(this,value);}

image.gif

set方法设置当前线程的值,使用键值对的形式存储Thread和looper之间的关系,Thread作为key,looper作为value

publicTget(){// Optimized for the fast path.Thread currentThread=Thread.currentThread();Values values=values(currentThread);if(values!=null){Object[]table=values.table;int index=hash&values.mask;if(this.reference==table[index]){return(T)table[index+1];}}else{values=initializeValues(currentThread);}return(T)values.getAfterMiss(this);}

image.gif

get方法就是取出当前线程对应的looper,也就是说ThreadLocal是负责thread和looper之间的关系的

下面看一下Looper.prepare()方法

privatestaticvoidprepare(boolean quitAllowed){if(sThreadLocal.get()!=null){thrownewRuntimeException("Only one Looper may be created per thread");}sThreadLocal.set(newLooper(quitAllowed));}默认情况下ThreadLocal是没有存储的,所以要创建一个新的looperprivateLooper(boolean quitAllowed){mQueue=newMessageQueue(quitAllowed);mThread=Thread.currentThread();}

image.gif

默认情况下ThreadLocal是没有存储的,所以要创建一个新的looper

privateLooper(boolean quitAllowed){mQueue=newMessageQueue(quitAllowed);mThread=Thread.currentThread();}

image.gif

从looper方法中,创建了一个MessageQueue,在looper中维护着一个消息队列

知道了looper和MessageQueue之后,究竟handler跟这两者有什么关系呢,继续看源码

publicHandler(Callback callback,booleanasync){if(FIND_POTENTIAL_LEAKS){final Class<?extendsHandler>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());}}mLooper=Looper.myLooper();if(mLooper==null){thrownewRuntimeException("Can't create handler inside thread that has not called Looper.prepare()");}mQueue=mLooper.mQueue;mCallback=callback;mAsynchronous=async;}

image.gif

首先调用Looper.myLooper()

publicstatic@Nullable LoopermyLooper(){returnsThreadLocal.get();}

image.gif

获得当前的looper对象,通过looper拿到MessageQueue,就完成了handler和looper之间的关联

下面继续看handler的消息发送

publicbooleansendMessageAtTime(Message msg,long uptimeMillis){MessageQueue queue=mQueue;if(queue==null){RuntimeException e=newRuntimeException(this+" sendMessageAtTime() called with no mQueue");Log.w("Looper",e.getMessage(),e);returnfalse;}returnenqueueMessage(queue,msg,uptimeMillis);}

image.gif

先获得当前的消息队列,如果队列为空就抛出异常,不为空,向消息队列中插入消息

privatebooleanenqueueMessage(MessageQueue queue,Message msg,long uptimeMillis){msg.target=this;if(mAsynchronous){msg.setAsynchronous(true);}returnqueue.enqueueMessage(msg,uptimeMillis);}

image.gif

插入消息之前就指定消息发送给谁(msg.target),默认情况下发送给自己的handler,然后把消息放入队列中,handler就完成了发送message到MessageQueue的过程

那么消息又是如何轮询的呢?

publicstaticvoidloop(){final Looper me=myLooper();if(me==null){thrownewRuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");}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();for(;;){Message msg=queue.next();// might blockif(msg==null){// No message indicates that the message queue is quitting.return;}// This must be in a local variable, in case a UI event sets the loggerPrinter logging=me.mLogging;if(logging!=null){logging.println(">>>>> Dispatching to "+msg.target+" "+msg.callback+": "+msg.what);}msg.target.dispatchMessage(msg);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();if(ident!=newIdent){Log.wtf(TAG,"Thread identity changed from 0x"+Long.toHexString(ident)+" to 0x"+Long.toHexString(newIdent)+" while dispatching to "+msg.target.getClass().getName()+" "+msg.callback+" what="+msg.what);}msg.recycleUnchecked();}}

image.gif

通过myLooper()方法获取当前looper,进而获得当前的消息队列,然后通过MessageQueue的next方法获取消息,消息为空时返回,不为空时,调用handler的dispatchMessage(msg)方法,然后这个过程一直循环

publicvoiddispatchMessage(Message msg){if(msg.callback!=null){handleCallback(msg);}else{if(mCallback!=null){if(mCallback.handleMessage(msg)){return;}}handleMessage(msg);}}

image.gif

首先查看msg.callback是否为空,不为空时去调用handleCallback(msg),这个方法在handler的构造方法中存在,可以实现消息的拦截;为空只就

调用handleMessage(msg),这个方法都是大家熟悉的,不在描述,整体的handler的原理就描述到这。

总结

handler在Android中扮演的非常重要的角色,熟悉handler的原理,不仅在面试的时候有用,就连activity的生命周期也是通过handler发送消息,详细请看Framewrok源码。

很多人都没有Framewrok源码这方面的资料,这里我整理了一份《Android Framework源码开发揭秘》分享给大家!

由于文档内容过多,因此为了避免影响到大家的阅读体验,在此只以截图展示部分内容,详细完整版的看文末有免费的获取方式!(文末还有使用ChatGPT机器人小福利哦!!大家不要错过)

《Android Framework源码开发揭秘》

本学习手册深入剖析了Android系统源代码,详细讲解了Android框架初始化过程及主要组件的工作原理,旨在通过实例和案例介绍 Android Framework 的核心概念和技术,从而帮助开发者更好地理解 Android 应用程序的设计和开发。
该手册适合具有一定 Android 应用开发经验的程序员,希望能通过深入学习 Android Framework 来帮助开发者更好地理解和掌握这一技术。

第一章 系统启动流程分析

Android系统完整的启动过程,从系统层次角度可分为 Linux 系统层、Android 系统服务层、Zygote进程模型三个阶段;
知识要点:
第一节 Android启动概括、
第二节 init.rc解析、
第三节 Zygote、
第四节 面试题

第二章 跨进程通信IPC解析

Binder作为Android进程间通信的机制,可以看做是一个驱动。在Android中,常见的进程间通信例如系统类的:打电话、闹钟等;自己创建的:像WebView、视频播放、音频播放、大图浏览等。

第三章 Handler源码解析
第一节 源码分析
第二节 难点问题
第三节Handler常问面试题

有需要的朋友扫描下方二维码领取!!!!

同时这里还搭建了一个基于chatGPT的微信群聊机器人,24小时为大家解答疑难技术问题

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/27 12:43:51

张朝阳建议年轻人“每天6点起床”,你赞同吗?

前言 近日&#xff0c;搜狐创始人张朝阳、万科集团创始人王石等直播对谈。张朝阳对年轻人表示&#xff0c;如果要想做出贡献、留下痕迹&#xff0c;那么时间非常宝贵。“别看你们年轻&#xff0c;但是时间很快就会过去&#xff0c;所以要抓紧时间。不要熬夜&#xff0c;早点睡觉…

作者头像 李华
网站建设 2026/8/27 12:43:22

Android Flutter入门与实战(Flutter基本功能、路由管理、动画...)

Flutter是Google推出的跨平台移动开发框架&#xff0c;在过去的几年中&#xff0c;Flutter的发展势头非常强劲&#xff0c;并且受到了越来越多的开发者和企业的关注。 学习Flutter开发并掌握相关技能对于Android开发人员来说是非常有必要的&#xff0c;也是非常有前途的。首先&…

作者头像 李华
网站建设 2026/8/27 12:42:07

【提升栈】ListView优化

前言 ListView优化一直是一个老生常谈的问题。无论是面试还是寻常的开发中&#xff0c;ListView永远不会被忽略掉&#xff0c;那么这篇文章我们来看看怎样最大化的优化ListView的性能。 1.在adapter中的getView方法中尽量少使用逻辑2.尽最大可能避免GC3.滑动的时候不载入图片4.…

作者头像 李华
网站建设 2026/8/27 12:38:07

RGBWY五路独立调光:支持PD充电宝、适配器、储能电源

你见过&#xff0c;不带电池&#xff0c;却能用充电宝供电的美容大排灯吗&#xff1f;采用PD方案的美容大排灯传统美容大排灯一般采用插电或锂电池供电&#xff0c;而大容量电池不仅成本高&#xff0c;充电时间也更长。现在越来越多的LED医美和光疗产品&#xff0c;开始采用 Ty…

作者头像 李华
网站建设 2026/8/27 12:37:06

给大家普及一下,腾讯Agent开发社招要达到什么强度

很多人觉得&#xff0c;腾讯Agent开发就是会调用大模型、会搭几个Agent Demo。 如果你真这么想&#xff0c;面试大概率过不了。 最近看了腾讯公开招聘的Agent岗位&#xff0c;再结合身边已经上岸朋友的反馈&#xff0c;我发现腾讯现在招Agent开发&#xff0c;考察重点已经变了。…

作者头像 李华