03.Android之View原理问题

2,512

目录介绍

  • 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万字],将会陆续发表到网上,转载请注明出处,谢谢!
  • 链接地址:github.com/yangchong21…
  • 如果觉得好,可以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\right\top\bottom就不会触发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.关于我的博客