自由笔记-AndroidView模块之WindowManager相关分析

所有和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<View> mViews = new ArrayList<View>();Window 所对应的 View

private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();Window 所对应的 ViewRootImpl

private final ArrayList<WindowManager.LayoutParams> mParams =

new ArrayList<WindowManager.LayoutParams>();Window 所对应的布局参数

private final ArraySet<View> mDyingViews = new ArraySet<View>();正在被删除的 View 对象

 

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<ViewRootImpl> mViewAncestor;

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<Session> mSessions = new ArraySet<>();//Session:它是客户端连接WMS的桥梁,在WindowManagerGlobal里面创建

final HashMap<IBinder, WindowState> mWindowMap = new HashMap<>();//所有窗口的WindowState对象。

final HashMap<IBinder, WindowToken> mTokenMap = new HashMap<>();//所有窗口的WindowToken对象

 

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<IBinder, WindowToken> mTokenMap 中。在之前说到的添加窗体的时候,会根据传入过来的token.asBinder,去获取对应的AppWindowToken来校验身份

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章