Android GUI系统-ViewTree的遍历(四)

本文探讨了ViewTree遍历的触发场景,包括应用程序启动、手动请求布局更新、设置View参数、界面重绘及应用可见性变化等情况。深入剖析了ViewRootImpl与View在遍历过程中的关键作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

那些情况会引起ViewTree的遍历

1)应用程序刚启动时,会在构造出整棵ViewTree后,执行第一次遍历。

public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) 	@ViewRootImpl.java{
	requestLayout();
}
	

public void requestLayout() @ViewRootImpl.java{
	if (!mHandlingLayoutInLayoutRequest) {
//检查是否是主线程。
		CheckThread();
//表明当前正在发起layout请求。
		mLayoutRequested = true;
//安排一个遍历。
		scheduleTraversals();
	}
}

2requestLayout()@View.javaview对象可以通过调用requestLayout来主动请求遍历。

//当有些什么变化时调用整个方法来刷新view的布局。这会安排一次viewtreelayout传递,但是如果view层级正在执行layout传递不应该调用requestLayout,如果layout正在执行,那么这次请求可能会被放置到当前layout传递的尾部。子类应该重写这个方法。

public void requestLayout() @View.java{
	if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == null) {
		ViewRootImpl viewRoot = getViewRootImpl();
//isInLayout()判断当前是否正在进行layout,如果当前正在执行layout,又强制调用了requestLayout ,那么把这个view放在mLayoutRequesters的尾部。
		if (viewRoot != null && viewRoot.isInLayout()) {
			if (!viewRoot.requestLayoutDuringLayout(this)) {
				return;
			}
		}
		mAttachInfo.mViewRequestingLayout = this;
	}

//PFLAG_FORCE_LAYOUT,说明是程序主动请求的layout。
	mPrivateFlags |= PFLAG_FORCE_LAYOUT;
	mPrivateFlags |= PFLAG_INVALIDATED;
//把layout请求提交给它的父类,它的父类可能是ViewGroup,也可能是ViewRoot,最后都要提交到ViewRoot处理,ViewRootImpl中的 requestLayout跟前面提到的应用程序的第一次遍历调用的是同一个函数。
	if (mParent != null && !mParent.isLayoutRequested()) {
		mParent. requestLayout();
	}
}


3setLayoutParams,设置View对象的各种属性。


public void setLayoutParams(ViewGroup.LayoutParams params) @View.java{
//如果父类是ViewGroup,调用 onSetLayoutParams做参数调整, onSetLayoutParams通常是被ViewGroup的子类重写的。
	if (mParent instanceof ViewGroup) {
		((ViewGroup) mParent).onSetLayoutParams(this, params);
	}
//这个调用跟前一个情况一样。
	requestLayout();
}


4invalidate,当前的UI无效了,所以需要重绘。这个方法有多个重载,最后的实现是invalidateInternalinvalidate只能在UI线程调用,其他线程调用postInvalidate()

void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache,boolean 	fullInvalidate)@View.java {
//如果view不可见,并且也没有动画在执行,就跳过这次刷新。
	if (skipInvalidate()) {
		return;
	}
//满足以下条件,才可能执行invalidate。
	if ((mPrivateFlags & (PFLAG_DRAWN | PFLAG_HAS_BOUNDS)) == 
		(PFLAG_DRAWN | PFLAG_HAS_BOUNDS)
		|| (invalidateCache && (mPrivateFlags & PFLAG_DRAWING_CACHE_VALID) == 				PFLAG_DRAWING_CACHE_VALID)
		|| (mPrivateFlags & PFLAG_INVALIDATED) != PFLAG_INVALIDATED
		|| (fullInvalidate && isOpaque() != mLastIsOpaque)) {
//fullInvalidate是否全面刷新,如果没有明确标记刷新的范围(调用的是有rect参数、或者有r,t,l,b参数的invalidate()方法)
		if (fullInvalidate) {
			mLastIsOpaque = isOpaque();
			mPrivateFlags &= ~PFLAG_DRAWN;
		}

		mPrivateFlags |= PFLAG_DIRTY;

//在不全部刷新的情况下,将需要刷新的区域传递到它的父View。
		final AttachInfo ai = mAttachInfo;
		final ViewParent p = mParent;
		if (p != null && ai != null && l < r && t < b) {
			final Rect damage = ai.mTmpInvalRect;
			damage.set(l, t, r, b);
			p.invalidateChild(this, damage);
		}
	}
}

在调用invalidateChild往上传递时,ViewParent可能是ViewGroup,也可能是ViewRootImpl,但是最终还是要由ViewRootImpl处理,ViewGroupinvalidateChild的处理是从当前点开始,沿着ViewTree回溯收集dirty区域,ViewRootImplinvalidateChild的处理是从ViewTree的根开始,发起ViewTree的遍历。


5setAppVisibility,当应用程序的可见性发生了变化,会调用WMS的这个函数,然后进一步通过WindowState的变量mClientViewRootImplW类型的对象)调用到ViewRootImpldispatchAppVisibilityViewRootImpl收到可见性变化的消息,也会通过scheduleTraversals发起一次遍历。


scheduleTraversalsViewRootImpl中遍历的起点。


void scheduleTraversals() @ViewRootImpl.java{
//当前是不是正在执行遍历。
	if (!mTraversalScheduled) {
		mTraversalScheduled = true;
//向 Choreographer注册了 CALLBACK_TRAVERSAL类型的会调用,一旦有Vsync信号到来,会回调 mTraversalRunnable中的run方法,Vsync是准备UI数据的触发源。
		mChoreographer.postCallback(
			Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
	}
}

TraversalRunnable的run方法,直接调用doTraversal。
void doTraversal()@ViewRootImpl.java {
	if (mTraversalScheduled) {
//把 mTraversalScheduled复位,然后执行遍历 performTraversals。
		mTraversalScheduled = false;
		performTraversals();
	}
}


接上一节的分析,应用主线程中在执行Activity的Resume流程的最后,会创建ViewRootImpl对象并调用其setView函数,从此并开启了应用界面UI布局与绘制的流程。在开始讲解这个过程之前,我们先来整理一下前面代码中讲到的这些概念,如Activity、PhoneWindow、DecorView、ViewRootImpl、WindowManager它们之间的关系与职责,因为这些核心类基本构成了Android系统GUI显示系统在应用进程侧的核心架构,其整体架构如下图所示: Window是一个抽象类,通过控制DecorView提供了一些标准的UI方案,比如背景、标题、虚拟按键等,而PhoneWindow是Window的唯一实现类,在Activity创建后的attach流程中创建,应用启动显示的内容装载到其内部的mDecor(DecorView); DecorView是整个界面布局View控件树的根节点,通过它可以遍历访问到整个View控件树上的任意节点; WindowManager是一个接口,继承自ViewManager接口,提供了View的基本操作方法;WindowManagerImp实现了WindowManager接口,内部通过组合方式持有WindowManagerGlobal,用来操作View;WindowManagerGlobal是一个全局单例,内部可以通过ViewRootImpl将View添加至窗口中; ViewRootImpl是所有View的Parent,用来总体管理View的绘制以及与系统WMS窗口管理服务的IPC交互从而实现窗口的开辟;ViewRootImpl是应用进程运转的发动机,可以看到ViewRootImpl内部包含mView(就是DecorView)、mSurface、Choregrapher,mView代表整个控件树,mSurfacce代表画布,应用的UI渲染会直接放到mSurface中,Choregorapher使得应用请求vsync信号,接收信号后开始渲染流程; 讲一下这几个之间的关系
最新发布
03-15
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值