-
调用
requestLayout
和invalidate
标记绘制和充布局信息 -
Choreographer
接受系统垂直同步等脉冲消息,在scheduleTraversals
方法中回调执行doTraversal
开始遍历view树。 -
触发
ViewRootImpl#performTraversals
完成view树遍历 -
如果
layoutRequested
为true,measureHierarchy
中测量mView
及其子view
-
需要的话,触发
ViewRootImpl#performLayout
完成布局 -
如果
view
没有隐藏且TreeObserver
中没有拦截绘制,就调用performDraw
,完成绘制 -
计算dirty脏区域
-
从mSurface中 获取脏区域的canvas,交给view绘制
2.2 ViewRootImpl
创建时机
从上面可以看到,所有的绘制和布局都是由ViewRootImpl#doTraversal
触发,然后对其持有的view树进行遍历绘制。所以一定要了解ViewRootImpl
和其持有的DecorView
的创建和关联时机。关键流程如下:
-
Activity#handleResume
的时候,调用WIndowManager#addView
添加decorView
-
调用到
WindowManagerGlobal#addView
的时候创建ViewRootImpl
实例。 -
调用
ViewRootImpl#setView
完成一系列初始化方法 -
注册
mDisplayListener
到DisplayManager
,接收显示更新回调 -
调用
requestLayout
更新一次布局大小和位置信,以确保从系统接收任何其他事件之前进行过一次布局 -
通过
WindowSession
调用addToDisplayAsUser,添加window -
在接收系统事件的时候,调用scheduleTraversals 绘制view树
WindowMangerGlobal 最终调用的其实都是ViewRootImpl方法。ViewRootImpl在addView关联号DecorView后,还调用了setView方法进行初始化,接收垂直同步脉冲信息,代码如下:
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
int userId) {
…
mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);
…
// Schedule the first layout -before- adding to the window
// manager, to make sure we do the relayout before receiving
// any other events from the system.
requestLayout();
…
try{
res = mWindowSession.addToDisplayAsUser(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), userId, mTmpFrame,
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mDisplayCutout, inputChannel,
}
}
在初始化的最后,通过WindowSession
调用addToDisplayAsUser
添加了window
到屏幕显示中。
三. 附加contentView到界面
===================
当我们启动activity,将我们写的xml布局文件显示在屏幕上,其中经历了那些过程呢?我们要在界面上展示内容,有如下几个步骤:
-
启动activity,在
performLaunchActivity
的时候创建Activity
并且attach和调用onCreate方法 -
在attach的时候,创建PhoneWindow实例并持有mWindow引用
-
调用
setContentView
以附加内容到windows中 -
通过确认
decorView
以及subDecorView
存在,创建DecorView
和subDecorView
-
添加
ContentView
到decorView
树中的R.id.content
节点 -
当
handleResumeActivity
的时候,调用WindowManager.addView
。关联View
和ViewRootImpl
,后续便可以绘制。
3.1 创建PhoneWindow
我们先看启动activity的方法,ActivityThread#performLaunchAcivity
。 从该方法源码中可知,启动activity的方法流程如下:
-
创建Activity实例 ,在
Instrumentation#newActivity
完成 -
创建PhoneWindows附加到Activity。在
Activity#attachAcitivity
完成 -
调用Activity的onCreate生命周期,代码是
Instrumentation#callActivityOnCreate
-
在
onCreate
中执行用户自定义的代码,比如setContentView
。
所以可知,在activity
准备启动的时候,就已经完成了PhoneWindows
实例的创建。而接下来就执行到了我们在Activity#onCreate
中调用setContentView
方法设置的自定义布局。
3.2 setContentView的本质
activity
在启动之后,我们通常在onCreate
调用setContentView
中设置自己的布局文件。我们来具体看看setContentView
做了什么。 setContentView
方法本质其实是向android.R.id.content
添加自己。 我们看AppCompatDelegateImpl#setContentView
@Override
public void setContentView(View v, ViewGroup.LayoutParams lp) {
///确认好 window decorView 以及 subDecorView
ensureSubDecor();
//向 android.R.id.content 添contentView
ViewGroup contentParent = mSubDecor.findViewById(android.R.id.content);
contentParent.removeAllViews();
contentParent.addView(v, lp);
mAppCompatWindowCallback.getWrapped().onContentChanged();
}
这一块代码关键在于向id为android.R.id.content
的子view中添加contentView
。 addView
的过程自然会触发布局的重新渲染。 关键之处还是在于ensureSubDecor()
方法中对于decoView
以及subDecorView
的实例化创建工作。
3.3 确认window ,decorView 以及 subDecorView
先看看AppCompatDelegateImpl#ensureSubDecor()
的主要实现:
private void ensureSubDecor() {
if (!mSubDecorInstalled) {
mSubDecor = createSubDecor();
}
}
private ViewGroup createSubDecor() {
// Now let’s make sure that the Window has installed its decor by retrieving it
ensureWindow();
mWindow.getDecorView();
final LayoutInflater inflater = LayoutInflater.from(mContext);
ViewGroup subDecor = null;
//省略其他样式subDecor布局的实例化
//包含 actionBar floatTitle ActionMode等样式
subDecor = (ViewGroup) inflater.inflate(R.layout.abc_screen_simple, null);
//省略状态栏适配代码
//省略actionBar布局替换代码
mWindow.setContentView(subDecor);
return subDecor;
}
代码很长,上面是经过省略之后的主要代码。可以看到代码逻辑很清晰:
-
步骤一:确认window并attach(设置背景等操作)
-
步骤二:获取DecorView,因为是第一次调用所以会installDecor(创建DecorView和Window#ContentLyout)
-
步骤三:从xml中实例化出subDecor布局
-
步骤四:设置内容布局:
mWindow.setContentView(subDecor);
3.4 初始化 installDecor
关键两处代码是Window#installDecor
和 Window#setContentView
。 先看一下Window#installDecor
的代码:
private void installDecor() {
mForceDecorInstall = false;
mDecor = generateDecor(-1);
if (mContentParent == null) {
//R.id.content
mContentParent = generateLayout(mDecor);
final decorContentParent = (DecorContentParent) mDecor.findViewById(
R.id.decor_content_parent);
if (decorContentParent != null) {
//…省略一些decorContentParent的处理
} else {
mTitleView = findViewById(R.id.title);
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
总结
【Android 详细知识点思维脑图(技能树)】
其实Android开发的知识点就那么多,面试问来问去还是那么点东西。所以面试没有其他的诀窍,只看你对这些知识点准备的充分程度。so,出去面试时先看看自己复习到了哪个阶段就好。
虽然 Android 没有前几年火热了,已经过去了会四大组件就能找到高薪职位的时代了。这只能说明 Android 中级以下的岗位饱和了,现在高级工程师还是比较缺少的,很多高级职位给的薪资真的特别高(钱多也不一定能找到合适的),所以努力让自己成为高级工程师才是最重要的。
这里附上上述的面试题相关的几十套字节跳动,京东,小米,腾讯、头条、阿里、美团等公司19年的面试题。把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节。
由于篇幅有限,这里以图片的形式给大家展示一小部分。
网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。
一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节。
由于篇幅有限,这里以图片的形式给大家展示一小部分。
[外链图片转存中…(img-0Z5FXABE-1712775570699)]
网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。
一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
[外链图片转存中…(img-WOlVTo6B-1712775570699)]