Android View#post()源码分析

文章目录

  • Android View#post()源码分析
    • 概述
    • onCreate和onResume不能获取View的宽高
    • post可以获取View的宽高
    • 总结

Android View#post()源码分析

概述

在 Activity 中,在 onCreate() 和 onResume() 中是无法获取 View 的宽高,可以通过 View#post() 获取 View 的宽高。

onCreate和onResume不能获取View的宽高

Activity 的生命周期都是在 ActivityThread 中,当调用 startActivity() ,最终会调用 ActivityThread#performLaunchActivity()。

// ActivityThread#performLaunchActivity()
// 核心代码:
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {Activity activity = null;java.lang.ClassLoader cl = appContext.getClassLoader();// 通过反射新建一个Activityactivity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);try {if (activity != null) {// 创建并初始化Windowactivity.attach(appContext, this, getInstrumentation(), r.token,r.ident, app, r.intent, r.activityInfo, title, r.parent,r.embeddedID, r.lastNonConfigurationInstances, config,r.referrer, r.voiceInteractor, window, r.activityConfigCallback,r.assistToken, r.shareableActivityToken);r.activity = activity;if (r.isPersistable()) {// 回调Activity#onCreate()mInstrumentation.callActivityOnCreate(activity, r.state, r.persistentState);} else {mInstrumentation.callActivityOnCreate(activity, r.state);}            } }return activity;
}
// ActivityThread#handleResumeActivity()
// 核心代码:
public void handleResumeActivity()(ActivityClientRecord r, boolean finalStateRequest,boolean isForward, String reason) {// 最终会回调Activity#onResume()if (!performResumeActivity(r, finalStateRequest, reason)) {return;}final Activity a = r.activity;if (r.window == null && !a.mFinished && willBeVisible) {r.window = r.activity.getWindow();View decor = r.window.getDecorView();decor.setVisibility(View.INVISIBLE); // 设置不可见ViewManager wm = a.getWindowManager();WindowManager.LayoutParams l = r.window.getAttributes();a.mDecor = decor;l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;l.softInputMode |= forwardBit;if (r.mPreserveWindow) {a.mWindowAdded = true;r.mPreserveWindow = false;ViewRootImpl impl = decor.getViewRootImpl();if (impl != null) {impl.notifyChildRebuilt();}}if (a.mVisibleFromClient) {if (!a.mWindowAdded) {a.mWindowAdded = true;// 添加View,开始View的操作wm.addView(decor, l);} else { a.onWindowAttributesChanged(l);}} }   
}

说明:Activity 先执行 onCreate(),再执行 onResume(),最后才调用 wm.addView() 将 DecorView 添加到视图中,也就是从这里才开始执行 View 测量布局绘制流程。简单说 View 的流程晚于 onResume()。

post可以获取View的宽高

// View#post()
public boolean post(Runnable action) {final AttachInfo attachInfo = mAttachInfo;if (attachInfo != null) {// 如果attachInfo不为null,表示View已经添加到Window,直接通过Handler发送到主线程队列return attachInfo.mHandler.post(action);}// 如果attachInfo为null,表示View未添加到Window,暂存在mRunQueue中getRunQueue().post(action);return true;
}private HandlerActionQueue getRunQueue() {if (mRunQueue == null) {mRunQueue = new HandlerActionQueue();}return mRunQueue;
}

说明:在 onCreate() 和 onResume() 中调用 View#post(),最终都会调用 getRunQueue().post(action)。

wm.addView(decor, l) 执行流程:

  • 调用 WindowManagerImpl#addView()。
  • 调用 WindowManagerGlobal#addView()。
  • 执行 new ViewRootImpl,创建 root 对象(布局管理器),并在内部创建 mAttachInfo 对象、Handler 对象。
  • 调用 ViewRootImpl#setView()。
// ViewRootImpl#setView()
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView, int userId) {synchronized (this) {if (mView == null) {mView = view;// 请求布局,最终调用ViewRootImpl#performTraversals()requestLayout();           try {// 通过Binder调用WMS添加Windowres = mWindowSession.addToDisplayAsUser(mWindow, mWindowAttributes,getHostVisibility(), mDisplay.getDisplayId(), userId,mInsetsController.getRequestedVisibilities(), inputChannel, mTempInsets,mTempControls);               }   }}
}
// ViewRootImpl#performTraversals()
private void performTraversals() {final View host = mView;// 调用View#dispatchAttachedToWindow()分发mAttachInfo    host.dispatchAttachedToWindow(mAttachInfo, 0);// 测量流程performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);// 布局流程performLayout(lp, mWidth, mHeight);// 绘制流程performDraw()
}
// View#dispatchAttachedToWindow()
void dispatchAttachedToWindow(AttachInfo info, int visibility) {mAttachInfo = info;// 执行缓存任务if (mRunQueue != null) {mRunQueue.executeActions(info.mHandler);mRunQueue = null;}  
}
// HandlerActionQueue#executeActions()
public class HandlerActionQueue {private HandlerAction[] mActions;public void executeActions(Handler handler) {synchronized (this) {final HandlerAction[] actions = mActions;for (int i = 0, count = mCount; i < count; i++) {final HandlerAction handlerAction = actions[i];handler.postDelayed(handlerAction.action, handlerAction.delay);}mActions = null;mCount = 0;}}
}

说明:执行 mRunQueue.executeActions(),会将所有缓存的任务发送到 handler 中,等待主线程执行完 performTraversals() 方法后,就会执行 mActions 中的任务,这时就可以获取到 View 的宽高。

总结

执行流程:

  • Activity#onCreate()
  • Activity#onResume()
  • WindowManagerImpl#addView()
  • new ViewRootImpl()
  • ViewRootImpl#setView()
  • View的测量流程
  • View的布局流程
  • View的绘制流程
  • WMS添加Window
  • 获取View的宽高

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

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

相关文章

SecureCrt设置显示区域横列数

1. Logical rows //逻辑行调显示区域高度的 一般超过50就全屏了 2. Logical columns //逻辑列调显示区域宽度的 3. Scrollback buffer //缓冲区大小

最短路径-Dijkstra算法板子(java)

自己把Dijkstra的板子整理了一下&#xff0c;也方便自己后续做题&#xff0c;在此做个记录。 Dijkstra基本上都会需要这些变量&#xff1a; dist[]&#xff1a;记录各个节点到起始节点的最短权值 path[]&#xff1a;记录各个节点的上一个节点(用来联系该节点到起始节点的路径)…

PostgreSQL数据库的array类型

PostgreSQL数据库相比其它数据库&#xff0c;有很多独有的字段类型。 比如array类型&#xff0c;以下表的pay_by_quarter与schedule两个字段便是array类型&#xff0c;即数组类型。 CREATE TABLE sal_emp (name text,pay_by_quarter integer[],schedule t…

centos的根目录占了大量空间怎么办

问题 当根目录磁盘不够时&#xff0c;就必须删除无用的文件了 上面的&#xff0c;如果删除/usr 或/var是可以释放出系统盘的 定位占空间大的文件 经过命令&#xff0c;一层层查哪些是占磁盘的。 du -sh /* | sort -rh | head -n 10 最终排查&#xff0c;是有个系统日志占了20…

PostgreSQL存储过程“多态“实现:同一方法名支持不同参数

引言 在传统编程语言中&#xff0c;方法重载&#xff08;同一方法名不同参数&#xff09;是实现多态的重要手段。但当我们将目光转向PostgreSQL数据库时&#xff0c;是否也能在存储过程&#xff08;函数&#xff09;中实现类似的功能&#xff1f;本文将深入探讨PostgreSQL中如…

快速学会Linux的WEB服务

一.用户常用关于WEB的信息 什么是WWW www是world wide web的缩写&#xff0c;及万维网&#xff0c;也就是全球信息广播的意思 通常说的上网就是使用www来查询用户所需要的信息。 www可以结合文字、图形、影像以及声音等多媒体&#xff0c;超链接的方式将信息以Internet传递到世…

Windows玩游戏的时候,一按字符键就显示桌面

最近打赛伯朋克 2077 的时候&#xff0c;不小心按错键了&#xff0c;导致一按字符键就显示桌面。如下&#xff1a; 一开始我以为是输入法的问题&#xff08;相信打游戏的人都知道输入法和奔跑键冲突的时候有多烦&#xff09;&#xff0c;但是后来解决半天发现并不是。在网上搜…

【测试开发】概念篇 - 从理解需求到认识常见开发、测试模型

&#x1f4e2;博客主页&#xff1a;https://blog.csdn.net/2301_779549673 &#x1f4e2;博客仓库&#xff1a;https://gitee.com/JohnKingW/linux_test/tree/master/lesson &#x1f4e2;欢迎点赞 &#x1f44d; 收藏 ⭐留言 &#x1f4dd; 如有错误敬请指正&#xff01; &…

核函数(Kernel function)

核函数 核函数在GPU上进行并行执行 注意: 限定词__global__修饰 [双下划线]返回值必须是void 形式: _global_ void kernel_function( argument arg){ ​ printf(“hello world from the GPU\n”); } void __global__kernel_function( argument arg){ ​ printf(“hello worl…

数据结构与算法:区间dp

前言 区间dp也是动态规划里很重要的一部分。 一、内容 区间dp的根本原理就是把大范围问题分解成若干小范围的问题去求解。一般来讲,常见的用法有对于两侧端点去展开可能性和对于范围上的划分点去展开可能性。 二、题目 1.让字符串成为回文串的最少插入次数 class Soluti…

AI Agent 入门指南:从 LLM 到智能体

AI. AI. AI. 最近耳朵里是不是总是被这些词轰炸&#xff1f;特别是“Agent”、“AI Agent”、“智能体”、“Agentic”…… 感觉一夜之间&#xff0c;AI 就从我们熟悉的聊天框里蹦出来&#xff0c;要拥有“独立思考”和“自主行动”的能力了&#xff1f; 说实话&#xff0c;一…

开启docker中mysql的binlog日志

1.登陆docker服务器,输入docker ps查看服务: 2.进入mysql服务 进入到mysql的服务容器后,输入mysql -u*** -p***登陆 mysql 客户端查看是否开启binlog 输入 : show variables like log_bin; 3.输入quit退出mysql客户端 4.之后在docker的mysql服务容器里查询mysql的配置文件所在…

Kotlin 中 List 和 MutableList 的区别

在 Kotlin 中&#xff0c;List 和 MutableList 是两种不同的集合接口&#xff0c;核心区别在于可变性。 Kotlin 集合框架的重要设计原则&#xff1a;通过接口分离只读&#xff08;read - only&#xff09;和可变&#xff08;mutable&#xff09;操作&#xff0c;以提高代码的安…

【能力比对】K8S数据平台VS数据平台

&#x1f525;&#x1f525; AllData大数据产品是可定义数据中台&#xff0c;以数据平台为底座&#xff0c;以数据中台为桥梁&#xff0c;以机器学习平台为中层框架&#xff0c;以大模型应用为上游产品&#xff0c;提供全链路数字化解决方案。 ✨AllData数据中台官方平台&…

Fastjson 从多层级的JSON数据中获取特定字段的值

使用 Fastjson 的 JSONPath.eval 可以通过 JSONPath 表达式直接定位多层级 JSON 中的目标字段&#xff0c;避免逐层调用 getJSONObject() 的繁琐操作。以下是具体实现方法和示例&#xff1a; 核心思路 通过 JSONPath.eval 方法&#xff0c;传入 JSON 对象&#xff08;或 JSON…

端口安全基本配置

1.top图 2.交换机配置 交换机swa <SWA> system-view [SWA] vlan batch 10 20[SWA] interface GigabitEthernet0/0/1 [SWA-GigabitEthernet0/0/1] port link-type trunk [SWA-GigabitEthernet0/0/1] port trunk allow-pass vlan 10[SWA] interface GigabitEthernet0/0/2 …

hadoop集群建立

建立Hadoop集群的步骤指南 建立Hadoop集群需要系统规划和多个步骤的配置。以下是详细的建立流程&#xff1a; 一、前期准备 硬件需求 多台服务器(至少3台&#xff0c;1主2从) 每台建议配置&#xff1a;至少4核CPU&#xff0c;8GB内存&#xff0c;100GB硬盘 稳定的网络连接(…

从零开始学java--集合类(2)

集合类 目录 集合类 Queue 队列的使用&#xff1a; 双端队列&#xff08;Deque&#xff09; Map和Set 概念&#xff1a; 模型&#xff1a; Map 常见方法说明&#xff1a; 注意&#xff1a; TreeMap和HashMap的区别&#xff1a; Set 常见方法说明&#xff1a; 注…

【HarmonyOS 5】鸿蒙发展历程

【HarmonyOS 5】鸿蒙发展历程 一、鸿蒙 HarmonyOS 版本年代记 鸿蒙 1.0&#xff1a; 2019 年 8 月 9 日&#xff0c;华为在开发者大会上正式发布鸿蒙 1.0 系统&#xff0c;这一版本首次应用于华为荣耀智慧屏产品中&#xff0c;标志着华为正式进军操作系统领域。该版本初步展现…

SpringBoot教学管理平台源码设计开发

概述 基于SpringBoot框架开发的​​教学管理平台​​完整项目&#xff0c;帮助开发者快速搭建在线教育平台。该系统包含学生端、教师端和管理后台&#xff0c;实现了课程管理、随堂测试、作业提交等核心功能&#xff0c;是学习SpringBoot开发的优质案例。 主要内容 1. 系统架…