【Android 源码解析】从源码角度深入分析Handler消息机制

新一代XMOS会议四麦方案主控芯片 XVF3800 2023年,知名的音频芯片制造厂商 XMOS 正式发布了新一代会议麦阵芯片片 XVF3800 ,这款芯片采用最先进的四麦音频算法,可在嘈杂的环境中提供高质量语音,算法支持圆形、条形阵列,可根据应用场景组成不同的产品形态。传统的麦阵解决方案,不仅需要捕捉会议中的人声,也会同时捕捉环境中的其他噪声,而 XMOS 先进的语音算法解决了这一问题。声学回声消除功能可消除麦克风音频输入的回声,同时减少远端回声,使 XVF3800 能提取清晰的语音信号。• 180度拾音条形麦克风阵列。• 360度拾音环形麦克风阵列。 阅读详情

一、引言

说起消息机制,相信每一个Android开发都不陌生。从开始写Android代码的第一天起,我就一直在试图理解handler机制,这几年中也不断去读handler源码。最近有了一些收获,觉得自己已经非常了解handler源码了,故在这里做下总结,希望分享给需要的小伙伴。

首先先看如上这个异常:只有创建View的原始线程才能更新UI。这个异常在什么情况下出现呢?在Android系统中,只能在主线程更新UI,如果在工作线程更新UI,就会报这个异常。出现这个问题的根本原因是:Android中的View不是线程安全的。

最简单的办法是给View加同步锁使其变成线程安全的。这样做不是不可以,只是有两点坏处:

1. 加锁会导致效率低下

2. 多个地方更新UI,开发时很容易导致时序错误。

基于以上两点,这种方式被抛弃。所以在Android中,专门设置了一个线程来处理UI控件的更新,这样就做到了“职责明确,各司其职”,我们开发时也很好理解。当其他线程要更新UI,就发送消息交给UI线程去处理。

实现原理呢?一条线程如何发送一个消息给另外一个线程呢?是不是可以找到两个线程共用某个资源的地方?不同进程享用不同的内存空间,但是同一个进程的不同线程享用同一片内存空间,那让其他线程吧要处理的消息放到特定的内存空间上,处理消息的线程来这个内存空间来去消息处理不就可以了吗?事实上,在Android消息机制中,扮演这个特定内存空间的对象就是MessageQueue。Handler和Looper都是为了配合线程切换用的。

其实不仅仅线程之间,不同进程之间进行消息传递也是这个思路,找到一个公用的一个资源点,文件系统啊,共享内存啊。

在Android应用启动时会自动创建一个线程(程序的主线程,负责View的展示,事件消息的分发,所以主线程常常被称为UI线程)。在Android平台中引入Handler,通过在子线程发送消息给主线程来更新UI。

 

二、什么是Handler?

Handler是Android消息机制的上层接口。在我们日常的开发工作中,我们只需要知道如何创建Handler并复写它的handleMessage方法就可以使用Handler了。我们可以简单地把Handler认为是更新UI的一种机制,它可以让耗时操作放在子线程,更新UI的操作放在主线程。

Handler通过发送和处理Message和Runnable对象来关联相应线程的MessageQueue。

 

三、子线程如何更新UI?

  Handler在这个时候就闪亮登场了!!

可以看到,在接口返回数据后,红框框出来的部分是发送了一个Message,而没有直接更新UI。然后在handleMessage中更新UI。

以上就是Handler的一个具体使用。

 

总结一下,Handler该如何使用呢?

(1)post(runnable)

        

        通过查看post(runnable)源码可知,其源码实现其实是通过sendMessage来实现。

(2)sendMessage

        

其背后的原理又是什么呢?

 

四、Handler消息机制图解

首先来看这样一张图,这个图里包含了4大部分:Looper、MessageQueue、Message、Handler。

        

Looper是每个线程独有的,它通过loop方法,读取MessageQueue中的Message,并发送给Handler进行处理。

 

五、从源码角度理解Android消息机制

mHandler.sendMessage(msg)其实最终调用的是sendMessageAtTime(msg,long)方法。

public boolean sendMessageAtTime(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);
    }

它其实就是调用了enqueueMessage方法将Message发送到MessageQueue尾部。接下来就等着别人来调用Handler中的方法了,可是是谁调用的,在哪调用的?

Loop.loop()方法一直遍历MessageQueue,阻塞线程,直到获取到一个Message,然后调用Message的一个成员变量target(其实就是Handler)的dispatchMessage(msg

)方法。

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

可以看到最终调用了Handler的handleMessage方法。

 

这里应该注意的是loop方法,它其实是一个死循环,当ActivityThread一启动Looper的loop方法就一直在执行!其中,ActivityThread其实就是我们所谓的主线程。

public static void loop() {
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("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 block
            if (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 logger
            final Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            final long traceTag = me.mTraceTag;
            if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
            }
            try {
                msg.target.dispatchMessage(msg);
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }

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

不过在看过下面的源码你可能会疑问:这个loop方法明明在msg为null时return了啊,这不就执行结束了吗?其实不是的。我们来看看queue.next方法,可以看到它其实也是一个死循环,只有当发送过来的消息是“quit”的时候才会返回null。

在我们创建Handler的时候,它需要与Looper关联。MessageQueue就是Looper的成员变量mQueue。如果没有looper,那就会报错“Can't create handler inside thread that has not called Looper.prepare()”。所以在new Handler的时候一定要调用过Looper.prepare(),在主线程中new Handler()没有调用Looper.prepare()是因为主线程默认就调过这个方法。

public Handler(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());
            }
        }

        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread that has not called Looper.prepare()");
        }
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }


到此,我们就深入理解了消息队列、Handler、Looper三者之间的关系:一一对应!

接着再看Looper类的 loop方法,我们知道,这其实是一个死循环,这里面一直在做一件事:

	    try {
                msg.target.dispatchMessage(msg);
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }    try {
                msg.target.dispatchMessage(msg);
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }

其中,msg.target就是Handler对象。那么Handler的dispatchMessage做了啥?

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

一目了然,就是在handleMessage。

 

六、Handler引起的内存泄漏以及它的解决办法

    内部类持有外部类的强引用,导致外部Activity无法被释放。

    如何解决呢?

    (1)handler内部持有Activity的弱引用,并把Handler改为静态内部类

    (2)在Activity的onDestroy方法中removeMessageAndCallbacks

 

七、最后

这里从源码角度讲清楚了MessageQueue、Handler、Looper的关系。如有任何疑问,欢迎留言或邮件联系zhshan@ctrip.com,博主每天都会查看。

 

~~~~~~~~~~~~~~~华丽丽的分割线:View.post()到底做了啥?~~~~~~~~~~~~~~~~~~~~

    在日常的开发过程中,很多奇葩问题总可以通过View.post()方法解决。比如,View.post()中的操作执行时,View的宽高已经计算完毕,所以经常看见在Activity的onCreate()里调用View.post()来解决获取View宽高为0的问题。我们来梳理一下,View.post()原理到底是什么,内部都做了啥事。

    View.post()方法很简单,代码很少,我们来分析一下。

    如果AttachInfo不为空,那就调用mAttachInfo.mHandler.post()方法;如果为空,那就调用getRunQueue.post()方法。

   那就找一下,mAttachInfo是什么时候赋值的。通过查看源码,可以看到在dispatchAttachedToWindow中赋值,在dispatchDetachedFromWindow中置空。那dispatchAttachedToWindow什么时候被调用呢?至少我们清楚一点,在Activity的onCreate期间,这个方法还没有调。那为什么View.post()方法可以保证在View宽高计算完毕呢?

    我们来看下getRunQueue()方法里做了什么。

    所以,其实调用的是HandlerActionQueue.post()方法,我们跟进去看一下。

可以看到是将runnable作为参数创建一个HandlerAction对象,然后放到mActions数组里。那保存到数组之后,什么时候会执行呢?我们找到了这个方法executeActions

从名字可以看出这是从数组里取出Action处理,那它是在哪里调用的呢?

可以看到是在dispatchAttachedToWindow生命周期方法里调用的。这下我们的思路就清晰了。当执行View.post()方法时,页面还没有渲染完毕,那就会等到dispatchAttachedToWindow生命周期方法回调时调用。

我们可以猜想,这个方法肯定就是在页面渲染结束后才回调的,这里就不写验证过程了,具体可以去查看源码。

 

参考

https://www.jianshu.com/p/e7b6fa788ae6

https://mp.weixin.qq.com/s/laR3_Xvr0Slb4oR8D1eQyQ

【1】Python交叉编译到OpenHarmony标准系统运行(arm32位) 本文介绍如何Python语言如何在OpenHarmony标准系统运行,包括5.0r和4.1r以及4.0r,和未来版本的OpenHarmony版本上。 Python语言在OpenHarmony上使用,需要将Python解释器CPython移植到OpenHarmony标准系统。通过交叉编译的方式。 阅读详情

相关推荐

排除 AcroForm 中 JavaScript 脚本的 Bug

本文介绍了Acrobat JavaScript脚本故障排除的基本方法。首先应检查控制台窗口的错误报告,通过具体示例演示了5类常见错误:语法错误(如缺少大括号)、未终止字符串、未定义变量、运算符混淆及变量作用域问题,并逐步给出修复方案。文章还提供了调试流程UML图和实用经验总结,强调关注细节(如分隔符匹配)、正确使用运算符和变量作用域等要点。最后建议采用系统化调试方法,包括关闭/重新打开表单等完整性测试。

小猫三两只。 1504

【React Native】环境搭建的error calling AppRegistery.runApplication解决办法

折腾了几天,终于把开发环境搭建成功了。这中间遇到了太多坑:(1)公司台式机没有无线网卡,而手机和packager必须要连接同一网段。只能去淘宝买一个无线网卡装上。(2)init出工程后,一直报错error calling AppRegistery.runApplication。     这个bug网上的答案非常少,以至于我在想这肯定是携程的windows台式机做了什么配置,然后尝试去修改host文...

你关注了我,是个概率极低的事件...... 6853

【现代电路理论】【2】考尔法设计无源单口网络

夏 弥:其实你在冰面上看到鱼浮上来 明年冬天如果你还在那里,那鱼还是会上来换气 那时候你就可以带一把冰镐 把冰面砸开把鱼捞上来熬汤

痛苦的时候装出幸福相,这不是那么难做到的事 2886

AppRegistry详细解析

在React-native中有一个api叫AppRegistry。这是RN中的一个重要api。 接口介绍 static registerComponent(appKey, componentProvider, section?)注册组件,js中非常重要的一步,只有这里注册了,native code中才会找到要执行的js。 static registerRunnable(appKey, run)...

道的博客 2714

AWR射频仿真基本操作(一)

这里只讲述一个基本的操作: 软件是AWR 14版本

钢丝 2万+

网络安全学习路线是怎样的?

为什么要学习安全? 近几年网络安全事件层出不穷,相信大家哪怕没遇到过,但也听过不少了吧。如服务器遭受入侵被黑,用户帐号被盗;被钓鱼、勒索病毒等,这些一旦发生,对企业而言都是不小的打击。因此,网络安全不容忽视,同时随着时代的不断发展,公司对于运维及网络工程师要求也越来越严格。未来越来越吃香的一定是技能方向广的复合型人才。网络安全可以说是一条非常好的发展道路。 今年两会期间,全国政协委员,360集团创始人、董事长周鸿祎提交了关于网络安全行业特殊人才的提案,为广大白帽子人员争取更好...

网安技术分享 8697

【Android源码篇】 深入剖析Handler内部原理

文章目录一、Handler介绍二、Handler源码剖析1. Handler构造函数:2. 创建Looper对象和mQueue消息队列3. sendMessage4. removeMessages5. post(Runnable r)6. dispatchMessage和handlerMessage三、Looper源码剖析1. myLooper方法2. Looper.prepare()创建对象3....

sslinp的博客 4194

【2024遥感应用组一等奖】基于时空融合和水量平衡法的若尔盖高原水源涵养功能评价

考虑到水源涵养的概念和尺度特征、数据的可获得性、模型的模拟精度等,构建了一种基于遥感、气象和水量平衡原理的草原湿地水源涵养评价方法,估算若尔盖湿地近23年(2000-2022)来的水源涵养量。若尔盖湿地是我国黄河源区的重要水源地,所以本研究以若尔盖湿地的水源涵养功能评估为科学问题,基于遥感数据、GIS数据等,利用水量平衡原理,发展计算简单、便捷、数据易获取的“水源涵养估算方法”,科学估算若尔盖湿地水源涵养服务量,探究其时空动态变化趋势及其影响因素;公式中,Z1和Z2为两个不同高度面的高度,u为摩擦速率;

JData_Engineer的博客 239

运行React Native项目报:error calling appregistry.run application

解决方案有二: 1、在webstorm命令行输入react-native start,出现错误error calling appregistry.run application,表示端口被占用 。然后通过命令行查找占用程序并关闭。 2、重启电脑,重新运行

孙朝阳的博客 9747

影像技术实战02:FFmpeg 镜头切分误检、漏检问题排查与基于场景变化的稳定分镜方案

本文介绍了基于FFmpeg的镜头切分技术在实际应用中的常见问题及解决方案。文章首先指出单纯使用select='gt(scene,0.4)'命令进行镜头检测存在误检、漏检等问题,包括黑场、字幕变化被误判为镜头切换,以及相似场景切换被漏检等情况。随后提出了一套四层架构的稳定解决方案:1)用FFmpeg提取候选镜头变化点;2)解析变化点时间和分数;3)通过后处理过滤异常点;4)按时间点切割视频片段并生成报告。文中详细展示了Python实现代码,包括场景点检测、时间间隔过滤等功能模块,为视频剪辑、影视解说等场景提供

ai技术增长的博客 402

09 SAP CPI 访问公共API接口

我在网上找了一个公共API,入参输入汉字返回拼音。至此,一个真正的访问API的接口就做好了。点击保存时可看到红叉报错。

SAP CPI 477

从Handler+Message+Looper源码带你分析Android系统的消息处理机制

引言 【转载请注明出处:http://blog.csdn.net/feiduclear_up 优快云 废墟的树】 作为Android开发者,相信很多人都使用过Android的Handler类来处理异步任务。那么Handler类是怎么构成一个异步任务处理机制的呢?这篇 博客带你从源码分析Android的消息循环处理机制,便于深入的理解。 这里不得不从“一个Bug引发的思考”开始研究Andr...

baidu_32472003的博客 1197

RNA-seq分析:Step4(比对)

预处理之后的fastq文件,可以使用HISAT2软件将reads比对至参考基因组上。HISAT2软件将reads比对至参考基因组上的步骤如下:1. 对参考基因组进行建索引。对参考基因组进行预处理,生成索引文件,包括基因组序列的FM索引和SA索引。2. 将原始的reads进行预处理。包括去除接头序列、去除低质量的碱基,以及进行过滤,得到质量较高的、可靠的reads。3. 利用建立的FM索引和SA索引,将处理后的reads与参考基因组进行比对。首先,通过FM索引,快速找到与reads匹配的可能位置;

沉香best的博客 2599

安卓消息机制之Handler基础知识

相信大家从网上也看到过Handler的代码解析,下面我将从我个人理解的角度,以尽量通俗易懂的语言带领大家对Handler的用法以及源码设计进行解读。

weixin_39330114的博客 1452

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

【Android】从源码角度理解Handler机制

Handler通常用来做主线程与子线程之间的通信工具。比如,在子线程中进行耗时操作(网络请求、数据库操作等),子线程在获取数据后,使用Handler来进行UI更新:子线程拿到数据后,不直接更新界面,而是把数据通过一个消息(Message)发送出去;主线程这边,会提前准备好一个“接收器”——Handler对象。这个Handler对象会专门接受子线程发送来的数据,把获取到的数据显示在界面上。Handler:消息的发送者和处理者:消息队列,存储待处理的消息Looper。

2302_79323248的博客 998

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

Android-源码角度解析Handler通信机制

这篇文章将从源码角度梳理Handler(处理器)、Message(消息体)、MessageQueue(消息队列)、Looper(循环器)之间的关系。 Handler的创建: public Handler(Callback callback, boolean async) { if (FIND_POTENTIAL_LEAKS) { final Class<? extends Handler> klass = getClass();

Stella的博客 1815

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

从源码角度简析 Android 消息机制

MessageQueue 源码解析 enqueueMessage() next() Looper 源码解析 prepare() loop() quit()、quitSafely() Handler 源码解析 构造方法 send() 系列 post() 系列 dispatchMessage() 流程一览 Handler 最佳写法 补充 MessageQueue 源码解析MessageQueue

joker 1341

从源码角度剖析Handler 机制

android中,在进行耗时操作更新UI用到最多的方法就是Handler了,一般在子线程中进行耗时操作(访问网络等),然后发送消息到UI线程(主线程),使得界面得以更新。对于Handler的用法,相信大家都比较熟悉了,那么Handler机制的原理是什么呢?我们今天从源码角度来分析下Handler机制的原理。Handler mHandler = new Handler(){ publ

JasonGaoH的专栏 1068

Android Handler消息机制源码解读

这个东西在网上已经被很多人写过了,但是自己也看过很多文章,大概因为自己比较愚笨一直对此不太理解,最近重新从源码的角度阅读,并且配合着网上的一些相关博客才算明白了一些 本文从源码的角度顺着代码的执行去源码,限于作者的表达能力及技术水平,可能会有些问题,请耐性阅读,如有不解或有误的地方欢迎提出 从ActivityThread的入口去看 ActivityThread.class pu...

武凯凯 558

从源码角度解析Android消息机制

在android项目的开发中我们经常会有需求在其他线程内更新UI界面,但是系统并不允许我们这么干。android的UI系统被设计成单线程访问模式,深究其原因,无非也是担心在多线程访问的情况下可能会导致界面更新的混乱,最终形成了这种界面只能在UI线程中进行更新的结局。但是android自身给我们提供了一些很方便的方法可以让我们在其他线程中容易的更新UI界面,其中最典型的有asynctask,hand

FSC的专栏 557

Android源码解析:Handler机制

关于如何在Android中进行多线程通信,使用Handler机制是一个好方法。Android提供的Handler机制为我们提供了方便的方法进行线程间通信,具体来说,Handler将会绑定一个线程和Looper,Looper将MessageQueue中的Message取出给Handler进行处理。我们可以看一下这幅图先来有一个大概的印象:本篇文章将从源码的角度对Handler机制进行进一步分析。首先我们需要明确一点,那就是Handler机制的设计是基于生产者-消费者模式的。这和Android UI是单线程的也

Tai_Monster的博客 447

Android异步消息处理机制完全解析,带你从源码的角度彻底理解

我们都知道,Android UI是线程不安全的,如果在子线程中尝试进行UI操作,程序就有可能会崩溃。相信大家在日常的工作当中都会经常遇到这个问题,解决的方案应该也是早已烂熟于心,即创建一个Message对象,然后借助Handler发送出去,之后在Handler的handleMessage()方法中获得刚才发送的Message对象,然后在这里进行UI操作就不会再出现崩溃了。这种处理方式被称为异步消息处理线程,虽然我相信大家都会用,可是你知道它背后的原理是什么样的吗?今天我们就来一起深入探究一下Handler和

郭霖的专栏 11万+

Handler消息机制超强源码解析

Handler消息机制在Android中占据重要的地位,主要使用场景就是线程间通讯。当我们在子线程执行完耗时操作后可以使用Handler通知UI线程去更新UI。对于一个有追求的开发者来说,我们不仅要会使用Handler,更要去深入探究其中的原理。本篇文章就来详细分析Handler的源码并从源码的角度来分析使用过程中经常出现的问题。 Handler消息机制的核心 在Handler机制中有三个重要...

烽火假动作的博客 244

android handler的机制和原理_Android消息机制的原理及源码解析

提起Android消息机制,想必都不陌生。其中包含三个部分:Handler,MessageQueue以及Looper,三者共同协作,完成消息机制的运行。本篇文章将由浅入深解析Android消息机制的运行原理,先介绍三者之间的协作框架,然后从源码的角度来分析具体的运行机制。一、消息机制概述1.消息机制的简介在Android中使用消息机制,我们首先想到的就是Handler。没错,Handler是And...

weixin_39587238的博客 94

android handler的机制和原理_一文搞懂handler:彻底明白Android消息机制的原理及源码

提起Android消息机制,想必都不陌生。其中包含三个部分:Handler,MessageQueue以及Looper,三者共同协作,完成消息机制的运行。本篇文章将由浅入深解析Android消息机制的运行原理,先介绍三者之间的协作框架,然后从源码的角度来分析具体的运行机制。文章已收录至我的知乎专栏《进击的Android秃头》,建议看之前,点一波双关(关注作者+关注专栏)。你会意想不到获得满满的干货!...

weixin_39662142的博客 292

从源码角度理解Handler消息机制

Handler是Android中的核心机制。通过Handler可以将Message发送到Handler所在线程对应的MessageQueue中。每一个Handler实例都只会属于唯一一个线程,也只会对应唯一一个MessageQueue。 源码解析 使用Handler第一步都要实例化,来看看Handler的构造函数: //默认构造函数,将会使用当前线程对应的Looper,如果当前线程没有Looper则会抛出异常 public Handler() { this(null, false); } //传入

折翅鵬的博客 413
上一篇: 【Android 前沿技术】用MediaPlayer+TextureView封装好的视频播放器,可直接使用(附demo)
下一篇: 【Android 自定义控件】自定义View相关知识总结
Colin_Mindset
博客等级 码龄12年 159粉丝 105原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值