View的post方法到底干了啥,深入分析Android异步消息处理机制

Android 非UI线程使用View.post()方法一处潜在的内存泄漏 最近开发中,使用 AsyncTask + ProgressDialog 显示进度信息,但在AsyncTask停止,Activity finish 后该Activity的实例始终不会被gc,多次运行程序后,会存在多个activity,造成内存泄漏。 下面详细分析一下: 一份显示进度条的测试代码: public class Main extends Activity { @Overri 阅读详情

emmm,大伙都知道,子线程是不能进行 UI 操作的,或者很多场景下,一些操作需要延迟执行,这些都可以通过 Handler 来解决。但说实话,实在是太懒了,总感觉写 Handler 太麻烦了,一不小心又很容易写出内存泄漏的代码来,所以为了偷懒,我就经常用 View.post() or View.postDelay() 来代替 Handler 使用。

但用多了,总有点心虚,View.post() 会不会有什么隐藏的问题?所以趁有点空余时间,这段时间就来梳理一下,View.post() 原理到底是什么,内部都做了啥事。

提问

开始看源码前,先提几个问题,带着问题去看源码应该会比较有效率,防止阅读源码过程中,陷得太深,跟得太偏了。

Q1: 为什么 View.post() 的操作是可以对 UI 进行操作的呢,即使是在子线程中调用 View.post()?

Q2:网上都说 View.post() 中的操作执行时,View 的宽高已经计算完毕,所以经常看见在 Activity 的 onCreate() 里调用 View.post() 来解决获取 View 宽高为0的问题,为什么可以这样做呢?

Q3:用 View.postDelay() 有可能导致内存泄漏么?

ps:本篇分析的源码基于 andoird-25 版本,版本不一样源码可能有些区别,大伙自己过源码时可以注意一下。另,下面分析过程有点长,慢慢看哈。

源码分析

好了,就带着这几个问题来跟着源码走吧。其实,这些问题大伙心里应该都有数了,看源码也就是为了验证心里的想法。第一个问题,之所以可以对 UI 进行操作,那内部肯定也是通过 Handler 来实现了,所以看源码的时候就可以看看内部是如何对 Handler 进行封装的。而至于剩下的问题,那就在看源码过程中顺带看看能否找到答案吧。

View.post()

View.post() 方法很简单,代码很少。那我们就一行行的来看。

如果 mAttachInfo 不为空,那就调用 mAttachInfo.mHanlder.post() 方法,如果为空,则调用 getRunQueue().post() 方法。

那就找一下,mAttachInfo 是什么时候赋值的,可以借助 AS 的 Ctrl + F 查找功能,过滤一下 mAttachInfo =,注意 = 号后面还有一个空格,否则你查找的时候会发现全文有两百多处匹配到。我们只关注它是什么时候赋值的,使用的场景就不管了,所以过滤条件可以细一点。这样一来,全文就只有两处匹配:







一处赋值,一处置空,刚好又是在对应的一个生命周期里:

  1. dispatchAttachedToWindow() 下文简称 attachedToWindow
  2. dispatchDetachedFromWindow() 下文简称 detachedFromWindow。

所以,如果 mAttachInfo 不为空的时候,走的就是 Handler 的 post(),也就是 View.post() 在这种场景下,实际上就是调用的 Handler.post(),接下去就是搞清楚一点,这个 Handler 是哪里的 Handler,在哪里初始化等等,但这点可以先暂时放一边,因为 mAttachInfo 是在 attachedToWindow 时才赋值的,所以接下去关键的一点是搞懂 attachedToWindow 到 detachedFromWindow 这个生命周期分别在什么时候在哪里被调用了。

虽然我们现在还不清楚,attachedToWindow 到底是什么时候被调用的,但看到这里我们至少清楚一点,在 Activity 的 onCreate() 期间,这个 View 的 attachedToWindow 应该是还没有被调用,也就是 mAttachInfo 这时候还是为空,但我们在 onCreate() 里执行 View.post() 里的操作仍然可以保证是在 View 宽高计算完毕的,也就是开头的问题 Q2,那么这点的原理显然就是在另一个 return 那边的方法里了:getRunQueue().post()。

那么,我们就先解决 Q2 吧,为什么 View.post() 可以保证操作是在 View 宽高计算完毕之后呢?跟进 getRunQueue() 看看:

getRunQueue().post()

所以调用的其实是 HandlerActionQueue.post() 方法,那么我们再继续跟进去看看:


post(Runnable) 方法内部调用了 postDelayed(Runnable, long),postDelayed() 内部则是将 Runnable 和 long 作为参数创建一个 HandlerAction 对象,然后添加到 mActions 数组里。下面先看看 HandlerAction:


很简单的数据结构,就一个 Runnable 成员变量和一个 long 成员变量。这个类作用可以理解为用于包装 View.post(Runnable) 传入的 Runnable 操作的,当然因为还有 View.postDelay() ,所以就还需要一个 long 类型的变量来保存延迟的时间了,这样一来这个数据结构就不难理解了吧。

所以,我们调用 View.post(Runnable) 传进去的 Runnable 操作,在传到 HandlerActionQueue 里会先经过 HandlerAction 包装一下,然后再缓存起来。至于缓存的原理,HandlerActionQueue 是通过一个默认大小为4的数组保存这些 Runnable 操作的,当然,如果数组不够用时,就会通过 GrowingArrayUtils 来扩充数组,具体算法就不继续看下去了,不然越来越偏。

到这里,我们先来梳理下:

当我们在 Activity 的 onCreate() 里执行 View.post(Runnable) 时,因为这时候 View 还没有 attachedToWindow,所以这些 Runnable 操作其实并没有被执行,而是先通过 HandlerActionQueue 缓存起来。

那么到什么时候这些 Runnable 才会被执行呢?我们可以看看 HandlerActionQueue 这个类,它的代码不多,里面有个 executeActions() 方法,看命名就知道,这方法是用来执行这些被缓存起来的 Runnable 操作的:


哇,看到重量级的人物了:Handler。看来被缓存起来没有执行的 Runnable 最后也还是通过 Hnadler 来执行的。那么,这个 Handler 又是哪里的呢?看来关键点还是这个方法在哪里被调用了,那就找找看:


借助 AS 的 Ctrl + Alt + F7 快捷键,可以查找 SDK 里的某个方法在哪些地方被调用了。


很好,找到了,而且只找到这个地方。其实,这个快捷键有时并没有办法找到一些方法被调用的地方,这也是源码阅读过程中令人头疼的一点,因为没法找到这些方法到底在哪些地方被调用了,所以很难把流程梳理下来。如果方法是私有的,那很好办,就用 Ctrl + F 在这个类里找一下就可以,如果匹配结果太多,那就像开头那样把过滤条件详细一点。如果方法不是私有的,那真的就很难办了,这也是一开始找到 dispatchAttachedToWindow() 后为什么不继续跟踪下去转而来分析Q2:getRunQueue() 的原因,因为用 AS 找不到 dispatchAttachedToWindow() 到底在哪些地方被谁调用了。哇,好像又扯远了,回归正题回归正题。

emmm,看来这里也绕回来了,dispatchAttachedToWindow() 看来是个关键的节点。

那到这里,我们再次来梳理一下:

我们使用 View.post() 时,其实内部它自己分了两种情况处理,当 View 还没有 attachedToWindow 时,通过 View.post(Runnable) 传进来的 Runnable 操作都先被缓存在 HandlerActionQueue,然后等 View 的 dispatchAttachedToWindow() 被调用时,就通过 mAttachInfo.mHandler 来执行这些被缓存起来的 Runnable 操作。从这以后到 View 被 detachedFromWindow 这段期间,如果再次调用 View.post(Runnable) 的话,那么这些 Runnable 不用再缓存了,而是直接交给 mAttachInfo.mHanlder 来执行。

以上,就是到目前我们所能得知的信息。这样一来,Q2 是不是渐渐有一些头绪了:View.post(Runnable) 的操作之所以可以保证肯定是在 View 宽高计算完毕之后才执行的,是因为这些 Runnable 操作只有在 View 的 attachedToWindow 到 detachedFromWiondow 这期间才会被执行。

那么,接下去就还剩两个关键点需要搞清楚了:

  1. dispatchAttachedToWindow() 是什么时候被调用的?
  2. mAttachInfo 是在哪里初始化的?

dispatchAttachedToWindow() & mAttachInfo

只借助 AS 的话,很难找到 dispatchAttachedToWindow() 到底在哪些地方被调用。所以,到这里,我又借助了 Source Insight 软件。


很棒!找到了四个被调用的地方,三个在 ViewGroup 里,一个在 ViewRootImpl.performTraversals() 里。找到了就好,接下去继续用 AS 来分析吧,Source Insight 用不习惯,不过分析源码时确实可以结合这两个软件。


哇,懵逼,完全懵逼。我就想看个 View.post(),结果跟着跟着,跟到这里来了。ViewRootImpl 我在分析Android KeyEvent 点击事件分发处理流程时短暂接触过,但这次显然比上次还需要更深入去接触,哎,力不从心啊。

我只能跟大伙肯定的是,mView 是 Activity 的 DecorView。咦~,等等,这样看来 ViewRootImpl 是调用的 DecorView 的 dispatchAttachedToWindow() ,但我们在使用 View.post() 时,这个 View 可以是任意 View,并不是非得用 DecorView 吧。哈哈哈,这是不是代表着我们找错地方了?不管了,我们就去其他三个被调用的地方: ViewGroup 里看看吧:


addViewInner() 是 ViewGroup 在添加子 View 时的内部逻辑,也就是说当 ViewGroup addView() 时,如果 mAttachInfo 不为空,就都会去调用子 View 的 dispatchAttachedToWindow(),并将自己的 mAttachInfo 传进去。还记得 View 的 dispatchAttachedToWindow() 这个方法么:


mAttachInfo 唯一被赋值的地方也就是在这里,那么也就是说,子 View 的 mAttachInfo 其实跟父控件 ViewGroup 里的 mAttachInfo 是同一个的。那么,关键点还是这个 mAttachInfo 什么时候才不为空,也就是说 ViewGroup 在 addViewInner() 时,传进去的 mAttachInfo 是在哪被赋值的呢?我们来找找看:


咦,利用 AS 的 Ctrl + 左键 怎么找不到 mAttachInfo 被定义的地方呢,不管了,那我们用 Ctrl + F 搜索一下在 ViewGroup 类里 mAttachInfo 被赋值的地方好了:


咦,怎么一个地方也没有。难道说,这个 mAttachInfo 是父类 View 定义的变量么,既然 AS 找不到,我们换 Source Insight 试试:



还真的是,ViewGroup 是继承的 View,并且处于同一个包里,所以可以直接使用该变量,那这样一来,我们岂不是又绕回来了。前面说过,dispatchAttachedToWindow() 在 ViewGroup 里有三处调用的地方,既然 addViewInner() 这里的看不出什么,那去另外两个地方看看:


剩下的两个地方就都是在 ViewGroup 重写的 dispatchAttachedToWindow() 方法里了,这代码也很好理解,在该方法被调用的时候,先执行 super 也就是 View 的 dispatchAttachedToWindow() 方法,还没忘记吧,mAttachInfo 就是在这里被赋值的。然后再遍历子 View,分别调用子 View 的 dispatchAttachedToWindow() 方法,并将 mAttachInfo 作为参数传递进去,这样一来,子 View 的 mAttachInfo 也都被赋值了。

但这样一来,我们就绕进死胡同了。

我们还是先来梳理一下吧:

目前,我们知道,View.post(Runnable) 的这些 Runnable 操作,在 View 被 attachedToWindow 之前会先缓存下来,然后在 dispatchAttachedToWindow() 被调用时,就将这些缓存下来的 Runnable 通过 mAttachInfo 的 mHandler 来执行。在这之后再调用 View.post(Runnable) 的话,这些 Runnable 操作就不用再被缓存了,而是直接交由 mAttachInfo 的 mHandler 来执行。

所以,我们得搞清楚 dispatchAttachedToWindow() 在什么时候被调用,以及 mAttachInfo 是在哪被初始化的,因为需要知道它的变量如 mHandler 都是些什么以及验证 mHandler 执行这些 Runnable 操作是在 measure 之后的,这样才能保证此时的宽高不为0。

然后,我们在跟踪 dispatchAttachedToWindow() 被调用的地方时,跟到了 ViewGroup 的 addViewInner() 里。在这里我们得到的信息是如果 mAttachInfo 不为空时,会直接调用子 View 的 dispatchAttachedToWindow(),这样新 add 进来的子 View 的 mAttachInfo 就会被赋值了。但 ViewGroup 的 mAttachInfo 是父类 View 的变量,所以为不为空的关键还是回到了 dispatchAttachedToWindow() 被调用的时机。

我们还跟到了 ViewGroup 重写的 dispatchAttachedToWindow() 方法里,但显然,ViewGroup 重写这个方法只是为了将 attachedToWindow 这个事件通知给它所有的子 View。

所以,最后,我们能得到的结论就是,我们还得再回去 ViewRootImpl 里,dispatchAttachedToWindow() 被调用的地方,除了 ViewRootImpl,我们都分析过了,得不到什么信息,只剩最后 ViewRootImpl 这里了,所以关键点肯定在这里。看来这次,不行也得上了。

ViewRootImpl.performTraversals()

这方法代码有八百多行!!不过,我们只关注我们需要的点就行,这样一省略无关代码来看,是不是感觉代码就简单得多了。

mFirst 初始化为 true,全文只有一处赋值,所以 if(mFirst) 块里的代码只会执行一次。我对 ViewRootImpl 不是很懂,performTraversals() 这个方法应该是通知 Activity 的 View 树开始测量、布局、绘制。而 DevorView 是 Activity 视图的根布局、View 树的起点,它继承 FrameLayout,所以也是个 ViewGroup,而我们之前对 ViewGroup 的 dispatchAttachedToWindow() 分析过了吧,在这个方法里会将 mAttachInfo 传给所有子 View。也就是说,在 Activity 首次进行 View 树的遍历绘制时,ViewRootImpl 会将自己的 mAttachInfo 通过根布局 DecorView 传递给所有的子 View 。

那么,我们就来看看 ViewRootImpl 的 mAttachInfo 什么时候初始化的吧:


在构造函数里对 mAttachInfo 进行初始化,传入了很多参数,我们关注的应该是 mHandler 这个变量,所以看看这个变量定义:


终于找到 new Handler() 的地方了,至于这个自定义的 Handler 类做了啥,我们不关心,反正通过 post() 方式执行的操作跟它自定义的东西也没有多大关系。我们关心的是在哪 new 了这个 Handler。因为每个 Handler 在 new 的时候都会绑定一个 Looper,这里 new 的时候是无参构造函数,那默认绑定的就是当前线程的 Looper,而这句 new 代码是在主线程中执行的,所以这个 Handler 绑定的也就是主线程的 Looper。至于这些的原理,就涉及到 Handler 的源码和 ThreadLocal 的原理了,就不继续跟进了,太偏了,大伙清楚结论这点就好。

这也就是为什么 View.post(Runnable) 的操作可以更新 UI 的原因,因为这些 Runnable 操作都通过 ViewRootImpl 的 mHandler 切到主线程来执行了。

这样 Q1 就搞定了,终于搞定了一个问题,不容易啊,本来以为很简单的来着。

跟到 ViewRootImpl 这里应该就可以停住了。至于 ViewRootImpl 跟 Activity 有什么关系、什么时候被实例化的、跟 DecroView 如何绑定的就不跟进了,因为我也还不是很懂,感兴趣的可以自己去看看,我在末尾会给一些参考博客。

至此,我们清楚了 mAttachInfo 的由来,也知道了 mAttachInfo.mHandler,还知道在 Activity 首次遍历 View 树进行测量、绘制时会通过 DecorView 的 dispatchAttachedToWindow() 将 ViewRootImpl 的 mAttachInfo 传递给所有子 View,并通知所有调用 View.post(Runnable) 被缓存起来的 Runnable 操作可以执行了。

但不知道大伙会不会跟我一样还有一点疑问:看网上对 ViewRootImpl.performTraversals() 的分析:遍历 View 树进行测量、布局、绘制操作的代码显然是在调用了 dispatchAttachedToWindow() 之后才执行,那这样一来是如何保证 View.post(Runnable) 的 Runnable 操作可以获取到 View 的宽高呢?明明测量的代码 performMeasure() 是在 dispatchAttachedToWindow() 后面才执行。


我在这里卡了很久,一直没想明白。我甚至以为是 PhoneWindow 在加载 layout 布局到 DecorView 时就进行了测量的操作,所以一直跟,跟到 LayoutInflater.inflate(),跟到了 ViewGroup.addView(),最后发现跟测量有关的操作最终都又绕回到 ViewRootImpl 中去了。

最后,感谢通过View.post()获取View的宽高引发的两个问题这篇博客的作者,解答了我的疑问。

原来是自己火候不够,对 Android 的消息机制还不大理解,这篇博客前前后后写了一两个礼拜,就是在不断查缺补漏,学习、理解相关的知识点。

大概的来讲,就是我们的 app 都是基于消息驱动机制来运行的,主线程的 Looper 会无限的循环,不断的从 MessageQueue 里取出 Message 来执行,当一个 Message 执行完后才会去取下一个 Message 来执行。而 Handler 则是用于将 Message 发送到 MessageQueue 里,等轮到 Message 执行时,又通过 Handler 发送到 Target 去执行,等执行完再取下一个 Message,如此循环下去。

清楚了这点后,我们再回过头来看看:

performTraversals() 会先执行 dispatchAttachedToWindow(),这时候所有子 View 通过 View.post(Runnable) 缓存起来的 Runnable 操作就都会通过 mAttachInfo.mHandler 的 post() 方法将这些 Runnable 封装到 Message 里发送到 MessageQueue 里。mHandler 我们上面也分析过了,绑定的是主线程的 Looper,所以这些 Runnable 其实都是发送到主线程的 MessageQueue 里排队,等待执行。然后 performTraversals() 继续往下工作,相继执行 performMeasure(),performLayout() 等操作。等全部执行完后,表示这个 Message 已经处理完毕,所以 Looper 才会去取下一个 Message,这时候,才有可能轮到这些 Runnable 执行。所以,这些 Runnable 操作也就肯定会在 performMeasure() 操作之后才执行,宽高也就可以获取到了。画张图,帮助理解一下:


哇,Q2的问题终于也搞定了,也不容易啊。本篇也算是结束了。

总结

分析了半天,最后我们来稍微小结一下:

  1. View.post(Runnable) 内部会自动分两种情况处理,当 View 还没 attachedToWindow 时,会先将这些 Runnable 操作缓存下来;否则就直接通过 mAttachInfo.mHandler 将这些 Runnable 操作 post 到主线程的 MessageQueue 中等待执行。

  2. 如果 View.post(Runnable) 的 Runnable 操作被缓存下来了,那么这些操作将会在 dispatchAttachedToWindow() 被回调时,通过 mAttachInfo.mHandler.post() 发送到主线程的 MessageQueue 中等待执行。

  3. mAttachInfo 是 ViewRootImpl 的成员变量,在构造函数中初始化,Activity View 树里所有的子 View 中的 mAttachInfo 都是 ViewRootImpl.mAttachInfo 的引用。

  4. mAttachInfo.mHandler 也是 ViewRootImpl 中的成员变量,在声明时就初始化了,所以这个 mHandler 绑定的是主线程的 Looper,所以 View.post() 的操作都会发送到主线程中执行,那么也就支持 UI 操作了。

  5. dispatchAttachedToWindow() 被调用的时机是在 ViewRootImol 的 performTraversals() 中,该方法会进行 View 树的测量、布局、绘制三大流程的操作。

  6. Handler 消息机制通常情况下是一个 Message 执行完后才去取下一个 Message 来执行(异步 Message 还没接触),所以 View.post(Runnable) 中的 Runnable 操作肯定会在 performMeaure() 之后才执行,所以此时可以获取到 View 的宽高。

好了,就到这里了。至于开头所提的问题,前两个已经在上面的分析过程以及总结里都解答了。而至于剩下的问题,这里就稍微提一下:

使用 View.post(),还是有可能会造成内存泄漏的,Handler 会造成内存泄漏的原因是由于内部类持有外部的引用,如果任务是延迟的,就会造成外部类无法被回收。而根据我们的分析,mAttachInfo.mHandler 只是 ViewRootImpl 一个内部类的实例,所以使用不当还是有可能会造成内存泄漏的。

参考链接

虽然只是过一下 View.post() 的源码,但真正过下去才发现,要理解清楚,还得理解 Handler 的消息机制、ViewRootImpl 的作用、ViewRootImpl 和 Activity 的关系,何时绑定等等。所以,需要学的还好多,也感谢各个前辈大神费心整理的博客,下面列一些供大伙参考:

  1. scnuxisan225#通过View.post()获取View的宽高引发的两个问题

  2. kc专栏#Activity WMS ViewRootImpl三者关系

  3. 废墟的树#从ViewRootImpl类分析View绘制的流程

  4. 凶残的程序员#Android 消息机制——你真的了解Handler?



作者:请叫我大苏
链接:https://www.jianshu.com/p/85fc4decc947
來源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

Android Studio入门——页面跳转 PS:之前因为秋招收集的二十套一二线互联网公司Android面试真题 (含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。一共十个专题,包括了Android进阶所有学习资料,Android进阶视频,Flutter,java基础,kotlin,NDK模块,计算机网络,数据结构与算法,微信小程序,面试题解析,framework源码! 阅读详情

相关推荐

Android-面试官:View-post()-为什么能够获取到-View-的宽高-?,android界面开发教程

String reason) { … // 1. 回调 onResume final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason); ··· View decor = r.window.getDecorView(); decor.setVisibility(View.INVISIBLE); ViewManager wm = a.getWindowManager(); WindowManager

m0_64603776的博客 337

Android view.post★★★★

1.view.post() 在开发过程中用到过几次view.post(),但是对它的原理不甚了解,今天就从源码看看它是怎么实现的吧。 开始源码前,先提几个问题,带着问题去看源码应该会比较有效率。 ①为什么View.post()是可以对UI进行操作的呢,即使在子线程中调用 View.post()? ②View.post()执行时,View的宽高已经计算完毕,所以经常看见在Activity的onCreate()里调用View.post()来解决获取View宽高为0的问题,为什么可以这样做呢? ③用 V

zenmela2011的专栏 3250

Android之多线程----异步消息处理机制之Handler详解

一、handler的引入: 我们都知道,Android UI是线程不安全的,如果在子线程中尝试进行UI操作,程序就有可能会崩溃。相信大家在日常的工作当中都会经常遇到这个问题,解决的方案应该也是早已烂熟于心,即创建一个Message对象,然后借助Handler发送出去,之后在Handler的handleMessage()方法中获得刚才发送的Message对象,然后在这里进行UI操作就不会再出现

码莎拉蒂 2037

onAttachToWindow() 调用

基于4.4 src: View的onAttachToWindow() 是在其dispatchAttachedToWindow(AttachInfo info, int visibility)里被无条件调用的, 而View的dispatchAttachedToWindow()有两个被调用途径: 1. ViewRootImpl 第一次 performTraversal()时会将整个view tr

fyfcauc的专栏 7812

LangChain + PlayWright实战:5分钟搞定网页自动化测试(附完整代码)

本文详细介绍了如何利用LangChain与PlayWright构建智能网页自动化测试系统。通过环境配置、核心架构设计、实战Agent构建及性能优化等步骤,开发者可以快速实现高效的Web操作自动化,提升测试效率。文章还提供了完整代码示例和典型问题解决方案。

weixin_27791839的博客 239

【硬件设计】_基于STM32单片机的智能台灯系统

智能台灯系统是一款多功能的智能照明设备,能够自动调节亮度、监测人体活动、感知环境光照,并具备定时和报警功能,极大提升了用户体验与功能的灵活性,主要包括以下功能:系统提供自动和手动两种模式以调节台灯亮度。在自动模式下,台灯会根据周围光强智能调整;而在手动模式下,用户可通过按键自行设置亮度。利用红外光电开关检测台灯与用户之间的距离,若距离过近,系统将发出报警提示。通过时钟模块设置定时提醒,并在OLED显示屏上显示当前时间,当达到设定的定时时间时,蜂鸣器将发出报警音。

2401_85381181的博客 1731

Android异步消息处理机制 全解析

本文已授权微信公众号“Android技术杂货铺”发布。 Android异步消息处理机制的重要性不言而喻: 中高级工程师面试必问、阅读源码基础。 本文将通过结合源码的形式,全面解析Handler和MessageQueue、Looper的关系.并分析Android异步消息机制的相关原理.

微不足道的博客 2190

深入解析BlueZ5与D-Bus:Linux蓝牙开发实战指南

本文深入解析BlueZ5与D-Bus在Linux蓝牙开发中的核心应用,提供从环境搭建到实战开发的完整指南。通过详细代码示例和调试技巧,帮助开发者掌握蓝牙协议栈与D-Bus接口的交互方法,实现高效蓝牙设备管理与GATT服务开发。

weixin_28292761的博客 450

异步消息处理机制

Message.线程间传递消息 Handle.发送和处理消息,利用sendMessage()发送消息,handleMessage()处理消息 MessageQueue.消息队列,用于存放所有message,每个线程只有以一个MessageQueue对象 Looper.是MessageQueue的管家,发现MessageQueue有消息时将它取出 public class MainActivit...

HughZhao的博客 2115

医疗信创国产化替代目前面临哪些难题?如何利用国产虚拟化技术促进医院信创建设

业务系统众多,存在大量信创改造模糊地带,既有老系统,也有新系统,故障点多,管理难度高,IT人力资源不足,其对硬件的支撑能力也提出了越来越高的要求,由此造成硬件成本、管理成本以及能耗成本都非常高。

云宏winhong的博客 2657

USB_TYPE-C 母座电路设计

本文详细介绍了USB Type-C接口(C2765186)的电路设计要点,重点解析了电源、识别和数据三部分的关键连接方案。电源部分需并联VBUS和GND引脚并加宽走线以承载3A电流;识别部分必须为CC1/CC2引脚配置5.1kΩ±1%精度的下拉电阻,这是触发PD充电的核心条件;数据部分通过短接正反插引脚实现盲插功能。文档还提供了完整的引脚对照表,并强调PCB布局需注意ESD防护、差分走线阻抗控制等要点,为Type-C接口设计提供了实用指导。

在读研究生,FPGA电路设计与功能开发。 584

Handler.post 与 View.post 区别

Android 两个线程 UI线程和非UI线程。而Handler是非UI线程向UI线程传递消息的桥梁。 相同 在与UI线程的通信上,Handler与View,其实最终都做了同样的事情。就是将消息传递在UI线程 的消息队列里,执行一些处理操作。 不同 View.post方法想在非UI线程有效工作。如该方法的注释所说,必须保证该View已经被添加至窗口。 View.post()获取View宽高 public boolean post(Runnable action) { fin.

xiaokakajk的博客 1136

基于YOLOv8的表面裂痕等缺陷检测:深度学习与UI界面的实践

本文详细介绍了如何使用YOLOv8进行表面裂痕等缺陷检测,并结合图形用户界面展示检测结果。通过YOLOv8的强大能力,我们可以实现高效、准确的表面缺陷检测,提升工业产品的质量控制水平。未来,随着深度学习技术和数据集的不断发展,表面缺陷检测的精度和速度将不断提升,应用领域也将进一步拓展。希望本文的实践能够为深度学习在工业检测领域的应用提供一些启示。

YOLO 550

Android异步消息处理:Handler机制详解与实践

在Android开发中,多线程通信是保证应用流畅性的关键技术。UI线程负责界面渲染,而耗时操作必须放在后台线程执行,这就需要通过消息机制实现线程间通信。Handler作为Android异步消息处理的核心组件,配合Message、MessageQueue和Looper构成了完整的工作流程。这种机制不仅解决了UI线程安全问题,还广泛应用于网络请求回调、定时任务等场景。通过消息复用、内存泄漏防护等最佳实践,开发者可以构建更高效的异步处理方案。随着技术演进,虽然出现了RxJava、Coroutine等现代方案,但理

weixin_30466039的博客 385

MobSF-v3源代码分析(一)

移动安全框架(MobSF)是一种自动化的移动应用程序(Android/iOS/Windows)测试框架,能够执行静态、动态和恶意软件分析。 它可用于Android、iOS和Windows移动应用程序的有效和快速安全分析,并支持二进制文件(APK,IPA和APPX)和压缩源代码。 MobSF可以在运行时为Android应用程序进行动态应用程序测试,并具有由CapFuzz(一种特定于Web API的安全扫描程序)提供支持的Web API模糊测试。MobSF旨在使您的CI/CD或DevSecOps管道集成无缝。

武天旭的博客 8960

CesiumJS【Basic】- #002 加载在线瓦片地图数据

cesiumjs 加载各类在线地图数据

AlanL的专栏 952

Android Handler机制:异步消息处理与UI线程更新

在Android开发中,异步消息处理是解决耗时操作与UI更新矛盾的核心机制。其基本原理是通过Handler、Looper和MessageQueue构建跨线程通信桥梁,实现子线程任务完成后安全通知主线程更新界面。这种机制既避免了直接在UI线程执行耗时操作导致的界面卡顿,又防止了子线程直接操作UI引发的崩溃问题。从技术价值看,Handler机制提供了精细的消息优先级控制和时序管理能力,适用于网络请求回调、定时任务、跨进程通信等场景。特别是在电商倒计时、图片分块处理等需要精确控制消息顺序的场景中表现优异。结合消息

weixin_30471561的博客 407

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

异步消息处理机制:Android线程通信核心解析

异步消息处理是现代软件开发中实现线程通信的基础架构,其核心思想是通过消息队列实现线程间的解耦通信。该机制基于生产者-消费者模式,包含Message、MessageQueue、Looper和Handler四个关键组件。在Android系统中,这种架构不仅实现了工作线程与UI线程的安全交互,还支撑了系统级的事件处理。通过消息池复用和epoll阻塞等待等优化,系统在高并发场景下仍能保持高效运行。典型应用包括UI更新、延迟任务调度和跨进程通信等场景,是移动开发中解决线程同步问题的首选方案。理解消息处理机制的工作原理

weixin_33858336的博客 302

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

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

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

Android多线程----异步消息处理机制之Handler

虽然是国庆佳节,但也不能停止学习的脚步,我选择在教研室为祖国母亲默默地庆生。   关于Android的多线程知识,请参考本人之前的一篇博客:Android 多线程----AsyncTask异步任务详解   在Android当中,提供了异步消息处理机制的两种方式来解决线程之间的通信问题,一种是今天要讲的Handler的机制,还有一种就是之前讲过的 AsyncTask 机制。   一、ha

xyjikl 575

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

Android异步消息阻塞,Android 异步消息处理机制

概述Handler无疑是Android的异步消息处理机制的核心,这遍文章将通过分析Handler的源码来系统的解析Android异步消息机制。Handler的使用public class MainActivity extends AppCompatActivity {private static final String TAG = "MainActivity";private Handler m...

weixin_30212109的博客 390

Android异步消息处理与UI线程更新机制详解

在移动开发中,线程安全与异步处理是保证应用流畅性的关键技术。Android通过独特的消息循环机制实现线程间通信,其核心原理基于生产者-消费者模式。Handler作为消息处理中枢,配合MessageQueue和Looper构成完整的事件驱动架构,这种设计既解决了UI线程安全问题,又提升了系统资源利用率。在电商、社交等高频交互场景中,合理的消息优先级管理能显著优化页面响应速度。随着Kotlin协程的普及,虽然现代开发更推荐使用协程处理异步任务,但理解底层Handler机制仍是性能调优和复杂问题排查的基础。特别是

weixin_34015336的博客 419

onCreate()、onResume() 中可以获取View的宽高吗?怎么做? View.post{} 为什么可以获取?

回答: View的宽高是在onLayout阶段才能最终确定的,而在Activity#onCreate中并不能保证View已经执行到了onLayout方法,也就是说Activity的声明周期与View的绘制流程并不是一一绑定。所以onCreate() 和 onResume() 中获取不到View的宽高值。以Handler为基础,View.post() 将传入任务的执行时机调整到View 绘制完成之后。 代码验证: ...

qq_40714317的博客 1384

Android view的post的作用

一、官方源码解读 public boolean post(Runnable action) { final AttachInfo attachInfo = mAttachInfo; if (attachInfo != null) { return attachInfo.mHandler.post(action); } // Assume that post will succeed later ViewRootImpl.getRunQueu

分享Android开发知识 681

Android Handler中的handleMessage方法和post方法之源码剖析 及UI更新方法

我们都知道,在子线程中进行UI操作(更新UI控件)包括以下四种方法: 1.Handler的handlerMessage()方法。 2.Handler的post()方法。 3.View的post()方法。 4.Activity的runOnUiThread()方法。 本文重点分析前两种方法,后面两种稍微说一下。在说第一个方法之前,让我们先来看张图片(图片来源于http://my.oschin

郭风朴(guofengpu)的Android影音专栏 1626

以下未发布-Android的多线程以及异步消息处理机制,android移动开发基础案例教程源码

1.Message: Message 是在线程之间传递的消息,它可以在内部携带少量的信息,用于在不同线程之间进行数据交换。除了 what 字段,还可以使用 arg1 和 arg2 来携带整型数据,使用 obj 来携带 Object 数据。 2、Handler: Handler主要用于发送和处理消息的。发送消息一般是使用Handler的sendMessage()方法,而发出的消息经过一系列的处理后,最终会传递到Handler的handleMessage()方法中。 3、MessageQueue: Messag

m0_66265052的博客 345

Android基础知识巩固系列 Android多线程编程(Handler机制或异步消息处理机制)

参考网站:https://www.jianshu.com/p/9fe944ee02f7 参考书籍:《第一行代码》 背景(摘自《第一行代码》): 和许多其他的GUI库一样,Android的UI也是线程不安全的。也就是说,如果想要更新应用程序里的UI元素,则必须在主线程中进行,否则就会出现异常。 一、Handler机制的...

Peter__Li的博客 466
上一篇: Android消息机制原理解析
Ray534
博客等级 码龄12年 2粉丝 2原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值