Android 窗口创建流程
添加一个 Window
不论是 Activity、Dialog、PopupWindow还是其他浮动窗口,屏幕上可以看到的一切窗口最终都是通过调用 WindowManager.addView(View view, ViewGroup.LayoutParams params)
方法添加的。我们在调查一个窗口的显示的时候只要找到调用WindowManager.addView()
的位置就可以了,这之前的流程可以在这里打印一个调用堆栈来跟踪。
这个 addView()
方法里有两个参数:
View view
View 树的根 View,也就是需要在窗口上显示的内容ViewGroup.LayoutParams params
窗口相关的参数,实际为WindowManager.LayoutParams
,包括显示的位置、大小、层级、焦点处理标记等
WindowManager
一般通过 context.getSystemService(Context.WINDOW_SERVICE)
或者 context.getSystemService(WindowManager.class)
获得,这里最终获得的是从 SystemServiceRegistry
创建的一个 WindowManagerImpl
的实例。而 WindowManagerImpl
里又创建了一个 WindowManagerGlobal
的实例。WindowManagerImpl
和 WindowManagerGlobal
都是单实例的,即每个进程只有一个实例。
// frameworks/base/core/java/android/app/SystemServiceRegistry.java
registerService(Context.WINDOW_SERVICE, WindowManager.class,
new CachedServiceFetcher<WindowManager>() {
@Override
public WindowManager createService(ContextImpl ctx) {
return new WindowManagerImpl(ctx);
}});
// frameworks/base/core/java/android/view/WindowManagerImpl.java
public final class WindowManagerImpl implements WindowManager {
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
}
addView()
方法的最终实现在 WindowManagerGlobal
里:
//frameworks/base/core/java/android/view/WindowManagerGlobal.java
public final class WindowManagerGlobal {
// 三个 ArrayList 保存着这个应用里所有窗口的 View 树、ViewRootImpl 以及 窗口参数 LayoutParams
private final ArrayList<View> mViews = new ArrayList<View>();
private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
private final ArrayList<WindowManager.LayoutParams> mParams
= new ArrayList<WindowManager.LayoutParams>();
...
/**
* 这里的几个参数:
* View view, view 树
* ViewGroup.LayoutParams params,窗口参数
* Display display, 确定这个窗口需要显示到哪个屏幕,默认为主屏幕
* Window parentWindow,父窗口
* Dialog、PopupWindow 等浮动窗口的父窗口为弹出它的Activity窗口,
* 从后台 Service 启动的窗口可以没有父窗口
*/
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
// wparams 会给窗口再附加一些属性
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
ViewRootImpl root;
// 如果将要添加的是子窗口,panelParentView 为父窗口的 View 树,用来参考设置该子窗口的显示
View panelParentView = null;
// 创建 ViewRootImpl
root = new ViewRootImpl(view.getContext(), display);
// 给 View 树关联窗口属性
view.setLayoutParams(wparams);
// 将 View 树、ViewRootImpl、以及窗口参数添加到各个列表里
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
// 将 View 树关联到 ViewRootImpl
root.setView(view, wparams, panelParentView);
}
}
之后的事情就都在 ViewRootImpl
里了,ViewRootImpl
的一些关键成员、方法如下面的代码:
public final class ViewRootImpl {
// 与 WindowManagerService 连接的会话
final IWindowSession mWindowSession;
// 保存该窗口显示的屏幕
Display mDisplay;
// 屏幕管理服务
final DisplayManager mDisplayManager;
// 该窗口所归属的应用包名
final String mBasePackageName;
// 创建该窗口的线程,一般为主线程
final Thread mThread;
// 保存窗口属性参数
public final WindowManager.LayoutParams mWindowAttributes = new WindowManager.LayoutParams();
// 应用端的窗口代表,后面介绍
final W mWindow;
// 该窗口要显示的 View 树
View mView;
// 如下两个成员表示保存该窗口的绘画所用的画布
public final Surface mSurface = new Surface();
private final SurfaceControl mSurfaceControl = new SurfaceControl();
// 记录该窗口是否第一次刷新