本文仅作为笔者自己的记录,后有贴原文链接4
Window
window是什么
window机制就是为了管理屏幕上的view的显示以及触摸事件的传递问题。 view数是window机制的操作单位,每一个view树对应一个window,view树是window的存在形式,window是view的载体。我们看到的都不是window而是view树,window是view树的载体,是view树的管理者。 window在源码中的存在形式,大概就是windowManagerService中每个view对应一个windowStatus。 window属性的常量值大部分存储在WindowManager.LayoutParams类中,我们可以通过这个类来获得这些常量,当然还有Gravity类和PixelFormat类等
window的添加过程,指的是通过WindowManagerImpl的addView方法来添加window的过程。想要添加一个window,我们知道首先得有view和WindowManager.LayoutParams对象,才能去创建window。
Button button = new Button(this);
WindowManager.LayoutParams windowParams = new WindowManager.LayoutParams();
// 这里对windowParam进行初始化
windowParam.addFlags...
// 获得应用PhoneWindow的WindowManager对象进行添加window
getWindowManager.addView(button,windowParams);
接下来进入addView方法中看看。windowManager的实现类是WindowManagerImpl。
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
可以看到addView()方法中把逻辑交给了mGlobal进行处理,mGlobal是WindowMangerGlobal,是一个全局单例,是WindowManager接口的具体逻辑实现。
public void addView(View view, ViewGroup.LayoutParams params, Display display, Window parentWindow) {
// 首先判断参数是否合法
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");
}
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
// 如果不是子窗口,会对其做参数的调整
if (parentWindow != null) {
parentWindow.adjustLayoutParamsForSubWindow(wparams);
} else {
final Context context = view.getContext();
if (context != null && (context.getApplicationInfo().flags & ApplicationInfo.FLAG_HARDWARE_ACCELERATED) != 0) {
wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
}
}
synchronized (mLock) {
...
// 这里新建了一个viewRootImpl,并设置参数
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
// 添加到windowManagerGlobal的三个重要list中,后面会讲到
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
// 最后通过viewRootImpl来添加window
try {
root.setView(view, wparams, panelParentView);
}
...
}
}
总给做的工作,分为以下几步:
- 首先对参数进行合法性检查
- 检查是否是子view,如果是的话需要对窗口进行调整
- 新建ViewRootImpl对象,并把view,viewRootImpl、params三个对象添加到list中进行保存
- 最后通过viewRootImpl来进行添加
在WindowManagerGlobal中有三个List,分别是:
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>();
每个window对应的三个对象都会保存在这里,之后对windiw的一些操作就可以直接来这里取对象了。当window被删除的时候,这些对象也会被从list中移除。
window的添加的逻辑交给了ViewRootImpl,viewRootImpl是window和view之间的桥梁,viewRootImpl可以处理两边的对象,然后联结起来。
ViewRoot怎么处理:
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
...
try {
mOrigWindowType = mWindowAttributes.type;
mAttachInfo.mRecomputeGlobalAttributes = true;
collectViewAttributes();
// 这里调用了windowSession的方法,调用wms的方法,把添加window的逻辑交给wms
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame,
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel,
mTempInsets);
setFrame(mTmpFrame);
}
...
}
}
viewRootImpl的逻辑很多,重要的是调用了mWindowSession的方法调用了WMS的方法。
mWindowSession是一个IWindowSession对象,看到这个命名很快可以想到这里用了AIDL跨进程通信。IWindowSession是一个IBinder接口,他的具体实现类在WindowManagerService,本地的mWindowSession只是一个Binder对象,通过这个mWindowSession可以调用WMS的方法进行跨进程通信。 mWindowSession来自于ViewRootImpl的构造方法:
public ViewRootImpl(Context context, Display display) {
...
mWindowSession = WindowManagerGlobal.getWindowSession();
...
}
可以看到session来自windowManagerGloval,再深入看一下
```java
public static IWindowSession getWindowSession() {
synchronized (WindowManagerGlobal.class) { if (sWindowSession == null) { try { ... sWindowSession = windowManager.openSession( new IWindowSessionCallback.Stub() { ... }); } ... } return sWindowSession; } }
session是一个单例,也就是说整个一个用的所有viewRootImpl的windowSession都是同一个,也就是一个应用只有一个windowSession。对于wms而言,他是服务于多个应用的,如果说每个viewRootImpl整个session,那么WMS的任务就过于繁重。WMS的对象单位应该是应用,他在内部给每个应用Session分配了一些数据结构,如list,用于保存每个应用的window以及对应的viewRootImpl。当需要操作view的时候,通过session直接找到viewRootImpl就可以操作了。
后面的逻辑就是WMS处理,WMS会创建window,然后结合参数计算window的高度等等,最后使用viewRootImpl进行绘制。 WindowManager是继承ViewManager接口的,viewManager还有另外两个接口:removeView、updateView。
总结
window的添加过程是通过PhoneWindow对应WindowManagerImpl来添加window,内部会调用windowManagerGlobal来实现。WindowManagerGlobal会使用ViewRootImpl来进行跨进程通信,让WMS执行创建Window的业务。每个应用都有一个WindowSession,用于负责和WMS的通信,如ApplicationThread与AMS的通信。
相关的类
Window的实现只有一个:PhoneWindow,他继承自Window抽象类
WindowManager
就是window的管理类。关键类:windowManager,ViewManager,WindowManagerImpl,WindowManagerGlobal。 windowManager是一个接口,继承自viewManager。ViewManager中包含三个熟悉的接口,addView(),removeView(),updateView()。 WindowManagerImpl和PhoneWindow是成对出现,前者负责管理后者。WindowManagerImpl是WindowManager的实现类,但是他并没有真正实现WindowManager的逻辑,而是交给了WindowManagerGlobal,在windowManagerImpl的内部使用了桥接模式,windowManagerGlobal是全局单例,也是WindowManager的逻辑的真正实现。
view相关
有一个重要的ViewRootImpl,每个view树都会有一个。当我使用WindowManager的addView时,就会创建一个ViewRootImpl。ViewRootImpl的作用很关键:
- 负责连接view和window的桥梁事务
- 负责与windowManagerService的事务
- 负责管理和绘制view树
- 事件的中转站
每个window都会有一个viewRootImpl,ViewRootImpl是负责绘制这个view树和window与view的桥梁。
WindowManagerService
是window的真正的管理者,类似于AMS。所有window创建最终都要经过WMS。整个Android中,WMS是处于绝对的核心地位,他决定了屏幕所有的window该如何显示如何分发点击事件等等。