• 03.Android之View原理问题


    目录介绍

    • 3.0.0.1 View的绘制需要经过哪些过程?有哪些常用回调方法?View的绘制流程的详细流程是怎样的?
    • 3.0.0.2 View绘制流程,当一个TextView的实例调用setText()方法后执行了什么?请说一下原理……
    • 3.0.0.3 requestLayout()、invalidate()与postInvalidate()有什么区别?
    • 3.0.0.4 DecorView的作用是什么?DecorView中如何获取ContentView以及Activity所设置的View?ViewRootIml如何和DecorView建立联系?
    • 3.0.0.5 getWidth()方法和getMeasureWidth()区别呢?为什么有时候用getWidth()或者getMeasureWidth()得到0?
    • 3.0.0.6 平时写的自定义控件有哪些?如何优化自定义view?View的绘制流程说一下?自定义View的注意点?
    • 3.0.0.7 View的wrap_content和match_parent效果一致的原因分析?getDefaultSize方法的处理逻辑?
    • 3.0.0.8 ViewGroup(抽象类)的measure流程?getChildMeasureSpec获取子元素MeasureSpec的要点?
    • 3.0.0.9 View的layout过程?View的layout()源码分析?LinearLayout的onLayout方法?View的测量宽高和最终宽高有什么区别?
    • 3.0.1.0 draw的过程步骤是什么?View特殊方法setWillNotDraw是干什么用的?
    • 3.0.1.1 View中x,y,translationX,translationY分别是什么?View平移时是否改变了left、top等原始参数?
    • 3.0.1.2 MeasureSpec是什么?MeasureSpec的组成?测量模式SpecMode的类型和具体含义?MeasureSpec和LayoutParams的对应关系?
    • 3.0.1.3 如何获取View的测量宽/高?如何在Activity启动时获得View的宽/高?Activity中获得View宽高的4种办法?
    • 3.0.1.4 Activity启动到最终加载ViewRoot(执行三大流程)的流程是什么?
    • 3.0.1.5 自定义View性能优化有哪些?针对异常销毁,自定义View如何优化?如何避免创建大量对象?

    好消息

    • 博客笔记大汇总【15年10月到至今】,包括Java基础及深入知识点,Android技术博客,Python学习笔记等等,还包括平时开发中遇到的bug汇总,当然也在工作之余收集了大量的面试题,长期更新维护并且修正,持续完善……开源的文件是markdown格式的!同时也开源了生活博客,从12年起,积累共计500篇[近100万字],将会陆续发表到网上,转载请注明出处,谢谢!
    • 链接地址:https://github.com/yangchong211/YCBlogs
    • 如果觉得好,可以star一下,谢谢!当然也欢迎提出建议,万事起于忽微,量变引起质变!所有的笔记将会更新到GitHub上,同时保持更新,欢迎同行提出或者push不同的看法或者笔记!

    3.0.0.1 View的绘制需要经过哪些过程?有哪些常用回调方法?View的绘制流程的详细流程是怎样的?

    • View的绘制需要经过哪些过程?
      • measure:测量View的宽和高
        • View的measure方法是final类型方法——表明该方法无法被重载
        • View的measure方法会调用onMeasure方法,onMeasure会调用setMeasuredDimension方法设置View宽/高的测量值
      • layout:确定View在父控件中的放置位置
      • draw:负责将View绘制在屏幕上。技术博客大总结
    • 有哪些常用回调方法?
      • 构造方法
      • onAttachToWindow:在包含View的Activity启动时调用
      • onDetachFromWindow:在包含View的Activity退出或者View被remove时回调
      • onVisibilityChanged:当View的可见状态发生改变时调用
    • 比较重要的概念
      • ViewRoot:连接WindowManager(外界访问Window的入口)和DecorView(顶级View)的纽带,View的三大流程均是通过ViewRoot来完成的。
      • DecorView:顶级View
        • DecorView是顶级View,本质就是一个FrameLayout
        • 包含了两个部分,标题栏和内容栏
        • 内容栏id是content,也就是activity中setContentView所设置的部分,最终将布局添加到id为content的FrameLayout中
    • View的绘制流程的详细流程是怎样的?技术博客大总结
      • View的绘制流程是从ViewRoot的PerformTraversals方法开始的。大概的流程如下所示
        • performTraversals会依次调用performMeasure, performLayout, performDraw三个方法,这三个方法分别完成顶层View的measure,layout,draw方法,onMeasure又会调用所有子元素的measure过程,直到完成整个View树的遍历。同理,performLayout, performDraw的传递流程与performMeasure相似。唯一不同在于,performDraw的传递过程在draw方法中通过dispatchDraw实现,但没有本质区别。
        • Measure过程后可以调用getMeasureWidth和getMeasureHeight方法获取View测量后的宽高,与getWidth和getHeight的区别是:getMeasuredHeight()返回的是原始测量高度,与屏幕无关,getHeight()返回的是在屏幕上显示的高度。实际上在当屏幕可以包裹内容的时候,他们的值是相等的,只有当view超出屏幕后,才能看出他们的区别。当超出屏幕后,getMeasuredHeight()等于getHeight()加上屏幕之外没有显示的高度。
        • Layout过程确定View四个顶点的位置和实际的宽高。
        • Draw过程确定View的显示,只有draw方法完成后View的内容才会出现在屏幕上。
        • image

    3.0.0.2 View绘制流程,当一个TextView的实例调用setText()方法后执行了什么?请说一下原理……

    • View的绘制流程主要分为三步:
      • onMeasure:测量视图的大小,从顶层父View到子View递归调用measure()方法,measure()调用onMeasure()方法,onMeasure()方法完成绘制工作。
      • onLayout:确定视图的位置,从顶层父View到子View递归调用layout()方法,父View将上一步measure()方法得到的子View的布局大小和布局参数,将子View放在合适的位置上。
      • onDraw:绘制最终的视图,首先ViewRoot创建一个Canvas对象,然后调用onDraw()方法进行绘制。onDraw()方法的绘制流程为
        • ① 绘制视图背景。
        • ② 绘制画布的图层。 技术博客大总结
        • ③ 绘制View内容。
        • ④ 绘制子视图,如果有的话。
        • ⑤ 还原图层。
        • ⑥ 绘制滚动条。

    3.0.0.3 requestLayout()、invalidate()与postInvalidate()有什么区别?requestLayout()何时不会触发onDraw()?

    • invalidate() postInvalidate()
      • invalidate()该方法递归调用父View的invalidateChildInParent()方法,直到调用ViewRootImpl的invalidateChildInParent()方法,最终触发ViewRootImpl的performTraversals()方法,此时mLayoutRequestede为false,不会触发onMesaure()与onLayout()方法,有可能会触发onDraw()方法。
      • 共同点:都是调用onDraw()方法,然后去达到重绘view的目的
      • 区别:invalidate()用于主线程,postInvalidate()用于子线程
    • requestLayout()
      • 该方法会递归调用父窗口的requestLayout()方法,直到触发ViewRootImpl的performTraversals()方法,此时mLayoutRequestede为true,会触发onMesaure()与onLayout()方法,不一定会触发onDraw()方法。
      • 当view确定自身已经不再适合现有的区域时,该view本身调用这个方法要求parent view(父类的视图)重新调用他的onMeasure、onLayout来重新设置自己位置。特别是当view的layoutparameter发生改变,并且它的值还没能应用到view上时,这时候适合调用这个方法requestLayout()。requestLayout调用onMeasure和onLayout,不一定调用onDraw技术博客大总结
    • 如何选择
      • 一般说来需要重新布局就调用requestLayout()方法,需要重新绘制就调用invalidate()方法。
    • requestLayout()何时不会触发onDraw()?
      • 如果没有改变控件的left ight opottom就不会触发onDraw()
    • invalidate()在什么情况下不会触发onDraw?
      • 在ViewGroup中,invalidate默认不重新绘制子view。
    • 如何让ViewGroup在invalidate时会触发onDraw?技术博客大总结
      • 本质需要将ViewGroup的dirtyOpaque设置为false
        • 1.在构造函数中调用setWillNotDraw(false);
        • 2.给ViewGroup设置背景。调用setBackground。

    3.0.0.4 DecorView的作用是什么?DecorView中如何获取ContentView以及Activity所设置的View?ViewRootIml如何和DecorView建立联系?

    • DecorView的作用是什么?
      • DecorView是顶级View,本质就是一个FrameLayout
      • 包含了两个部分,标题栏和内容栏,内容栏id是content,也就是activity中setContentView所设置的部分,最终将布局添加到id为content的FrameLayout中技术博客大总结
    • DecorView中如何获取ContentView以及Activity所设置的View?
      • 获取content:ViewGroup content = findViewById(R.android.id.content)
      • 获取设置的View:content.getChidlAt(0)
    • ViewRootIml如何和DecorView建立联系?技术博客大总结
      • Activity对象在ActivityThread中创建完毕后,会将DecorView添加到Window中
      • 同时会创建ViewRootImpl,调用ViewRoot的setView方法将ViewRootImpl和DevorView建立关联
        root = new ViewRootImpl(view.getContext(), display);
        root.setView(view, wparams, panelParentView);
        
    • ViewRoot为什么要和DecorView建立关联
      • DecorView等View的三大流程需要通过ViewRoot完成

    3.0.0.5 getWidth()方法和getMeasureWidth()区别呢?为什么有时候用getWidth()或者getMeasureWidth()得到0?

    • getWidth()方法和getMeasureWidth()区别呢
      • getMeasureWidth()
        • getMeasureWidth()方法在measure()过程结束后就可以获取到了,另外,getMeasureWidth()方法中的值是通过setMeasuredDimension()方法来进行设置的
        • 这里mMeasuredWidth & MEASURED_SIZE_MASK表示的是测量阶段结束之后,view真实的值。
        public final int getMeasuredWidth() {
            return mMeasuredWidth & MEASURED_SIZE_MASK;
        }
        
      • getWidth()
        • getWidth()方法要在layout()过程结束后才能获取到,getWidth()方法中的值则是通过视图右边的坐标减去左边的坐标计算出来的。
        • mRight和mLeft是什么值,是在什么时候被设置的。具体看layout()过程中源码
        @ViewDebug.ExportedProperty(category = "layout")
        public final int getWidth() {
            return mRight - mLeft;
        }
        
    • 为什么有时候用getWidth()或者getMeasureWidth()得到0
      • 问题描述:使用getMeasuredWidth()和getMeasuredHeight()方法,无论是在onCreate()、onStart()、onResume()中调用,都无法得到控件的长度、和宽度。如下图,测量的结果为0。
      • 解释:技术博客大总结
        • 因为View的Measure过程和Activity的生命周期方法不是同步执行的,所以无法保证Activity执行了onCreate()、onStart()、onResume()时某个View已经测量完毕了,如果View还没有测量完毕,那么获得宽/高就是0。
        • 后来觉得这种解释有点牵强,比如
    • 解决控件测量宽高问题
      • 如下所示
      @Override
      protected void onCreate(Bundle savedInstanceState) {
          super.onCreate(savedInstanceState);
          setContentView(R.layout.activity_main);
          test();
      }
      
      @Override
      public void onWindowFocusChanged(boolean hasFocus) {
          super.onWindowFocusChanged(hasFocus);
          getWidth(4);
      }
      
      private void test(){
          getWidth(1);
          
          marqueeView.measure(0, 0);
          getWidth(2);
          
          marqueeView.post(new Runnable() {
              @Override
              public void run() {
                  getWidth(3);
              }
          });
      }
      
      private void getWidth(int a){
          int width2 = marqueeView.getWidth();
          int measuredWidth2 = marqueeView.getMeasuredWidth();
          Log.e(a+"MainActivity-----",width2+"-----"+measuredWidth2);
      }
      
      //11-28 17:03:17.559 15990-15990/com.yc.cn.ycbanner E/1MainActivity-----: 0-----0
      //11-28 17:03:17.567 15990-15990/com.yc.cn.ycbanner E/2MainActivity-----: 0-----760
      //11-28 17:03:17.684 15990-15990/com.yc.cn.ycbanner E/3MainActivity-----: 960-----960
      //11-28 17:03:17.685 15990-15990/com.yc.cn.ycbanner E/4MainActivity-----: 960-----960
      
    • 什么时候测量宽高不等于实际宽高?
      • MeasuredWidth/height!=getWidth/Height()的场景:更改View的布局参数并进行重新布局后,就会导致测量宽高!=实际宽高

    3.0.0.6 平时写的自定义控件有哪些?如何优化自定义view?View的绘制流程说一下?自定义View的注意点?

    • 平时写的自定义控件有哪些?
      • 1组合控件。这种自定义控件不需要我们自己绘制,而是使用原生控件组合成的新控件。如标题栏,recyclerView封装控件。
      • 2继承原有的控件。这种自定义控件在原生控件提供的方法外,可以自己添加一些方法。比如图片缩放控件,进度条控件。
      • 3完全自定义控件:这个View上所展现的内容全部都是我们自己绘制出来的。比如百分比进度条控件
    • 如何优化自定义 view
      • 为了加速你的view,对于频繁调用的方法,需要尽量减少不必要的代码。先从onDraw开始,需要特别注意不应该在这里做内存分配的事情,因为它会导致GC,从而导致卡顿。在初始化或者动画间隙期间做分配内存的动作。不要在动画正在执行的时候做内存分配的事情。
      • 你还需要尽可能的减少onDraw被调用的次数,大多数时候导致onDraw都是因为调用了invalidate().因此请尽量减少调用invaildate()的次数。如果可能的话,尽量调用含有4个参数的invalidate()方法而不是没有参数的invalidate()。没有参数的invalidate会强制重绘整个view。技术博客大总结
      • 另外一个非常耗时的操作是请求layout。任何时候执行requestLayout(),会使得Android UI系统去遍历整个View的层级来计算出每一个view的大小。如果找到有冲突的值,它会需要重新计算好几次。另外需要尽量保持View的层级是扁平化的,这样对提高效率很有帮助。
      • 如果你有一个复杂的UI,你应该考虑写一个自定义的ViewGroup来执行他的layout操作。与内置的view不同,自定义的view可以使得程序仅仅测量这一部分,这避免了遍历整个view的层级结构来计算大小。这个PieChart 例子展示了如何继承ViewGroup作为自定义view的一部分。PieChart 有子views,但是它从来不测量它们。而是根据他自身的layout法则,直接设置它们的大小。
    • View的绘制流程说一下?
      • View的绘制流程:OnMeasure()——>OnLayout()——>OnDraw()
      • 第一步:OnMeasure():测量视图大小。从顶层父View到子View递归调用measure方法,measure方法又回调OnMeasure。
      • 第二步:OnLayout():确定View位置,进行页面布局。从顶层父View向子View的递归调用view.layout方法的过程,即父View根据上一步measure子View所得到的布局大小和布局参数,将子View放在合适的位置上。
      • 第三步:OnDraw():绘制视图。ViewRoot创建一个Canvas对象,然后调用OnDraw()。六个步骤:①、绘制视图的背景;②、保存画布的图层(Layer);③、绘制View的内容;④、绘制View子视图,如果没有就不用;⑤、还原图层(Layer);⑥、绘制滚动条。技术博客大总结
    • 自定义View的注意点?
      • View需要支持wrap_content、padding
      • ViewGroup需要支持子View的margin和自身的padding
      • 尽量不要在View中使用Handler,View已经有post系列方法
      • View如果有线程或者动画,需要及时停止(onDetachedFromWindow会在View被remove时调用)——避免内存泄露
      • View如果有滑动嵌套情形,需要处理好滑动冲突

    3.0.0.7 View的wrap_content和match_parent效果一致的原因分析?getDefaultSize方法的处理逻辑?

    • View的wrap_content和match_parent效果一致的原因分析?
      • 根据View的onMeasure方法中的getDefaultSize方法,我们可以发现在两种模式下,View的测量值等于该View的测量规格MeasureSpec中的尺寸。
      • View的MeasureSpec本质是由自身的LayoutParams和父容器的MeasureSpec决定的。
      • 当View为wrap_content时,该View的模式为AT_MOST,且尺寸specSize为父容器的剩余空间大小。
      • 当View为match_parent时,该View的模式跟随父容器的模式(AT_MOST/EXACTLY), 且尺寸specSize为父容器的剩余空间大小。
      • 因此getDefaultSize中无论View是哪种模式,最终测量宽/高均等于尺寸specSize,因此两种属性效果是完全一样的(View的大小充满了父容器的剩余空间)技术博客大总结
      • 除非给定View固定的宽/高,View的specSize才会等于该固定值。
    • getDefaultSize方法的处理逻辑?
      • getDefaultSize: 根据建议获取的最小宽高和测量规格,决定实际的测量宽高
        • UNSPECIFIED模式:测量宽高 = 建议的最小宽高
        • EXACTLY / AT_MOST模式:测量宽高 = specSize技术博客大总结
      • View的getDefaultSize源码要点(决定了View宽高的测量值)
        • UNSPECIFIED模式时,宽/高为第一个参数也就是getSuggestedMinimumWidth()获取的建议最小值
        • AT_MOST(wrap_content)和EXACTLY(match_parent/具体值dp等)这两个模式下,View宽高的测量值为当前View的MeasureSpec(测量规格)中指定的尺寸specsize

    3.0.0.8 ViewGroup(抽象类)的measure流程?getChildMeasureSpec获取子元素MeasureSpec的要点?

    • ViewGroup(抽象类)的measure流程?
      • ViewGroup没有onMeasure方法,只定义了measureChildren方法(onMeasure根据不同布局难以统一)
      • measureChildren中遍历所有子元素并调用measureChild方法
      • measureChild方法中会获取子View的MeasureSpec(getChildMeasureSpec),然后调用子元素View的measure方法进行测量
    • getChildMeasureSpec获取子元素MeasureSpec的要点?
      • 子View的MeasureSpec是根据自身的LayoutParams和父容器SpecMode生成
      • 当子View的布局参数为wrap_content,且父容器模式为AT_MOST时,效果与子元素布局为match_parent是一样的。因此当子View的布局参数为wrap_content时,需要给指定默认的宽/高
    • LinearLayout的onMeasure()分析
      • ViewGroup因为布局的不同,无法统一onMeasure方法,具体内容根据布局的不同而不同,这里直接以LinearLayout进行分析
      • onMeasure会根据orientation选择measureVertical或者measureHorizontal进行测量
      • measureVertical本质是遍历子元素,并执行子元素的measure方法,并获得子元素的总高度以及子元素在竖直方向上的margin等。技术博客大总结
      • 最终LinearLayout会测量自己的大小,在orientation的方向上,如果布局是match_parent或者具体数值,测量过程与View一致(高度为specSize);如果布局是wrap_content,高度是所有子元素高度总和,且不会超过父容器的剩余空间,最终高度需要考虑在竖直方向上的padding

    3.0.0.9 View的layout过程?View的layout()源码分析?LinearLayout的onLayout方法?View的测量宽高和最终宽高有什么区别?

    • View的layout过程?
      • 使用layout方法确定View本身的位置
      • layout中调用onLayout方法确定所有子View的位置
    • View的layout()源码分析?
      • 调用setFrame()设置View四个定点位置(即初始化mLeft,mRight,mTop,mBottom的值)
      • 之后调用onLayout确定子View位置,该方法类似于onMeasure,View和ViewGroup中均没有实现,具体实现与具体布局有关。
    • LinearLayout的onLayout方法?
      • 根据orientation选择调用layoutVertical或者layoutHorizontal
      • layoutVertical中会遍历所有子元素并调用setChildFrame(里面直接调用子元素的layout方法)
      • 层层传递下去完成了整个View树的layout过程
      • setChildFrame中的宽/高实际就是子元素的测量宽/高(getMeasure…后直接传入)
    • View的测量宽高和最终宽高有什么区别?技术博客大总结
      • 等价于getMeasuredWidth和getWidth有什么区别
      • getWidth = mRight - mLeft,结合源码测量值和最终值是完全相等的。
      • 区别在于:测量宽高形成于measure过程,最终宽高形成于layout过程(赋值时机不同)
      • 也有可能导致两者不一致:强行重写View的layout方法,在传参方面改变最终宽/高(虽然这样毫无实际意义)
      • 某些情况下,View需要多次measure才能确定自己的测量宽高,在前几次测量中等到的值可能有最终宽高不一致。但是最终结果上,测量宽高=最终宽高

    3.0.1.0 draw的过程步骤是什么?View特殊方法setWillNotDraw是干什么用的?

    • draw的过程步骤是什么?
      • 绘制背景(drawBackground(canvas))
      • 绘制自己(onDraw)
      • 绘制children(dispatchDraw)-遍历调用所有子View的draw方法
      • 绘制装饰(如onDrawScollBars)
    • View特殊方法setWillNotDraw是干什么用的?
      • 若一个View不绘制任何内容,需要将该标志置为true,系统会进行相应优化
      • 默认View不开启该标志位技术博客大总结
      • 默认ViewGroup开启该标志位
      • 如果我们自定义控件继承自ViewGroup并且本身不进行绘制时,就可以开启该标志位
      • 当该ViewGroup明确通过onDraw绘制内容时,就需要显式关闭WILL_NOT_DRAW标志位。

    3.0.1.1 View中x,y,translationX,translationY分别是什么?View平移时是否改变了left、top等原始参数?

    • View中x,y,translationX,translationY分别是什么?
      • x,y是View当前左上角的坐标
      • translationX,translationY是在滑动/动画后,View当前位置和View最原始位置的距离。
      • 因此得出等式:x(View左上角当前位置) = left(View左上角初始位置) + translationX(View左上角偏移的距离)
    • View平移时是否改变了left、top等原始参数?技术博客大总结
      • View平移时top、left等参数不变,改变的是x,y,tranlsationX和tranlsationY

    3.0.1.2 MeasureSpec是什么?MeasureSpec的组成?测量模式SpecMode的类型和具体含义?MeasureSpec和LayoutParams的对应关系?

    • MeasureSpec是什么?
      • MeasureSpec是一种“测量规则”或者“测量说明书”,决定了View的测量过程
      • View的MeasureSpec会根据自身的LayoutParamse和父容器的MeasureSpec生成。
      • 最终根据View的MeasureSpec测量出View的宽/高(测量时数据并非最终宽高)
    • MeasureSpec的组成?
      • MeasureSpec代表一个32位int值,高2位是SpecMode,低30位是SpecSize
      • SpecMode是指测量模式
      • SpecSize是指在某种测量模式下的大小
      • 类MesaureSpec提供了用于SpecMode和SpecSize打包和解包的方法
    • 测量模式SpecMode的类型和具体含义?技术博客大总结
      • UNSPECIFIED:父容器不对View有任何限制,一般用于系统内部
      • EXACTLY:精准模式,View的最终大小就是SpecSize指定的值(对应于LayoutParams的match_parent和具体的数值)
      • AT_MOST:最大值模式,大小不能大于父容器指定的值SpecSize(对应于wrap_content)
    • MeasureSpec和LayoutParams的对应关系?
      • View的MeasureSpec是需要通过自身的LayoutParams和父容器的MeasureSpec一起才能决定
      • DecorView(顶级View)是例外,其本身MeasureSpec由窗口尺寸和自身LayoutParams共同决定
      • MeasureSpec一旦确定,onMeasure中就可以确定View的测量宽/高

    3.0.1.3 如何获取View的测量宽/高?如何在Activity启动时获得View的宽/高?Activity中获得View宽高的4种办法?

    • 如何获取View的测量宽/高?
      • 在measure完成后,可以通过getMeasuredWidth/Height()方法,就能获得View的测量宽高
      • 在一定极端情况下,系统需要多次measure,因此得到的值可能不准确,最好的办法是在onLayout方法中获得测量宽/高或者最终宽/高
    • 如何在Activity启动时获得View的宽/高?
      • Activity的生命周期与View的measure不是同步运行,因此在onCreate/onStart/onResume均无法正确得到
      • 若在View没有测量好时,去获得宽高,会导致最终结果为0
      • 有四种办法去正确获得宽高
    • Activity中获得View宽高的4种办法?技术博客大总结
      • onWindowFocusChanged
        • View已经初始化完毕,可以获得宽高;Activity得到焦点和失去焦点均会调用一次(频繁onResume和onPause会导致频繁调用)
      • view.post(runnable)
        • 通过post将一个runnable投递到消息队列尾部;等到Looper调用次runnable时,View已经完成初始化
      • ViewTreeObserver
        • 使用ViewTreeObserver的接口,可以在View树状态改变或者View树内部View的可见性改变时,onGlobalLayout会被回调;能正确获取View宽/高
      • view.measure

    3.0.1.4 Activity启动到最终加载ViewRoot(执行三大流程)的流程是什么?

    • Activity启动到最终加载ViewRoot(执行三大流程)的流程是什么?
      • Activity调用startActivity方法,最终会调用ActivityThread的handleLaunchActivity方法
      • handleLaunchActivity会调用performLauchActivity方法(会调用Activity的onCreate,并完成DecorView的创建)和handleResumeActivity方法
      • handleResumeActivity方法会做四件事:performResumeActivity(调用activity的onResume方法)、getDecorView(获取DecorView)、getWindowManager(获取WindowManager)、WindowManager.addView(decor, 1)
      • WindowManager.addView(decor, 1)本质是调用WindowManagerGlobal的addView方法。其中主要做两件事:1、创建ViewRootImpl实例 2、root.setView(decor, ….)将DecorView作为参数添加到ViewRoot中,这样就将DecorView加载到了Window中
      • ViewRootImpl还有一个方法performTraveals方法,用于让ViewTree开始View的工作流程:其中会调用performMeasure/Layout/Draw()三个方法,分别对应于View的三大流程。

    3.0.1.5 自定义View性能优化有哪些?针对异常销毁,自定义View如何优化?如何避免创建大量对象?

    • 自定义View性能优化有哪些?
      • 避免过度绘制
      • 尽量减少或简化计算
      • 避免创建大量对象造成频繁GC
      • 禁止或避免I/O操作
      • onDraw中避免冗余代码、避免创建对象
      • 复合View,要减少布局层级。
      • 状态和恢复和保存
      • 开启硬件加速
      • 合理使用invalidate的参数版本。
      • 减少冗余代码:不要使用Handler,因为已经有post系列方法.
      • 使用的线程和动画,要在onDetachedFromWindow中进行清理工作。
      • 要妥善处理滑动冲突。
    • 避免过度绘制
      • 像素点能画一次就不要多次绘制,以及绘制看不到的背景
      • 开发者选项里内的工具,只对xml布局有效果,看不到自定义View的过度绘制,仍然需要注意。
    • 尽量减少或简化计算
      • 不要做无用计算。尽可能的复用计算结果。技术博客大总结
      • 没有数据,或者数据较少的时候应如何处理,没有事件需要响应的时候如何处理。
      • 应该避免在for或while循环中做计算。比如:去计算屏幕宽度等信息。
    • 避免创建大量对象造成频繁GC
      • 应该避免在for或while循环中new对象。这是减少内存占用量的有效方法。
    • 禁止或避免I/O操作
      • I/O操作对性能损耗极大,不要在自定义View中做IO操作。
    • onDraw中避免冗余代码、避免创建对象
      • onDraw中禁止new对象.如:不应该在ondraw中创建Paint对象。Paint类提供了reset方法。可以在初始化View时创建对象。
      • 要避免冗余代码,提高效率。
    • 复合View,要减少布局层级。
      • 复合控件:继承自现有的LinearLayout等ViewGroup,然后组合多个控件来实现效果。这种实现方法要注意减少布局层级,层级越高性能越差。
    • 状态和恢复和保存
      • Activity还会因为内存不足或者旋转屏幕而导致重建Activity,自定义View也要去进行自我状态的保存和读取。
      • 在onSaveInstanceState()保存状态;在onRestoreInstanceState()恢复状态
    • 开启硬件加速
    • 合理使用invalidate的参数版本。技术博客大总结
      • 避免任何请款下之际调用默认参数的invalidate
      • 调用有参数的invalidate进行局部和子View刷新,能够提高性能。
    • 减少冗余代码:不要使用Handler,因为已经有post系列方法
      • View已经有post系列方法,没有必要重复去写。
      • 可以直接使用,最终会投递到主线程的Handler中
    • 使用的线程和动画,要在onDetachedFromWindow中进行清理工作。
      • View如果有线程或者动画,需要及时停止.
      • View的onDetachedFromWindow会在View被remove时调用,在该方法内进行终止
      • 这样能避免内存泄露
    • 要妥善处理滑动冲突。
      • View如果有滑动嵌套情形,需要处理好滑动冲突

    关于其他内容介绍

    01.关于博客汇总链接

    02.关于我的博客

  • 相关阅读:
    Linux下Zookeeper安装使用
    Dubbo的@Reference和@Service说明
    java.lang.ClassNotFoundException: org.I0Itec.zkclient.IZkStateListener异常解决
    Java 开发常用工具
    Vue自动化路由(基于Vue-Router)开篇
    HTML5 开发APP( 支付宝支付)
    关于微信网页缓存的问题
    小程序(仿微信发布说说功能)
    移动端九宫格抽奖源码
    H5 + 开发App(分享功能)
  • 原文地址:https://www.cnblogs.com/yc211/p/10238413.html
Copyright © 2020-2023  润新知