requestLayout()
方法中会循环递归调用 mParent.requestLayout()
方法,直到找到ViewRootImpl
中的requestLayout()
方法,而它的方法做了线程检测如下图:这就是requestLayout()
方法会崩溃的原因。
验证猜想: 为TextView
设置跑马灯属性,使上面的if (mEllipsize != TextUtils.TruncateAt.MARQUEE)
不成立,走下面的requestLayout()
方法,代码如下:
override fun onResume() {
super.onResume()
mBind.btTest.ellipsize = TextUtils.TruncateAt.valueOf(“MARQUEE”)
mBind.btTest.setOnClickListener{
lifecycleScope.launch(Dispatchers.IO) {
mBind.btTest.text = “子线程点击改变:${Thread.currentThread().name}”
}
}
}
果然点击后崩溃:
2.3、继续分析invalidate()
方法,为什么不会导致textview的更新崩溃
看代码在View.java
文件中
public void invalidate() {
invalidate(true);
}
public void invalidate(boolean invalidateCache) {
invalidateInternal(0, 0, mRight - mLeft, mBottom - mTop, invalidateCache, true);
}
void invalidateInternal(int l, int t, int r, int b, boolean invalidateCache,
boolean fullInvalidate) {
if (mGhostView != null) {
mGhostView.invalidate(true);
return;
}
if (skipInvalidate()) {
return;
}
// Reset content capture caches
mPrivateFlags4 &= ~PFLAG4_CONTENT_CAPTURE_IMPORTANCE_MASK;
mContentCaptureSessionCached = false;
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)) {
if (fullInvalidate) {
mLastIsOpaque = isOpaque();
mPrivateFlags &= ~PFLAG_DRAWN;
}
mPrivateFlags |= PFLAG_DIRTY;
if (invalidateCache) {
mPrivateFlags |= PFLAG_INVALIDATED;
mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
}
// Propagate the damage rectangle to the parent 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);
}
// Damage the entire projection receiver, if necessary.
if (mBackground != null && mBackground.isProjected()) {
final View receiver = getProjectionReceiver();
if (receiver != null) {
receiver.damageInParent();
}
}
}
}
核心代码是上面第三段invalidateInternal
方法中的invalidateChild
方法
它回调到ViewGroup
中的invalidateChild
方法
看:invalidateChild
如下图:我们知道
if (attachInfo != null && attachInfo.mHardwareAccelerated)
条件成立attachInfo不为空 ,且硬件加速被开启(从API 14 (3.0)起。硬件加速默认开启)。 attachInfo
是一个view在attach至其父window被赋值的一系列信息。
所以条件成立后走的onDescendantInvalidated
方法 如下:
@CallSuper
public void onDescendantInvalidated(@NonNull View child, @NonNull View target) {
/*
- HW-only, Rect-ignoring damage codepath
- We don’t deal with rectangles here, since RenderThread native code computes damage for
- everything drawn by HWUI (and SW layer / drawing cache doesn’t keep track of damage area)
*/
// if set, combine the animation flag into the parent
mPrivateFlags |= (target.mPrivateFlags & PFLAG_DRAW_ANIMATION);
if ((target.mPrivateFlags & ~PFLAG_DIRTY_MASK) != 0) {
// We lazily use PFLAG_DIRTY, since computing opaque isn’t worth the potential
// optimization in provides in a DisplayList world.
mPrivateFlags = (mPrivateFlags & ~PFLAG_DIRTY_MASK) | PFLAG_DIRTY;
// simplified invalidateChildInParent behavior: clear cache validity to be safe…
mPrivateFlags &= ~PFLAG_DRAWING_CACHE_VALID;
}
// … and mark inval if in software layer that needs to repaint (hw handled in native)
if (mLayerType == LAYER_TYPE_SOFTWARE) {
// Layered parents should be invalidated. Escalate to a full invalidate (and note that
// we do this after consuming any relevant flags from the originating descendant)
mPrivateFlags |= PFLAG_INVALIDATED | PFLAG_DIRTY;
target = this;
}
if (mParent != null) {
mParent.onDescendantInvalidated(this, target);
}
}
上面一段代码核心是 mParent.onDescendantInvalidated(this, target);
类似于requestLayout()
方法 onDescendantInvalidated
中会循环递归调用 mParent.onDescendantInvalidated(this, target);
方法,直到找到ViewRootImpl
中的onDescendantInvalidated(this, target)
方法,而它的方法没做线程检测如下图:这就是开了硬件加速后invalidate
方法不会崩溃的原因。如下图:直接走scheduleTraversals绘制刷新
有兴趣可看:
而关闭硬件加速后会怎样呢? 继续看invalidateChild
方法
@Deprecated
@Override
public final void invalidateChild(View child, final Rect dirty) {
final AttachInfo attachInfo = mAttachInfo;
if (attachInfo != null && attachInfo.mHardwareAccelerated) {
// HW accelerated fast path
onDescendantInvalidated(child, child);
return;
}
ViewParent parent = this;
if (attachInfo != null) {
…
do {
…
parent = parent.invalidateChildInParent(location, dirty);
…
} while (parent != null);
}
}
上面一段核心是 parent = parent.invalidateChildInParent(location, dirty);
方法 同理while循环不停调用 invalidateChildInParent
方法直到找到ViewRootImpl
中的invalidateChildInParent(int[] location, Rect dirty)
方法,如下图内部进行了线程检测
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数HarmonyOS鸿蒙开发工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年HarmonyOS鸿蒙开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上HarmonyOS鸿蒙开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新
如果你觉得这些内容对你有帮助,可以添加VX:vip204888 (备注鸿蒙获取)
一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新**
如果你觉得这些内容对你有帮助,可以添加VX:vip204888 (备注鸿蒙获取)
[外链图片转存中…(img-CNNlEus7-1712784669280)]
一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!