所有和Activity相关的Window初始化都在Activity的attach方法里面,该方法会在Activity被创建的时候执行
Window:窗体抽象类,主要实现对象是PhoneWindow,主要成员:
// 创建窗口默认会创建对应的窗体布局参数
private final WindowManager.LayoutParams mWindowAttributes =
new WindowManager.LayoutParams();
PhoneWindow:Window的主要实现类
ViewManager:View管理者接口,主要提供添加view,更新view,删除view的接口
public void addView(View view, ViewGroup.LayoutParams params);
public void updateViewLayout(View view, ViewGroup.LayoutParams params);
public void removeView(View view);
windowManager 接口,继承了ViewManager.
WindowManager.LayoutParams:Window窗体添加布局参数,父类是ViewGroup.LayoutParams
public LayoutParams() {
super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
type = TYPE_APPLICATION;//添加窗体的类型
format = PixelFormat.OPAQUE;
}
主要参数:type:窗体类型,主要有三种。应用窗体、子窗体、系统窗体;
Window 层级
应用 Window 1~99
子 Window 1000~1999
系统 Window 2000~2999
flags:这个主要是设置窗体的模式,我们看下一些常见的,具体参考
https://blog.csdn.net/zyjzyj2/article/details/53819964
public IBinder token = null;//Identifier for this window. 该窗口的身份验证口令,对应非系统窗口如果token不对,WMS会拒绝添加window,这也就是为什么dialog必须依附在Activity上
windowManagerImpl WindowManager的实现者,主要方法:
public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
return new WindowManagerImpl(mContext, parentWindow);//创建一个本地WindowManager,就是它自己
}
private WindowManagerImpl(Context context, Window parentWindow) {
mContext = context;
mParentWindow = parentWindow;//这个parentWindow非常重要,后面讲token的时候
}
所以在activity里面通过getWindowManager获取到的对象就是它。
WindowManager的创建:
我们都知道在调用activity的attach函数的时候,会创建phonewindow顺便会设置WindowManager
if (wm == null) {
wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
}
mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
如果wm为null,那么通过getSystemService获取到一个WindowManagerImpl,如果不为空,那么调用它的createLocalWindowManager方法创建一个
我们先看不为null的情况
public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
return new WindowManagerImpl(mContext, parentWindow);
}
该方法就是想刚刚创建的phoneWindow传入进来,新建一个WindowManagerImpl。
如果wm为null,那么会调用mContext的getSystemService方法获取一个。这里需要注意下这个mContext的类型
对应PhoneWindow来说,这个mContext是一个acticity,我们看下acivity里面的getSystemService方法
@Override
public Object getSystemService(@ServiceName @NonNull String name) {
if (getBaseContext() == null) {
throw new IllegalStateException(
"System services not available to Activities before onCreate()");
}
if (WINDOW_SERVICE.equals(name)) {
return mWindowManager;
} else if (SEARCH_SERVICE.equals(name)) {
ensureSearchManager();
return mSearchManager;
}
return super.getSystemService(name);
}
如果是一个windowservice,那么会直接返回创建好的windowManager,那么对于首次初始化,这里肯定是null。那么是怎么保证获取到一个不为null呢
我们看最开始的地方
mWindow.setWindowManager(
(WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
mToken, mComponent.flattenToString(),
(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
这里也是通过context获取,但是这里需要注意的是,这里的context是一个ContextImpl,我们看他的getSystemService方法
@Override
public Object getSystemService(String name) {
return SystemServiceRegistry.getSystemService(this, name);
}
这里通过一个叫做系统服务注册的类获取,这个类的作用就是注册所有的系统服务,并将这些服务保存在一个map里面,以便后面的获取
从上面的activity的getSystemService方法可以知道,获取其他的service会进入到super.getSystemService(name);这里面如果是获取
LayoutInflater,那么会创建返回,如果是获取其他的,那么就会进入到ContextImpl的getSystemService的方法里面
windowManagerGlobal WindowManager和WindowManagerService的中间桥梁,主要成员有:
private static WindowManagerGlobal sDefaultWindowManager;//它自己
private static IWindowManager sWindowManagerService;//WindowManagerService的代理实现类
private static IWindowSession sWindowSession;//Session的代理实现类,和WindowManagerService的沟通桥梁
private final ArrayList
private final ArrayList
private final ArrayList
new ArrayList
private final ArraySet
viewRootImpl,每个添加的Window所对应的根View,主要成员类
final IWindowSession mWindowSession;//Session的代理实现类,和WindowManagerService的沟通桥梁,采用Binder机制
mThread = Thread.currentThread();//当前线程对象,在添加View的时候会检查,如果创建ViewRoot的线程和后续添加View的线程不是同一个,
那么就会报错,这就是为什么非UI线程一般不给刷新UI的原因
mWindow = new W(this);//这个东西是一个Binder,是给WindowManager回调ViewRoot里面的方法的。代表着创建的Window对象
static class W extends IWindow.Stub {
private final WeakReference
private final IWindowSession mWindowSession;
...
}
mAttachInfo = new View.AttachInfo(mWindowSession, mWindow, display, this, mHandler, this);//绑定信息集合类,所有和Window相关的信息都在这里面,后续会分发到该Window内的各个View
final WindowManager.LayoutParams mWindowAttributes = new WindowManager.LayoutParams();需要添加窗体的参数
所以最终Window添加流程:WindowManager->WindowManagerImp->WindowManagerGlobal->ViewRootImpl->IWindowSession->Session->WMS
WindowSurfacePlacer WMS那边的绘制者
window的添加过程:
源头最开始的地方:ActivityThread的handleResumeActivity方法,当Activity创建成功,DecorView将我们的布局文件加载完成,我们开始将根View添加到Window中
wm.addView(decor, l);//通过WindowManager添加根View
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);//通过windowManagerGlobal添加
这里这个mParentWindow注意下,它就是Activity创建时候的PhoneWindow,表示添加的View要依附在它上面
在global的addView里面有这么一段很重要的代码
if (parentWindow != null) {
parentWindow.adjustLayoutParamsForSubWindow(wparams);
}
如果不是添加系统window,那么parent一定是不会为null的,我们进入adjustLayoutParamsForSubWindow这个方法里面看看
void adjustLayoutParamsForSubWindow(WindowManager.LayoutParams wp) {
CharSequence curTitle = wp.getTitle();
if (wp.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
wp.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
if (wp.token == null) {
View decor = peekDecorView();
if (decor != null) {
wp.token = decor.getWindowToken();
}
}
...
} else if (wp.type >= WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW &&
wp.type <= WindowManager.LayoutParams.LAST_SYSTEM_WINDOW) {
// We don't set the app token to this system window because the life cycles should be
// independent. If an app creates a system window and then the app goes to the stopped
// state, the system window should not be affected (can still show and receive input
// events).
if (curTitle == null || curTitle.length() == 0) {
final StringBuilder title = new StringBuilder(32);
title.append("Sys").append(wp.type);
if (mAppName != null) {
title.append(":").append(mAppName);
}
wp.setTitle(title);
}
} else {
if (wp.token == null) {
wp.token = mContainer == null ? mAppToken : mContainer.mAppToken;
}
if ((curTitle == null || curTitle.length() == 0)
&& mAppName != null) {
wp.setTitle(mAppName);
}
}
if (wp.packageName == null) {
wp.packageName = mContext.getPackageName();
}
if (mHardwareAccelerated ||
(mWindowAttributes.flags & FLAG_HARDWARE_ACCELERATED) != 0) {
wp.flags |= FLAG_HARDWARE_ACCELERATED;
}
}
这个方法主要做了三点,如果不是系统窗口,赋值token,这个token是后续和WMS交互的身份验证口令,如果是则不需要token。
打开硬件加速,赋值包名
接下来判断该View是否添加过,如果添加过,那么移除后在添加。如果是添加子View,找到对应的父View
int index = findViewLocked(view, false);
if (index >= 0) {
if (mDyingViews.contains(view)) {
// Don't wait for MSG_DIE to make it's way through root's queue.
mRoots.get(index).doDie();
} else {
throw new IllegalStateException("View " + view
+ " has already been added to the window manager.");
}
// The previous removeView() had not completed executing. Now it has.
}
// If this is a panel window, then find the window it is being
// attached to for future reference.
if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
final int count = mViews.size();
for (int i = 0; i < count; i++) {
if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
panelParentView = mViews.get(i);
}
}
}
一切准备就绪之后,创建对应的对象,然后通过ViewRoot添加View
root = new ViewRootImpl(view.getContext(), display);
view.setLayoutParams(wparams);
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
try {
root.setView(view, wparams, panelParentView);
}
进入到ViewRootImpl的setView方法
首先调用mWindowAttributes.copyFrom(attrs); 复制窗体布局参数
requestLayout();调用该方法,准备异步绘制View到Window上
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(),
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mInputChannel);
通过session将window添加到WMS,为异步添加准备,如果添加失败,那么取消绘制任务
if (res < WindowManagerGlobal.ADD_OKAY) {
unscheduleTraversals();
...
}
void unscheduleTraversals() {
if (mTraversalScheduled) {
mTraversalScheduled = false;
mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
mChoreographer.removeCallbacks(
Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
}
}
WindowManagerService部分:
关键对象:
/**
* All currently active sessions with clients.所有客户端的session对象集合
*/
final ArraySet
final HashMap
final HashMap
WindowToken和AppWindowToken:
WindowToken的成员变量token是IBinder对象,具有系统唯一性。因此,向WMS的mWindowMap或者mTokenMap插入对象时都是使用token值作为索引。
APPWindowToken从WindowToken类派生,是一种比较特殊的WindowToken,代表应用窗口,主要是Activity中创建的顶层窗口.
一个WindowToken对象的成员变量APPWindowToken如果为NULL,那么它就不是APPWindowToken,否则就是APPWindowToken对象。
APPWindowToken中的appToken用来表示应用token,它在AMS中创建,表示一个应用。它也是一个binder
这两个token中都有一个WindowList,代表保存了所有相同APPWindowToken的应用窗口及其子窗口
客户端调用了addToDisplay方法之后,最终会调用WSM的addWindow方法:
1、参数检查,进入到这个方法之后首先进行参数检查,分为系统窗口、应用窗口和子窗口的检查;
如果已经添加过窗口,那么不在添加
if (mWindowMap.containsKey(client.asBinder())) {
Slog.w(TAG_WM, "Window " + client + " is already added");
return WindowManagerGlobal.ADD_DUPLICATE_ADD;
}
如果是子窗口,那么必须存在父窗口切父窗口不能是子窗口类型
if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) {
attachedWindow = windowForClientLocked(null, attrs.token, false);
if (attachedWindow == null) {
Slog.w(TAG_WM, "Attempted to add window with token that is not a window: "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
}
if (attachedWindow.mAttrs.type >= FIRST_SUB_WINDOW
&& attachedWindow.mAttrs.type <= LAST_SUB_WINDOW) {
Slog.w(TAG_WM, "Attempted to add window with token that is a sub-window: "
+ attrs.token + ". Aborting.");
return WindowManagerGlobal.ADD_BAD_SUBWINDOW_TOKEN;
}
}
WindowToken token = mTokenMap.get(attrs.token);//根据appToken获取windowToken
如果是系统窗口,那么token为null,窗口类型是应用窗口,输入法窗口,壁纸,Dream则退出,别的窗口则创建token,注意此时创建的token是没有appWindowToken的
如果是应用窗口,那么获取到token,检查其他匹配规则。如果不是应用窗口而appWindowToken又不为null,那么重新创建WindowToken。
2、创建窗口对象:
WindowState win = new WindowState(this, session, client, token,
attachedWindow, appOp[0], seq, attrs, viewVisibility, displayContent);
if (addToken) {
mTokenMap.put(attrs.token, token);
}如果是新的token,加入到map中
win.attach();将窗口添加记录。将session添加进入到sessions集合中,session中窗体数增加一
mWindowMap.put(client.asBinder(), win);将创建好的窗口对象也存放入map中,以viewRoot的IWindow为key
Session对象中重要变量:
mSurfaceSession表示一个SurfaceSession对象,这个SurfaceSession对象是WindowManagerService服务用来与SurfaceSession服务建立连接的
SurfaceSession创建后会建立一个SurfaceComposerClient对象,可以用来和SurfaceFlinger服务通信,请求SurfaceComposerClient创建绘制表面(Surface)
mNumWindow:用来描述当前正在处理的Session对象内部包含有多少个窗口
3、将窗口添加在display上
if (type == TYPE_INPUT_METHOD) {
win.mGivenInsetsPending = true;
mInputMethodWindow = win;
addInputMethodWindowToListLocked(win);
imMayMove = false;
} else if (type == TYPE_INPUT_METHOD_DIALOG) {
mInputMethodDialogs.add(win);
addWindowToListInOrderLocked(win, true);
moveInputMethodDialogsLocked(findDesiredInputMethodWindowIndexLocked(true));
imMayMove = false;
} else {
addWindowToListInOrderLocked(win, true);
if (type == TYPE_WALLPAPER) {
mWallpaperControllerLocked.clearLastWallpaperTimeoutTime();
displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
} else if ((attrs.flags&FLAG_SHOW_WALLPAPER) != 0) {
displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
} else if (mWallpaperControllerLocked.isBelowWallpaperTarget(win)) {
// If there is currently a wallpaper being shown, and
// the base layer of the new window is below the current
// layer of the target window, then adjust the wallpaper.
// This is to avoid a new window being placed between the
// wallpaper and its target.
displayContent.pendingLayoutChanges |= FINISH_LAYOUT_REDO_WALLPAPER;
}
}
以应用窗口为例,具体可以查看addWindowToListInOrderLocked->addAppWindowToListLocked
4、接下来的代码就是调整窗口顺序,然后将添加窗口是否成功的状态码res返回
Window添加过程的身份检验:
关键对象: static class Token extends IApplicationToken.Stub {}
Token(ActivityRecord activity, ActivityManagerService service) {
weakActivity = new WeakReference<>(activity);
mService = service;
}
在ActivityStarter.startActivityLocked方法里面,会新建一个ActivityRecord
ActivityRecord r = new ActivityRecord(mService, callerApp, callingUid, callingPackage,
intent, resolvedType, aInfo, mService.mConfiguration, resultRecord, resultWho,
requestCode, componentSpecified, voiceSession != null, mSupervisor, container,
options, sourceRecord);
在构造函数里面,会创建这个Token
appToken = new Token(this, service);
接着会执行startActivityUnchecked->setInitialState
在setInitialState方法里面会将新建的ActivityRecord赋值给mStartActivity变量
然后会调用mTargetStack.startActivityLocked(mStartActivity, newTask, mKeepCurTransition, mOptions);方法进入到ActivityStack里面
在这里面会执行一个addConfigOverride(r, task);方法,会将token传递给WMS;
mWindowManager.addAppToken(task.mActivities.indexOf(r), r.appToken,
r.task.taskId, mStackId, r.info.screenOrientation, r.fullscreen,
(r.info.flags & FLAG_SHOW_FOR_ALL_USERS) != 0, r.userId, r.info.configChanges,
task.voiceSession != null, r.mLaunchTaskBehind, bounds, task.mOverrideConfig,
task.mResizeMode, r.isAlwaysFocusable(), task.isHomeTask(),
r.appInfo.targetSdkVersion, r.mRotationAnimationHint);
r.taskConfigOverride = task.mOverrideConfig;
在WMS的addAppToken方法里面,会新建一个AppWindowToken,代表一个新的应用要建立窗体,然后将其存放入
HashMap