「这是我参与11月更文挑战的第6天,活动详情查看:2021最后一次更文挑战」
前言:
window是一个抽象类,它的具体实现是PhoneWindow。创建一个window是非常简单的。我们只需要通过WindowManager即可完成。WindowMangaer是外界访问Window的入口。Window的具体实现位于WindowManagerService中。WIndowManager和WindowService的交互是一个IPC的过程。android中所有的视图都是通过Window来呈现的,不管是Activity,toast,Dialog,我们常见的setContentView在底层也是通过Window来完成的。
Window的内部机制
每一个Window都对应着一个View和ViewRootImpl.Window和view是通过ViewRootImpl来建立联系,因此Window并不是实际存在的,它是以View的形式存在,这点从WindowManager的定义也可以看出它继承ViewManager,ViewManager提供三个接口方法 addView,updateViewLayout,removeView都是针对View的,这说明View才是Window存在的实体。在实际使用中无法直接访问Window,对Window的访问必须通过WindowManager。WindowManage人具体实现是WindowManagerImpl
在WindowManagerImpl中的操作如下:
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplayNoVerify(), mParentWindow,
mContext.getUserId());
}
@Override
public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.updateViewLayout(view, params);
}
可以发现WindowMnagerImpl并没有真正的实现Window的三大操作,而是交给了WindowMnagerGlobal。WindwManagerGlobal的实现如下;
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow, int userId) {
//检查参数是否合法
if (view == null) {
throw new IllegalArgumentException("view must not be null");
}
if (display == null) {
throw new IllegalArgumentException("display must not be null");
}
if (!(params instanceof WindowManager.LayoutParams)) {
throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
}
//.......省略一些代码
ViewRootImpl root;
View panelParentView = null;
//.......省略中间代码
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
// 开始执行操作。
try {
root.setView(view, wparams, panelParentView, userId);
} catch (RuntimeException e) {
// BadTokenException or InvalidDisplayException, clean up.
if (index >= 0) {
removeViewLocked(index, true);
}
throw e;
}
}
}
最终调用root.setView执行绘制操作
/**
* We have one child
*/
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView,
int userId) {
//....省略一些代码
// 最终调用了我们熟悉的requestLayout进行异步刷新请求
requestLayout();
//....省略一些代码
}
//requestLayout又交给scheduleTraversals
@Override
public void requestLayout() {
if (!mHandlingLayoutInLayoutRequest) {
checkThread();
mLayoutRequested = true;
scheduleTraversals();
}
}
可以发现scheduleTraversals才是View真正的入口. 接着会通过WindowSession来完成Window的添加过程。windowSession的类型是IwindowSession,是一个binder对象。真正的实现类是Session。也就是Window的添加过程是一次IPC调用,在session内部会通过WindowManagerService来实现Window的添加。这样一来,Window的添加请求就交给了WindowManagerService去处理了。。。。其他删除,更新操作都差不多,可以自行查看。
Window创建
从上面可以学习到View依附于window上。Window的实现类是PhoneWindow.
1.window的创建是什么时候触发的? 要明白这个问题,我们就必须了解activity的启动过程。戳一戳前一篇Activity是如何启动的
从上篇文章我们会发现Activity启动最终会调用ActivityThread的performLaunchActivity方法,在performLaunchActivity中会通过类加载器从Instrumentation中获取Activity的信息来进行创建Activity的对象,度调用Activity中的attach方法关联运行过程中所依赖的一系列上下文环境变量。
在Activity的attach方法里系统会创建Activity所属的Window(phoneWindow)对象mWindow = new PhoneWindow(this, window, activityConfigCallback);
2. setConteView之后发生了什么
我们可以先看一个Activity的setContentView
public void setContentView(@LayoutRes int layoutResID) {
getWindow().setContentView(layoutResID);
initWindowDecorActionBar();
}
发现使用Activity将具体实现交给了Window处理。而Window的具体实现是phoneWindow,所以我们只需要看phoneWindow的相关逻辑即可。 PhoneWindow的setConteView方法大致分为以下几个步骤:
-
判断是否存在DecorView没有就调用generateDecor()进行创建DecorView,DecorView实现于Framelayout。
public class DecorView extends FrameLayout
-
调用generateLayout创建内容区域的布局参数
-
使用LayoutInflate解析布局文件并添加中decorView中
-
调用Window.CallBack中的onContentChanged通知Activity内容已经发生改变。(因为Activity实现了Window.CallBack)
经过上面的步骤,到这里为止DecorView已经创建完毕并且已经把我们创建的ContentView添加到DecorView中。但是这个时候DecorView还没有被WindowManager添加到Window中。最终会调用ActivityThread中的handlerResumeActivity方法。然后在handlerResumeActivity方法中再调用Activity的onResume和makeVisible使得Window显示出来。我们就可以操作UI了。