Android 绘制原理浅析【干货】,在字节跳动我是如何当面试官的

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行代码),大致流程是

  1. 判断是否需要重新计算视图大小,如果需要就执行performMeasure()
  2. 是否需要重新安置所在的位置,performLayout()
  3. 是否需要重新绘制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移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)
img

最后

说一千道一万,不如自己去行动。要想在移动互联网的下半场是自己占有一席之地,那就得从现在开始,从今天开始,马上严格要求自己,既重视业务实现能力,也重视基础和原理。基础夯实好了,高楼才能够平地而起,稳如泰山。

最后为了帮助大家深刻理解Android相关知识点的原理以及面试相关知识,这里放上相关的我搜集整理的24套腾讯、字节跳动、阿里、百度2020-2021面试真题解析,我把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包知识脉络 + 诸多细节

还有 高级架构技术进阶脑图、Android开发面试专题资料 帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。

一线互联网面试专题

379页的Android进阶知识大全

379页的Android进阶知识大全

点击:

Android架构视频+BAT面试专题PDF+学习笔记​》

即可免费获取~

存中…(img-JEZ3gf66-1710768622104)]

[外链图片转存中…(img-k996WRg8-1710768622104)]

[外链图片转存中…(img-Rx39Fyha-1710768622105)]

点击:

Android架构视频+BAT面试专题PDF+学习笔记​》

即可免费获取~

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

  • 19
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值