installDecor();
} else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
mContentParent.removeAllViews();
}
if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
getContext());
transitionTo(newScene);
} else {
mLayoutInflater.inflate(layoutResID, mContentParent);
}
}
在setContentView中,如果mContentParent为空,会去调用installDecor,最后将布局infalte到mContentParent.在来看一下installDecor
//PhoneWindow
// This is the view in which the window contents are placed. It is either
// mDecor itself, or a child of mDecor where the contents go.
ViewGroup mContentParent;
private DecorView mDecor;
private void installDecor() {
mForceDecorInstall = false;
if (mDecor == null) {
mDecor = generateDecor(-1);
} else {
mDecor.setWindow(this);
}
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
}
}
protected DecorView generateDecor(int featureId) {
return new DecorView(context, featureId, this, getAttributes());
}
在installDecor,创建了一个DecorView.看mContentParent的注释我们可以知道,他本身就是mDecor或者是mDecor的contents部分.
综上,我们大概知道了三者的关系,
- Activity包含了一个PhoneWindow,
- PhoneWindow就是继承于Window
- Activity通过setContentView将View设置到了PhoneWindow上
- PhoneWindow里面包含了DecorView,最终布局被添加到Decorview上.
理解ViewRootImpl,WindowManager,WindowManagerService(WMS)之间的关系
看了上述三者的关系后,我们知道布局最终被添加到了DecorView上.那么DecorView是怎么被添加到系统的Framework层.
当Activity准备好后,最终会调用到Activity中的makeVisible,并通过WindowManager添加View,代码如下
//Activity
void makeVisible() {
if (!mWindowAdded) {
ViewManager wm = getWindowManager();
wm.addView(mDecor, getWindow().getAttributes());
mWindowAdded = true;
}
mDecor.setVisibility(View.VISIBLE);
}
那他们到底是什么关系呢? (下面提到到客户端服务端是Binder通讯中的客户端服务端概念. )
以下内容是重点需要理解的部分
-
ViewRootImpl(客户端):View中持有与WMS链接的mAttachInfo,mAttachInfo持有ViewRootImpl.ViewRootImpl是ViewRoot的的实现,WMS管理窗口时,需要通知客户端进行某种操作,比如事件响应等.ViewRootImpl有个内部类W,W继承IWindow.Stub,实则就是一个Binder,他用于和WMS IPC交互。ViewRootHandler也是其内部类继承Handler,用于与远程IPC回来的数据进行异步调用.
-
WindowManger(客户端):客户端需要创建一个窗口,而具体创建窗口的任务是由WMS完成,WindowManger就像一个部门经理,谁有什么需求就告诉它,它和WMS交互,客户端不能直接和WMS交互.
-
WindowManagerService(WMS)(服务端):负责窗口的创建,显示等.
View的重绘
从上述关系中,ViewRootImpl是用于接收WMS传递来的消息.那么我们来看一下ViewRootImpl里面的几个关于View绘制的代码.
在这里在强调一下,ViewRootImpl 两个重要的内部类
- W类 继承Binder 用于接收WMS 传递来的消息
- ViewRootHandler类继承Handler 接收W类的异步消息
下面看一下ViewRootHandler类.(以View的setVisible为例.)
// ViewRootHandler(ViewRootImpl的内部类,用于异步消息处理,和Acitivity的启动很像)
//第一步 Handler接收W(Binder)传递来的消息
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_INVALIDATE:
((View) msg.obj).invalidate();
break;
case MSG_INVALIDATE_RECT:
final View.AttachInfo.InvalidateInfo info = (View.AttachInfo.InvalidateInfo) msg.obj;
info.target.invalidate(info.left, info.top, info.right, info.bottom);
info.recycle();
break;
case MSG_DISPATCH_APP_VISIBILITY://处理Visible
handleAppVisibility(msg.arg1 != 0);
break;
}
}
void handleAppVisibility(boolean visible) {
if (mAppVisible != visible) {
mAppVisible = visible;
scheduleTraversals();
if (!mAppVisible) {
WindowManagerGlobal.trimForeground();
}
}
}
void scheduleTraversals() {
if (!mTraversalScheduled) {
mTraversalScheduled = true;
mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
//开启下次刷新,就遍历View树
mChoreographer.postCallback(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
if (!mUnbufferedInputDispatch) {
scheduleConsumeBatchedInput();
}
notifyRendererOfFramePending();
pokeDrawLockIfNeeded();
}
}
看一下mTraversalRunnable
final class TraversalRunnable implements Runnable {
@Override
public void run() {
doTraversal();
}
}
final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
void doTraversal() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
performTraversals();
}
}
在TraversalRunnable中,执行doTraversal.并在doTraversal执行performTraversals(),是不是看到了我们熟悉的performTraversals()了?是的,在这里才开始View的绘制工作.
在ViewRootImpl中的performTraversals(),这个方法代码很长(大约800行代码),大致流程是
- 判断是否需要重新计算视图大小,如果需要就执行performMeasure()
- 是否需要重新安置所在的位置,performLayout()
- 是否需要重新绘制performDraw()
那么是什么导致View的重绘呢?这里总结了3个主要原因
- 视图本身内部状态(enable,pressed等)变化,可能引起重绘
- View内部添加或者删除了View
- View本身的大小和可见性发生了变化
View的绘制流程
在上一小节了,讲述了performTraversals()的是被WMS IPC调用执行的.View的绘制流程一般是
从performTraversals -> performMeasure() -> performLayout() -> performDraw().
下面看一下performMeasure()
//ViewRootImpl
private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {
if (mView == null) {
return;
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
最后
说一千道一万,不如自己去行动。要想在移动互联网的下半场是自己占有一席之地,那就得从现在开始,从今天开始,马上严格要求自己,既重视业务实现能力,也重视基础和原理。基础夯实好了,高楼才能够平地而起,稳如泰山。
最后为了帮助大家深刻理解Android相关知识点的原理以及面试相关知识,这里放上相关的我搜集整理的24套腾讯、字节跳动、阿里、百度2020-2021面试真题解析,我把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包知识脉络 + 诸多细节。
还有 高级架构技术进阶脑图、Android开发面试专题资料 帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。
点击:
《Android架构视频+BAT面试专题PDF+学习笔记》
即可免费获取~
存中…(img-JEZ3gf66-1710768622104)]
[外链图片转存中…(img-k996WRg8-1710768622104)]
[外链图片转存中…(img-Rx39Fyha-1710768622105)]
点击:
《Android架构视频+BAT面试专题PDF+学习笔记》
即可免费获取~
网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。