其他
一文看懂DecorView的一生
The following article is from Android补给站 Author Rouse
DecorView是Android应用程序中所有视图的根视图。它是框架用来管理和显示应用程序界面的核心组件之一。理解DecorView的创建流程对于理解Android视图系统的运作方式至关重要。
与Window的关系
与Activity的关系
与ViewRootImpl的关系
ViewRootImpl是Android UI系统的内部机制,作为桥梁连接Window和DecorView。它负责初始化视图层次结构的根,处理布局、绘制、事件分发等。当一个Activity的视图被设置或者窗口发生变化时,ViewRootImpl确保DecorView得到更新和重新绘制。ViewRootImpl是不对开发者公开的,但它在视图渲染和事件处理过程中起着关键作用。
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 使用LayoutInflater加载布局
val inflater = LayoutInflater.from(this)
val view = inflater.inflate(R.layout.activity_main, null)
setContentView(view)
}
创建PhoneWindow
final void attach(Context context, ActivityThread aThread,
Instrumentation instr, IBinder token, int ident,
Application application, Intent intent, ActivityInfo info,
CharSequence title, Activity parent, String id,
NonConfigurationInstances lastNonConfigurationInstances,
Configuration config, String referrer, IVoiceInteractor voiceInteractor,
Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken,
IBinder shareableActivityToken) {
attachBaseContext(context);
mFragments.attachHost(null /*parent*/);
mActivityInfo = info;
// 创建Window
mWindow = new PhoneWindow(this, window, activityConfigCallback);
mWindow.setWindowControllerCallback(mWindowControllerCallback);
mWindow.setCallback(this);
mWindow.setOnWindowDismissedCallback(this);
mWindow.getLayoutInflater().setPrivateFactory(this);
....
}
初始化DecorView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
获取Window: 首先,setContentView通过getWindow()方法获取当前Activity的Window对象。Window对象代表了Android窗口管理系统中的一个窗口。 布局解析: 使用LayoutInflater解析指定的布局资源ID。这个过程会根据布局文件中的定义,创建出对应的View对象,并按照布局文件的层次结构组装这些对象,形成一个完整的视图树。 设置内容视图: 通过Window的setContentView方法,将解析好的视图树设置为Window的内容视图。这个视图树的根节点,就是我们所说的DecorView。
public void setContentView(int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
@Override
public void setContentView(int layoutResID) {
// 确保DecorView已经被创建
if (mContentParent == null) {
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);
}
mContentParent.requestApplyInsets();
final Callback cb = getCallback();
if (cb != null && !isDestroyed()) {
cb.onContentChanged();
}
mContentParentExplicitlySet = true;
}
private void installDecor() {
if (mDecor == null) {
mDecor = generateDecor(-1);
mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
mDecor.setIsRootNamespace(true);
...
} else {
...
}
if (mContentParent == null) {
mContentParent = generateLayout(mDecor);
// 其他初始化代码...
}
}
将DecorView添加到WindowManager中
@Override
public void handleResumeActivity(ActivityClientRecord r, boolean finalStateRequest,
boolean isForward, boolean shouldSendCompatFakeFocus, String reason) {
...
// 执行Activity onResume
if (!performResumeActivity(r, finalStateRequest, reason)) {
return;
}
...
if (r.window == null && !a.mFinished && willBeVisible) {
// PhoneWindow
r.window = r.activity.getWindow();
View decor = r.window.getDecorView();
decor.setVisibility(View.INVISIBLE);
ViewManager wm = a.getWindowManager();
WindowManager.LayoutParams l = r.window.getAttributes();
a.mDecor = decor;
...
if (a.mVisibleFromClient) {
if (!a.mWindowAdded) {
a.mWindowAdded = true;
// 添加到WindowManager中,并与wms建立双向通信
wm.addView(decor, l);
} else {
a.onWindowAttributesChanged(l);
}
}
} else if (!willBeVisible) {
if (localLOGV) Slog.v(TAG, "Launch " + r + " mStartedActivity set");
r.hideForNow = true;
}
...
}
这就是为什么我们在onCreate与onResume的时候不能直接拿到View的宽高的原因。因为DecorView添加是在onResume之后。
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow, int userId) {
...
ViewRootImpl root;
View panelParentView = null;
synchronized (mLock) {
...
// 创建ViewRootImpl
if (windowlessSession == null) {
root = new ViewRootImpl(view.getContext(), display);
} else {
root = new ViewRootImpl(view.getContext(), display,
windowlessSession, new WindowlessWindowLayout());
}
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
try {
// 将DecorView交由ViewRootImpl,进行后续的绘制与事件分发等出来。
root.setView(view, wparams, panelParentView, userId);
} catch (RuntimeException e) {
final int viewIndex = (index >= 0) ? index : (mViews.size() - 1);
if (viewIndex >= 0) {
removeViewLocked(viewIndex, true);
}
throw e;
}
}
}
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
int userId) {
...
requestLayout()
...
}
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
// 主线程判断
checkThread();
mLayoutRequested = true;
// 等待垂直刷新信号量的到来,触发分发绘制流程
scheduleTraversals();
}
}
验证是否是在主线程触发。 等待刷新,触发后续的绘制流程。
在Activity的attach()方法里面先创建PhoneWindow并获取WindowManager。 在Activity的onCreate()方法里调用setContentView()会通过调用用PhoneWindow的installDecor()来创建DecorView。 在Activity的onResume()方法之后,也就是handleResumeActivity()方法中,会把DecorView添加到WindowMangaer中,并与wms建立双向通信。最终交个ViewRootImpl进行后续的绘制流程。 在ViewRootImple中,验证触发线程,并等到屏幕刷新信号来了,会调用到ViewRootImpl的performTraversals()来进行后续的绘制。
最后推荐一下我做的网站,玩Android: wanandroid.com ,包含详尽的知识体系、好用的工具,还有本公众号文章合集,欢迎体验和收藏!
推荐阅读:
详解Android14 Activity 启动过程
不同版本上 Bitmap 内存分配与回收对比
扫一扫 关注我的公众号
如果你想要跟大家分享你的文章,欢迎投稿~
┏(^0^)┛明天见!