03.Android之View原理问题

11次阅读

共计 15121 个字符,预计需要花费 38 分钟才能阅读完成。

目录介绍

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/yangchong2…
如果觉得好,可以 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 的内容才会出现在屏幕上。

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()?
如果没有改变控件的 leftrighttopbottom 就不会触发 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. 关于博客汇总链接

1. 技术博客汇总

2. 开源项目汇总

3. 生活博客汇总

4. 喜马拉雅音频汇总

5. 其他汇总

02. 关于我的博客

我的个人站点:www.yczbj.org,www.ycbjie.cn
github:https://github.com/yangchong211

知乎:https://www.zhihu.com/people/…

简书:http://www.jianshu.com/u/b7b2…

csdn:http://my.csdn.net/m0_37700275

喜马拉雅听书:http://www.ximalaya.com/zhubo…

开源中国:https://my.oschina.net/zbj161…

泡在网上的日子:http://www.jcodecraeer.com/me…

邮箱:yangchong211@163.com
阿里云博客:https://yq.aliyun.com/users/a… 239.headeruserinfo.3.dT4bcV
segmentfault 头条:https://segmentfault.com/u/xi…

掘金:https://juejin.im/user/593943…

正文完
 0