1、TextView#setText()绘制流程

setText()方法中调用checkForRelayout()方法

        if (mLayout != null) {checkForRelayout();}
复制代码

具体逻辑看checkForRelayout方法: 当宽度不是wrap_content,高度固定或者高度没有发生变化时,调用invalidate方法后return了;否则执行后面代码,调用requestLayout、invalidate重新绘制。invalidate在Android 28以上,开启硬件加速的设备上,不会检查UI线程(下一点具体讲)。

    /*** Check whether entirely new text requires a new view layout* or merely a new text layout.*/private void checkForRelayout() {// If we have a fixed width, we can just swap in a new text layout// if the text height stays the same or if the view height is fixed.if ((mLayoutParams.width != LayoutParams.WRAP_CONTENT ||(mMaxWidthMode == mMinWidthMode && mMaxWidth == mMinWidth)) &&(mHint == null || mHintLayout != null) &&(mRight - mLeft - getCompoundPaddingLeft() - getCompoundPaddingRight() > 0)) {// Static width, so try making a new text layout.int oldht = mLayout.getHeight();int want = mLayout.getWidth();int hintWant = mHintLayout == null ? 0 : mHintLayout.getWidth();/** No need to bring the text into view, since the size is not* changing (unless we do the requestLayout(), in which case it* will happen at measure).*/makeNewLayout(want, hintWant, UNKNOWN_BORING, UNKNOWN_BORING,mRight - mLeft - getCompoundPaddingLeft() - getCompoundPaddingRight(),false);if (mEllipsize != TextUtils.TruncateAt.MARQUEE) {// In a fixed-height view, so use our new text layout.if (mLayoutParams.height != LayoutParams.WRAP_CONTENT &&mLayoutParams.height != LayoutParams.MATCH_PARENT) {invalidate();return;}// Dynamic height, but height has stayed the same,// so use our new text layout.if (mLayout.getHeight() == oldht &&(mHintLayout == null || mHintLayout.getHeight() == oldht)) {invalidate();return;}}// We lose: the height has changed and we have a dynamic height.// Request a new view layout using our new text layout.requestLayout();invalidate();} else {// Dynamic width, so we have no choice but to request a new// view layout with a new text layout.nullLayouts();requestLayout();invalidate();}}
复制代码

TextView#requestLayout:内部调用ViewRootImpl#requestLayout方法,走测量、布局、绘制流程了。

    /*** Call this when something has changed which has invalidated the* layout of this view. This will schedule a layout pass of the view* tree. This should not be called while the view hierarchy is currently in a layout* pass ({@link #isInLayout()}. If layout is happening, the request may be honored at the* end of the current layout pass (and then layout will run again) or after the current* frame is drawn and the next layout occurs.** <p>Subclasses which override this method should call the superclass method to* handle possible request-during-layout errors correctly.</p>*/public void requestLayout() {if (mMeasureCache != null) mMeasureCache.clear();if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == null) {// Only trigger request-during-layout logic if this is the view requesting it,// not the views in its parent hierarchyViewRootImpl viewRoot = getViewRootImpl();if (viewRoot != null && viewRoot.isInLayout()) {if (!viewRoot.requestLayoutDuringLayout(this)) {return;}}mAttachInfo.mViewRequestingLayout = this;}mPrivateFlags |= PFLAG_FORCE_LAYOUT;mPrivateFlags |= PFLAG_INVALIDATED;if (mParent != null && !mParent.isLayoutRequested()) {mParent.requestLayout();}if (mAttachInfo != null && mAttachInfo.mViewRequestingLayout == this) {mAttachInfo.mViewRequestingLayout = null;}}
复制代码

ViewRootImpl#requestLayout:此处才有checkThread检测是否在主线程,因此在子线程调用TextView#setText时,如果未导致View重绘,则不会去调用checkThread,也就不会抛出异常了。

    @Overridepublic void requestLayout() {if (!mHandlingLayoutInLayoutRequest) {checkThread();mLayoutRequested = true;scheduleTraversals();}}
复制代码

而invalidate方法,将会调用到ViewRootImpl中,再次调用scheduleTraversals方法。

因此绘制了2次。

2、invalidate原理

    // View 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;}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();}}}}复制代码

到ViewGroup#invalidateChild方法中

    @Deprecated@Overridepublic final void invalidateChild(View child, final Rect dirty) {final AttachInfo attachInfo = mAttachInfo;//Android 28if (attachInfo != null && attachInfo.mHardwareAccelerated) {// HW accelerated fast pathonDescendantInvalidated(child, child);return;}......do{...parent = parent.invalidateChildInParent(location, dirty);...return parent;}while(parent != null);return null;}
复制代码

在Android 28以下,走的是parent = parent.invalidateChildInParent(location, dirty),最终在ViewRootImpl的invalidateChildInParent方法第一句检查UI线程。而当父View不需要重新绘制时,parent将返回null结束循环,只绘制当前View,也不会检测UI线程。 www.jianshu.com/p/753441fcb…

而在Android 28的invalidateChild方法中,硬件加速开启的情况下,走的是onDescendantInvalidated方法,不会去检查UI线程。因此,如果TextView宽度不是wrap_content,高度未变的情况下,是可以在子线程使用setText方法更新UI的。

    //ViewGroup 28@Override@CallSuperpublic 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 parentmPrivateFlags |= (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);}}
复制代码
    //ViewRootImpl@Overridepublic void onDescendantInvalidated(@NonNull View child, @NonNull View descendant) {if ((descendant.mPrivateFlags & PFLAG_DRAW_ANIMATION) != 0) {mIsAnimating = true;}invalidate();}void invalidate() {mDirty.set(0, 0, mWidth, mHeight);if (!mWillDrawSoon) {scheduleTraversals();}}
复制代码

3、Dalvik和ART有什么区别

Dalvik基于寄存器,运行dex文件,使用16位指令集

ART即Android Runtime。 ART 的机制与 Dalvik不同。在Dalvik下,应用每次运行的时候,字节码都需要通过即时编译器(just in time ,JIT)转换为机器码,这会拖慢应用的运行效率; 而在ART 环境中,应用在第一次安装的时候,字节码就会预先编译成机器码,使其成为真正的本地应用。这个过程叫做预编译(AOT,Ahead-Of-Time)。这样的话,应用的启动(首次)和执行都会变得更加快速。

13:TextView#setText、DalvikART虚拟机相关推荐

  1. Error:Attempt to invoke virtual method ‘void android.widget.TextView.setText(java.lang.CharSeq

    在进行Android开发学习是遇到一个error:Attempt to invoke virtual method 'void android.widget.TextView.setText(java ...

  2. TextView.setText()为什么会出错

    出现TextView.setText();出错的原因:setText()有两种方法: 一.final void setText(int resid);接收int型参数时,调用的是第一个方法,系统会去R ...

  3. win7笔记本VirtualBox安装黑苹果MacOS 10.13,win10 VMware虚拟机已升级Mojave 10.14.5

    2019-07-18温馨提示 你应该放弃使用VirtualBox来装MacOS,改用VMware.折腾了好几天安装MacOS Mojave 10.14.5,VirtualBox死活装不上,换成VMwa ...

  4. android settext 速度,Android TextView setText卡顿问题

    TextView 是经常使用控件之中的一个,最经常使用的方法是setText()  . 可是 我们在显示大量的文本的时候,使用setText还是会有一些性能的问题. 这篇文章 关于TextView的s ...

  5. Android里面的settext作用,android TextView setText无效

    我知道有很多类似的线程,但我已经完成了它们仍然无法弄清楚问题.我的程序到达处理程序,但它总是返回捕获异常"消息未处理". 我声明了TextView私有TextView聊天框; 在o ...

  6. android settext内容乱码,Android解决TextView setText显示乱码

    TextView在setText会因为编码的问题出现乱码的情况,以下是一些解决方案的记录. 1.在程序中对组件setText出现中文乱码,统一为UTF-8也没解决这个问题. 2.在build.grad ...

  7. 安卓TextView.setText卡顿优化

    码字辛苦!转载请注明出处! 0.前言 最近博主在工作之余,抽空写了一个聊天机器人app,然而为了debug,不得不加入一个用于显示日志的页面. 日志这种东西吧,懂得都懂,即便是做了日志转储,也免不了1 ...

  8. android settext里面的参数,Android: fragment 中有时textview.setText()不起效

    如题. 出现场景: 送礼物的界面一个gridview 显示不同礼物item,每送出去一个,对应要更新展示的金币余额. 在一个fragment里面调用控件的setText()的时候,偶尔不起作用或者明显 ...

  9. QT5.13.0 for IOS虚拟机开发环境配置版本

    iphone开发环境配置真的坑,不知道版本之间的匹配,装了好多个版本才配好.使用的是vmware15虚拟机来配置的环境: macOS版本:10.13.6 XCode版本:10.1 QT版本:5.13. ...

最新文章

  1. 支持向量机背后的数学原理!
  2. golang微服务框架对比_Go语言开发的微服务框架,你了解多少?
  3. Boost:将自定义占位符_1复制到arg <1>的测试程序
  4. eazy ui 复选框单选_UI备忘单:单选按钮,复选框和其他选择器
  5. 用perl操作excel的介绍
  6. java单例模式之深入浅出
  7. 【转】推荐几本学习MySQL的好书-MySQL 深入的书籍
  8. Unity笔记—常用小功能整合
  9. linux启动supervisord服务,supervisord进程管理服务
  10. java设计模式---创建者模式
  11. 树莓派CM4烧录系统
  12. Oracle Acs资深顾问罗敏 老罗技术核心感悟:11g的数据压缩技术
  13. vue中怎么获取元素
  14. mysqladmin命令简介
  15. 丝印代码html,常用贴片二三极管丝印 印字 代码
  16. 极大似然估计原理详细说明
  17. 1044 mysql_Mysql的常见几种错误:1045,1044
  18. nodejs遍历一个目录下所有的文件
  19. Powershell--正则表达式--字符--含义
  20. Copy攻城狮的年度之“战”|回顾2020

热门文章

  1. Python hashlib 无法打印
  2. 结构体序列为JSON
  3. BZOJ 3175 最大独立集
  4. 开源数据库连接池之Tomcat内置连接池
  5. .sh文件是什么语言_shell命令在C语言程序中的调用
  6. UVA11389巴士司机问题
  7. UVA10391复合词
  8. hdu4990 矩阵快速幂
  9. visual studio 64位汇编 listing列表文件
  10. 【错误记录】Android Gradle 配置报错 ( gradle.properties 配置到 BuildConfig 中需要注意类型转换 | 位置: 类 BuildConfig )