当前位置: 首页 > news >正文

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的宽高

相关文章:

  • tinyrenderer笔记(Shader)
  • C语言数组和函数的实践———扫雷游戏
  • APP自动化测试(一)
  • 9-4 USART串口数据包
  • [HOT 100] 1377. T 秒后青蛙的位置
  • 在若依里创建新菜单
  • uniapp开发11-v-for动态渲染list列表数据
  • Beetle 树莓派RP2350 - 桌面时钟摆件
  • 探索Hello Robot开源移动操作机器人Stretch 3的技术亮点与市场定位
  • Banana Pi BPI-CM6 是一款八核 RISC-V 模块,兼容 Raspberry Pi CM 载板
  • POI创建Excel文件
  • Android setContentView()源码分析
  • [学习]RTKLib详解:rtkcmn.c与rtkpos.c
  • Java实现堆排序算法
  • 【省电级子印章系统建设方案】
  • 一款独立于游戏外的键盘源按键辅助工具他来了
  • 2025年AI视觉革命:Dual-Stream Diffusion如何让PS下岗?
  • 交换机 MUX VLAN
  • 雨云游戏云MCSM面板服使用教程我的世界Forge服务端开服教程
  • 通话篇:FreeBuds 6的正确打开方式!
  • 马上评|不再提“智驾”,新能源车企回归理性
  • 深圳一购房者交首付后迟迟无法签合同,澎湃介入后开发商承诺退款
  • “穿越看洪武”,明太祖及其皇后像台北故宫博物院南院展出
  • “子宫肌瘤男性病例”论文后:“宫颈癌、高危产妇”论文也现男性病例,作者称“打错了”
  • 解锁川北底色密码,“文化三地”志愿宣讲员招募计划启动报名
  • 莫斯科一机场实施临时限制措施