WindowManagerService的理解

2024-06-05 16:18

本文主要是介绍WindowManagerService的理解,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

Android中的窗口概念:屏幕上一块用于绘制各种UI元素并可以响应用户输入的一块矩形区域。从原理上来看,窗口的概念是独自占有一个Surface实例的显示区域。如Dialog、Activity的界面、壁纸、状态栏以及Toast等都是窗口。 
上述对窗口的概念性描述,提取出来主要有以下几点信息:

  • 用于绘制UI元素

  • 响应用户输入

  • 独自占有一个surface实例

    • Surface是一块画布,应用通过canvas或者openGL在上面作画
    • 作画后通过SurfaceFlinger将多块Surface的内容按照Z-order进行混合并输出到FrameBuffer,从而将Android的页面显示给用户。

每个窗口都有一块Surface用于显示自己的ui,必然需要一个角色对窗口进行统一管理,这个时候,WMS应运而生。WMS为所有窗口分配Surface,掌管z-order以及位置、尺寸、窗口的进场出场动画,并且还是输入系统的中转站。本文的分析角度:

  • 布局系统:计算管理窗口的位置和层次
  • 动画系统:根据布局系统的计算渲染窗口动画

一个窗口的创建流程大概如下: 
1. 客户端通过WindowManagerGlobal获取IWindowSession,通过IWindowSession来向WMS发起添加请求。 
2. 初始化WindowManager.LayoutParam,指定响应的type,当type大于System级别的时候,不需要token,WMS会创建一个token,而type小于system级别的窗口,是必须要求有token带过来给WMS的。WMS根据这个type来对窗口的z-order进行排序。 
3. 向was申请relayout,所谓重新布局,是根据窗口新的属性去调整其Surface的相关属性,或者重新创建一个Surface。向wms添加一个窗口,仅仅是将它在wms中进行注册。只有经过了relayout后,窗口才拥有了wms为其分配的画布,有了画布,窗口才能进行绘制工作。 
4. 之后,窗口的绘制过程如下: 
- 通过surface.lock()函数获取可以在surface上作画的canvas 
- 使用canvas进行绘图 
- 通过unlockCanvasAndPost提交绘制结果

说到底,Window的本质其实就是一块Surface画布. 
于是,根据对Surface的操作类型可以将Android的显示系统分为3个层次。 
1. UI框架层,负责View空间的布局、绘制、事件分发、响应 
2. WMS管理窗口Surface的布局与次序 
3. SurfaceFlinger 将WMS维护的窗口Surface按照一定的次序混合后显示到屏幕上。

说了那么多,我们来看看窗口的核心管理WMS的构成。 
- WMS也属于系统服务,都是由SystemServer来启动,代码位于SystemServer.java中:

//这里需要加入SystemServer启动WMS的相关代码,最后进入到WMS.Main函数中
  • 1
//可以看到,SystemServer启动WMS的过程是将WSM的构造函数所需要的相关参数传入main方法//由main方法来调用HandlerThread的handler将构造函数丢入looper中进行wms的实例化public static WindowManagerService main(final Context context,final InputManagerService im,final boolean haveInputMethods, final boolean showBootMsgs,final boolean onlyCore) {final WindowManagerService[] holder = new WindowManagerService[1];//那么这里的runWithScissor到底起了什么作用呢?DisplayThread.getHandler().runWithScissors(new Runnable() {@Overridepublic void run() {holder[0] = new WindowManagerService(context, im,haveInputMethods, showBootMsgs, onlyCore);}}, 0);return holder[0];}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
//这个方法位于android.os.Handler.java类,是一个@hide方法
//同时这个方法是一个阻塞的方法,会阻塞目前县城直到返回,这也就
//说,return的时候在wms线程会被此方法阻塞,直到wms完成实例化
//,也可以理解为,main方法return后,holder可能仍然为null,
//但是wms线程中,所有消息都需要等待这个实例化完成
public final boolean runWithScissors(final Runnable r, long timeout) {if (r == null) {throw new IllegalArgumentException("runnable must not be null");}if (timeout < 0) {throw new IllegalArgumentException("timeout must be non-negative");}//由于 wms 的 Main()方法调用//如果调用此方法的looper和handler的looper相同,则直接运行返回if (Looper.myLooper() == mLooper) {r.run();return true;}//如果不是则新建一个阻塞的runnable来进入msgqueue里面BlockingRunnable br = new BlockingRunnable(r);return br.postAndWait(this, timeout);}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

接下来,看wms的构造函数,所有参数都是SystemServer传入:

 private WindowManagerService(Context context, InputManagerService inputManager,boolean haveInputMethods, boolean showBootMsgs, boolean onlyCore) {mContext = context;mHaveInputMethods = haveInputMethods;mAllowBootMessages = showBootMsgs;mOnlyCore = onlyCore;...//保存了用于接受输入事件的inputManagermInputManager = inputManager; // Must be before createDisplayContentLocked....//保存屏幕相关信息mDisplayManager = (DisplayManager)context.getSystemService(Context.DISPLAY_SERVICE);mDisplays = mDisplayManager.getDisplays();for (Display display : mDisplays) {createDisplayContentLocked(display);}...//掌管所有window的动画mAnimator = new WindowAnimator(this);//初始化WindowManagerPolicy成员initPolicy();// Add ourself to the Watchdog monitors.Watchdog.getInstance().addMonitor(this);SurfaceControl.openTransaction();try {createWatermarkInTransaction();} finally {SurfaceControl.closeTransaction();}showEmulatorDisplayOverlayIfNeeded();}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35

至此,WMS的初始化工作已经完成,那么初始化工作有哪些重要的成员变量被初始化呢?

  • mInputManager : 输入系统服务,用于管理每个窗口的输入事件管道(InputManager)以及向通道上派发事件。
  • mChoreographer ,从显示子系统获取VSYNC同步事件,从而来在合适的实际通知渲染动作,避免在渲染的过程中因为发生屏幕重绘而导致画面撕裂。WMS使用这个对象驱动所有的窗口动画,屏幕旋转动画,墙纸动画的渲染。
  • mAnimator,WindowAnimator实例,在Choreographer的驱动下逐个渲染所有动画。
  • mPolicy,WindowManagerPolicy的实现,目前只有PhoneWindowManager一个实现类。mPolicy定义了很多窗口相关的策略,是wms的首席顾问,当WMS要做任何事情的时候,都会向policy求助。例如,过塑wms某一个类型的window 的zOrder是多少,帮助wms矫正不合理的窗口属性,为wms监听屏幕旋转状态。预处理一些系统按键事件,例如(home back键都在这里实现默认行为),所以mPolicy是一个非常重要的成员变量。
  • mDisplayContents ,一个DisplayContent列表,4.2支持基于Wi-Fi Display的多屏幕输出,而一个DisplayContent描述了一块可以绘制窗口的屏幕。每个DisplayContent都用一个整形变量做为其ID。
  • mTokenMap,一个hash map,保存了所有显示令牌(类型为WindowToken),用于窗口管理。一个窗口必须有一个显示令牌,mAppTokens保存了所有的Activity令牌(WindowToken的子类AppWindowToken),mExitingTokens则保存了正在退出过程中的显示令牌,其中mAppTokens列表是有序的,他与AMS中的mHistory的顺序保持一致,反应了系统中activity的顺序。
  • mWindowMap,也是一个HashMap,保存了所有窗口的状态信息,用于窗口管理。状态信息的类型为WindowState。实际上,所有被add过的window的状态都会保存在这个map中,与mTokenMap一样,mWindowMap也有衍生的子集,mPendingRemove保存了退出动画播放完成即将被移除的窗口,mLosingFocus保存了失去输入焦点的窗口。在DisplayContent中,也有一个window列表,这个列表以order为顺序存储了所有window。
  • mSessions,一个List,元素为Session,这个列表保存了所有当前向WMS发起请求的客户端进程唯一的IWindowSession对象,注意,这里的Session也是进程唯一的。
  • mRotation,是一个int变量。保存了当前手机的旋转状态。

接下来,我们从WindowManagerService的addWindow()函数开始讨论WMS的工作流程:

public int addWindow(Session session, IWindow client, int seq,WindowManager.LayoutParams attrs, int viewVisibility, int displayId,Rect outContentInsets, Rect outStableInsets, Rect outOutsets,InputChannel outInputChannel) {int[] appOp = new int[1];//首先,检查权限int res = mPolicy.checkAddPermission(attrs, appOp);if (res != WindowManagerGlobal.ADD_OKAY) {return res;}boolean reportNewConfig = false;//当为某个窗口添加子窗口的时候,attachedWindow用来保存父窗口的实例WindowState attachedWindow = null;long origId;final int type = attrs.type;synchronized(mWindowMap) {if (!mDisplayReady) {throw new IllegalStateException("Display has not been initialialized");}// 获取窗口要添加的displayContent// 在添加窗口时,必须的通过displayId参数指定添加哪个DisplayContent,如果没有指定,session会替Window添加DefaultDisplay// 也就是手机屏幕final DisplayContent displayContent = getDisplayContentLocked(displayId);if (displayContent == null) {Slog.w(TAG_WM, "Attempted to add window to a display that does not exist: "+ displayId + ".  Aborting.");return WindowManagerGlobal.ADD_INVALID_DISPLAY;}if (!displayContent.hasAccess(session.mUid)) {Slog.w(TAG_WM, "Attempted to add window to a display for which the application "+ "does not have access: " + displayId + ".  Aborting.");return WindowManagerGlobal.ADD_INVALID_DISPLAY;}//已经添加了窗口if (mWindowMap.containsKey(client.asBinder())) {Slog.w(TAG_WM, "Window " + client + " is already added");return WindowManagerGlobal.ADD_DUPLICATE_ADD;}// 如果是type是子窗口if (type >= FIRST_SUB_WINDOW && type <= LAST_SUB_WINDOW) {//检查添加窗口的父窗口是否为nullattachedWindow = 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;}}if (type == TYPE_PRIVATE_PRESENTATION && !displayContent.isPrivate()) {Slog.w(TAG_WM, "Attempted to add private presentation window to a non-private display.  Aborting.");return WindowManagerGlobal.ADD_PERMISSION_DENIED;}boolean addToken = false;//从tokenMap中取出已经注册的tokenWindowToken token = mTokenMap.get(attrs.token);AppWindowToken atoken = null;//如果看看已经注册的token没有if (token == null) {//如果是application 级别 activityif (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) {Slog.w(TAG_WM, "Attempted to add application window with unknown token "+ attrs.token + ".  Aborting.");return WindowManagerGlobal.ADD_BAD_APP_TOKEN;}if (type == TYPE_INPUT_METHOD) {Slog.w(TAG_WM, "Attempted to add input method window with unknown token "+ attrs.token + ".  Aborting.");return WindowManagerGlobal.ADD_BAD_APP_TOKEN;}if (type == TYPE_VOICE_INTERACTION) {Slog.w(TAG_WM, "Attempted to add voice interaction window with unknown token "+ attrs.token + ".  Aborting.");return WindowManagerGlobal.ADD_BAD_APP_TOKEN;}if (type == TYPE_WALLPAPER) {Slog.w(TAG_WM, "Attempted to add wallpaper window with unknown token "+ attrs.token + ".  Aborting.");return WindowManagerGlobal.ADD_BAD_APP_TOKEN;}if (type == TYPE_DREAM) {Slog.w(TAG_WM, "Attempted to add Dream window with unknown token "+ attrs.token + ".  Aborting.");return WindowManagerGlobal.ADD_BAD_APP_TOKEN;}if (type == TYPE_QS_DIALOG) {Slog.w(TAG_WM, "Attempted to add QS dialog window with unknown token "+ attrs.token + ".  Aborting.");return WindowManagerGlobal.ADD_BAD_APP_TOKEN;}if (type == TYPE_ACCESSIBILITY_OVERLAY) {Slog.w(TAG_WM, "Attempted to add Accessibility overlay window with unknown token "+ attrs.token + ".  Aborting.");return WindowManagerGlobal.ADD_BAD_APP_TOKEN;}//如果说以上的情况,都new一个token,最后一个参数是false,表明是wms创建的tokentoken = new WindowToken(this, attrs.token, -1, false);//说明token是临时创建的addToken = true;//如果说token不为null,则说明已经被注册过了 ,看一下activity级别的讨论,这里要求级别也是application的//并且atoken是appWindowToken} else if (type >= FIRST_APPLICATION_WINDOW && type <= LAST_APPLICATION_WINDOW) {atoken = token.appWindowToken;if (atoken == null) {Slog.w(TAG_WM, "Attempted to add window with non-application token "+ token + ".  Aborting.");return WindowManagerGlobal.ADD_NOT_APP_TOKEN;} else if (atoken.removed) {Slog.w(TAG_WM, "Attempted to add window with exiting application token "+ token + ".  Aborting.");return WindowManagerGlobal.ADD_APP_EXITING;}if (type == TYPE_APPLICATION_STARTING && atoken.firstWindowDrawn) {// No need for this guy!if (DEBUG_STARTING_WINDOW || localLOGV) Slog.v(TAG_WM, "**** NO NEED TO START: " + attrs.getTitle());return WindowManagerGlobal.ADD_STARTING_NOT_NEEDED;}} else if (type == TYPE_INPUT_METHOD) {if (token.windowType != TYPE_INPUT_METHOD) {Slog.w(TAG_WM, "Attempted to add input method window with bad token "+ attrs.token + ".  Aborting.");return WindowManagerGlobal.ADD_BAD_APP_TOKEN;}} else if (type == TYPE_VOICE_INTERACTION) {if (token.windowType != TYPE_VOICE_INTERACTION) {Slog.w(TAG_WM, "Attempted to add voice interaction window with bad token "+ attrs.token + ".  Aborting.");return WindowManagerGlobal.ADD_BAD_APP_TOKEN;}} else if (type == TYPE_WALLPAPER) {if (token.windowType != TYPE_WALLPAPER) {Slog.w(TAG_WM, "Attempted to add wallpaper window with bad token "+ attrs.token + ".  Aborting.");return WindowManagerGlobal.ADD_BAD_APP_TOKEN;}} else if (type == TYPE_DREAM) {if (token.windowType != TYPE_DREAM) {Slog.w(TAG_WM, "Attempted to add Dream window with bad token "+ attrs.token + ".  Aborting.");return WindowManagerGlobal.ADD_BAD_APP_TOKEN;}} else if (type == TYPE_ACCESSIBILITY_OVERLAY) {if (token.windowType != TYPE_ACCESSIBILITY_OVERLAY) {Slog.w(TAG_WM, "Attempted to add Accessibility overlay window with bad token "+ attrs.token + ".  Aborting.");return WindowManagerGlobal.ADD_BAD_APP_TOKEN;}} else if (type == TYPE_QS_DIALOG) {if (token.windowType != TYPE_QS_DIALOG) {Slog.w(TAG_WM, "Attempted to add QS dialog window with bad token "+ attrs.token + ".  Aborting.");return WindowManagerGlobal.ADD_BAD_APP_TOKEN;}} else if (token.appWindowToken != null) {Slog.w(TAG_WM, "Non-null appWindowToken for system window of type=" + type);// It is not valid to use an app token with other system types; we will// instead make a new token for it (as if null had been passed in for the token).attrs.token = null;token = new WindowToken(this, null, -1, false);addToken = true;}//这个windowState维护了所有窗口的信息WindowState win = new WindowState(this, session, client, token,attachedWindow, appOp[0], seq, attrs, viewVisibility, displayContent);if (win.mDeathRecipient == null) {// Client has apparently died, so there is no reason to// continue.Slog.w(TAG_WM, "Adding window client " + client.asBinder()+ " that is dead, aborting.");return WindowManagerGlobal.ADD_APP_EXITING;}if (win.getDisplayContent() == null) {Slog.w(TAG_WM, "Adding window to Display that has been removed.");return WindowManagerGlobal.ADD_INVALID_DISPLAY;}//windowPolicy粉墨登场mPolicy.adjustWindowParamsLw(win.mAttrs);win.setShowToOwnerOnlyLocked(mPolicy.checkShowToOwnerOnly(attrs));res = mPolicy.prepareAddWindowLw(win, attrs);if (res != WindowManagerGlobal.ADD_OKAY) {return res;}final boolean openInputChannels = (outInputChannel != null&& (attrs.inputFeatures & INPUT_FEATURE_NO_INPUT_CHANNEL) == 0);if  (openInputChannels) {win.openInputChannel(outInputChannel);}// From now on, no exceptions or errors allowed!res = WindowManagerGlobal.ADD_OKAY;if (excludeWindowTypeFromTapOutTask(type)) {displayContent.mTapExcludedWindows.add(win);}origId = Binder.clearCallingIdentity();//刚才被wms临时创建的token被加入到tokenMap中if (addToken) {mTokenMap.put(attrs.token, token);}win.attach();//然后将WindowState加入到map中mWindowMap.put(client.asBinder(), win);if (win.mAppOp != AppOpsManager.OP_NONE) {int startOpResult = mAppOps.startOpNoThrow(win.mAppOp, win.getOwningUid(),win.getOwningPackage());if ((startOpResult != AppOpsManager.MODE_ALLOWED) &&(startOpResult != AppOpsManager.MODE_DEFAULT)) {win.setAppOpVisibilityLw(false);}}if (type == TYPE_APPLICATION_STARTING && token.appWindowToken != null) {token.appWindowToken.startingWindow = win;if (DEBUG_STARTING_WINDOW) Slog.v (TAG_WM, "addWindow: " + token.appWindowToken+ " startingWindow=" + win);}boolean imMayMove = true;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;}}// If the window is being added to a task that's docked but non-resizeable,// we need to update this new window's scroll position when it's added.win.applyScrollIfNeeded();// If the window is being added to a stack that's currently adjusted for IME,// make sure to apply the same adjust to this new window.win.applyAdjustForImeIfNeeded();if (type == TYPE_DOCK_DIVIDER) {getDefaultDisplayContentLocked().getDockedDividerController().setWindow(win);}final WindowStateAnimator winAnimator = win.mWinAnimator;winAnimator.mEnterAnimationPending = true;winAnimator.mEnteringAnimation = true;// Check if we need to prepare a transition for replacing window first.if (atoken != null && !prepareWindowReplacementTransition(atoken)) {// If not, check if need to set up a dummy transition during display freeze// so that the unfreeze wait for the apps to draw. This might be needed if// the app is relaunching.prepareNoneTransitionForRelaunching(atoken);}if (displayContent.isDefaultDisplay) {final DisplayInfo displayInfo = displayContent.getDisplayInfo();final Rect taskBounds;if (atoken != null && atoken.mTask != null) {taskBounds = mTmpRect;atoken.mTask.getBounds(mTmpRect);} else {taskBounds = null;}if (mPolicy.getInsetHintLw(win.mAttrs, taskBounds, mRotation,displayInfo.logicalWidth, displayInfo.logicalHeight, outContentInsets,outStableInsets, outOutsets)) {res |= WindowManagerGlobal.ADD_FLAG_ALWAYS_CONSUME_NAV_BAR;}} else {outContentInsets.setEmpty();outStableInsets.setEmpty();}if (mInTouchMode) {res |= WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE;}if (win.mAppToken == null || !win.mAppToken.clientHidden) {res |= WindowManagerGlobal.ADD_FLAG_APP_VISIBLE;}mInputMonitor.setUpdateInputWindowsNeededLw();boolean focusChanged = false;if (win.canReceiveKeys()) {focusChanged = updateFocusedWindowLocked(UPDATE_FOCUS_WILL_ASSIGN_LAYERS,false /*updateInputWindows*/);if (focusChanged) {imMayMove = false;}}if (imMayMove) {moveInputMethodWindowsIfNeededLocked(false);}mLayersController.assignLayersLocked(displayContent.getWindowList());// Don't do layout here, the window must call// relayout to be displayed, so we'll do it there.if (focusChanged) {mInputMonitor.setInputFocusLw(mCurrentFocus, false /*updateInputWindows*/);}mInputMonitor.updateInputWindowsLw(false /*force*/);if (localLOGV || DEBUG_ADD_REMOVE) Slog.v(TAG_WM, "addWindow: New client "+ client.asBinder() + ": window=" + win + " Callers=" + Debug.getCallers(5));if (win.isVisibleOrAdding() && updateOrientationFromAppTokensLocked(false)) {reportNewConfig = true;}if (attrs.removeTimeoutMilliseconds > 0) {mH.sendMessageDelayed(mH.obtainMessage(H.WINDOW_REMOVE_TIMEOUT, win),attrs.removeTimeoutMilliseconds);}}if (reportNewConfig) {sendNewConfiguration();}Binder.restoreCallingIdentity(origId);return res;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229
  • 230
  • 231
  • 232
  • 233
  • 234
  • 235
  • 236
  • 237
  • 238
  • 239
  • 240
  • 241
  • 242
  • 243
  • 244
  • 245
  • 246
  • 247
  • 248
  • 249
  • 250
  • 251
  • 252
  • 253
  • 254
  • 255
  • 256
  • 257
  • 258
  • 259
  • 260
  • 261
  • 262
  • 263
  • 264
  • 265
  • 266
  • 267
  • 268
  • 269
  • 270
  • 271
  • 272
  • 273
  • 274
  • 275
  • 276
  • 277
  • 278
  • 279
  • 280
  • 281
  • 282
  • 283
  • 284
  • 285
  • 286
  • 287
  • 288
  • 289
  • 290
  • 291
  • 292
  • 293
  • 294
  • 295
  • 296
  • 297
  • 298
  • 299
  • 300
  • 301
  • 302
  • 303
  • 304
  • 305
  • 306
  • 307
  • 308
  • 309
  • 310
  • 311
  • 312
  • 313
  • 314
  • 315
  • 316
  • 317
  • 318
  • 319
  • 320
  • 321
  • 322
  • 323
  • 324
  • 325
  • 326
  • 327
  • 328
  • 329
  • 330
  • 331
  • 332
  • 333
  • 334
  • 335
  • 336
  • 337
  • 338
  • 339
  • 340
  • 341
  • 342
  • 343
  • 344
  • 345
  • 346
  • 347
  • 348
  • 349
  • 350
  • 351
  • 352
  • 353
  • 354
  • 355
  • 356
  • 357
  • 358
  • 359
  • 360
  • 361
  • 362
  • 363
  • 364
  • 365
  • 366
  • 367
  • 368
  • 369
  • 370
  • 371

接下来,我们来理解WindowToken的重要作用:

WindowToken类位于:com.android.server.wm.WindowToken.java,

/*** Container of a set of related windows in the window manager.  Often this* is an AppWindowToken, which is the handle for an Activity that it uses* to display windows.  For nested windows, there is a WindowToken created for* the parent window to manage its children.*/
//先看类说明,WindowToken是一个位于WindowManager中相互关联的Window的结合。
//对于一个activity来说,WindowToken是一个AppWindowToken的句柄(WindowToken的一个子类),用来显示各种window。对于嵌套的window来说,会创建一个WindowToken来给父window管理儿子。
class WindowToken {// The window manager!final WindowManagerService service;// The actual token.final IBinder token;// The type of window this token is for, as per WindowManager.LayoutParams.final int windowType;// Set if this token was explicitly added by a client, so should// not be removed when all windows are removed.final boolean explicit;// For printing.String stringName;// If this is an AppWindowToken, this is non-null.AppWindowToken appWindowToken;// All of the windows associated with this token.final WindowList windows = new WindowList();// Is key dispatching paused for this token?boolean paused = false;// Should this token's windows be hidden?boolean hidden;// Temporary for finding which tokens no longer have visible windows.boolean hasVisible;// Set to true when this token is in a pending transaction where it// will be shown.boolean waitingToShow;// Set to true when this token is in a pending transaction where its// windows will be put to the bottom of the list.boolean sendingToBottom;WindowToken(WindowManagerService _service, IBinder _token, int type, boolean _explicit) {service = _service;token = _token;windowType = type;explicit = _explicit;}void removeAllWindows() {for (int winNdx = windows.size() - 1; winNdx >= 0; --winNdx) {WindowState win = windows.get(winNdx);if (DEBUG_WINDOW_MOVEMENT) Slog.w(TAG_WM, "removeAllWindows: removing win=" + win);win.mService.removeWindowLocked(win);}windows.clear();}void dump(PrintWriter pw, String prefix) {pw.print(prefix); pw.print("windows="); pw.println(windows);pw.print(prefix); pw.print("windowType="); pw.print(windowType);pw.print(" hidden="); pw.print(hidden);pw.print(" hasVisible="); pw.println(hasVisible);if (waitingToShow || sendingToBottom) {pw.print(prefix); pw.print("waitingToShow="); pw.print(waitingToShow);pw.print(" sendingToBottom="); pw.print(sendingToBottom);}}@Overridepublic String toString() {if (stringName == null) {StringBuilder sb = new StringBuilder();sb.append("WindowToken{");sb.append(Integer.toHexString(System.identityHashCode(this)));sb.append(" "); sb.append(token); sb.append('}');stringName = sb.toString();}return stringName;}
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • WindowToken将同一个应用组件的窗口组织在一起,应用组件可以是Activity,InputMethod,WallPaper以及Dream。在WMS管理窗口的过程中,用WindowToken指代一个应用组件。例如在进行窗口ZOrder的排序中,属于同一个WindowToken的窗口会安排在一起。在其中定义的一些属性会影响到所有属于这个windowToken的窗口。
  • WindowToken具有令牌的作用,这是WMS管理应用组件的一个手段。WindowToken由应用组件或其管理者负责向WMS生命并持有。应用组件在需要新的窗口时,必须提供WindowToken以表明自己的身份,并且窗口的类型必须与所持有的WindowToken类型一致。从前面代码看到,System级别窗口不需要提供Token,会由WMS隐式的创建一个WindowToken。那么是不是所有人都能够添加一个系统窗口呢?可以看到addWindow刚开始mPolicy就对应用进行了permission的检查,也就是客户端持有SYSTEM_ALERT_WINDOW等呼出系统窗口的权限,才能创建系统窗口。

那么,应用组件在创建一个窗口的时候必须指定的WindowToken,到底该如何声明呢? 
回到WMS的addWindowToken()函数中,

//可以看到,token其实是一个Binder对象的包装@Overridepublic void addWindowToken(IBinder token, int type) {if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS,"addWindowToken()")) {throw new SecurityException("Requires MANAGE_APP_TOKENS permission");}synchronized(mWindowMap) {//去map中找token,以binder对象为键,WindowToken为值WindowToken wtoken = mTokenMap.get(token);if (wtoken != null) {Slog.w(TAG_WM, "Attempted to add existing input method token: " + token);return;}//创建一个windowToken,放入map中wtoken = new WindowToken(this, token, type, true);mTokenMap.put(token, wtoken);if (type == TYPE_WALLPAPER) {mWallpaperControllerLocked.addWallpaperToken(wtoken);}}}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

从addWindowToken()这个函数可以看到,WindowToken有两层含义。 
- 对显示组件(客户端)而言,token是一个任意的binder实例,对客户端而言,token仅仅是一个创建窗口的令牌,没有其他含义。 
- 对WMS来说,WindowToken是一个WindowToken的实例,包装了客户端一侧的binder实例,并且以这个token为键,以WindowToken为值,保存了token。mTokenMap是否已经保存了该binder实例和对应的WindowToken,标志着客户端一侧的binder token是否已经在WMS声明。

  • WMS通过token来管理各个窗口,那么最重要的Activity的token是如何管理的呢,首先Activity的token使用方式与WallPaper和InputMethod类似的,但是扩展了更多的内容。对此,WMS创建了一个继承WindowToken的AppWindowToken来对Activity进行描述和管理。既然AppWindowToken是为Activity服务的,那么其声明也自然是在ActivityManagerService中,具体位置在ActivityStack.validateAppTokensLocked()中,代码如下:
 final void validateAppTokensLocked() {mValidateAppTokens.clear();mValidateAppTokens.ensureCapacity(numActivities());final int numTasks = mTaskHistory.size();for (int taskNdx = 0; taskNdx < numTasks; ++taskNdx) {TaskRecord task = mTaskHistory.get(taskNdx);final ArrayList<ActivityRecord> activities = task.mActivities;if (activities.isEmpty()) {continue;}TaskGroup group = new TaskGroup();group.taskId = task.taskId;mValidateAppTokens.add(group);final int numActivities = activities.size();for (int activityNdx = 0; activityNdx < numActivities; ++activityNdx) {final ActivityRecord r = activities.get(activityNdx);group.tokens.add(r.appToken);}}
//WMS对appTokens进行更新mWindowManager.validateAppTokens(mStackId, mValidateAppTokens);}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

下面一段代码是AppToken的aidl类,可以看出,实际的binder对象是由这个AIDL文件生成的stub类,分别有一些方法供给WMS进行回调

interface IApplicationToken
{
//窗口完成初次绘制void windowsDrawn();//窗口可见通知AMSvoid windowsVisible();//窗口不可见通知AMSvoid windowsGone();//窗口没能按时完成输入事件的处理boolean keyDispatchingTimedOut(String reason);//从AMS处获取界定ANR的事件long getKeyDispatchingTimeout();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

AMS通过ActivityRecord表示一个Activity,而ActivityRecord的appToken在其构造函数时被创建,类型为IApplicationToken.Stub,就是上述AIDL的stub类,所以每个Activity有自己的appToken,而WMS接受AMS对Application的Binder对象appToken粘结了AMS的ActivityRecord与WMS的AppWindowToken,只要给定一个ActivityRecord,都可以通过appToken在WMS中找到对应的AppWindowToken,从而使得AMS拥有了操纵Activity窗口绘制的能力。例如,当AMS认为一个Activity需要被隐藏的时候,以Activity对应的ActivityRecord所拥有的appToken为参数调用WMS的setAppVisibility()函数,此函数通过appToken找到appdWindowToken(通过mAppWindowMap),然后将这个token的窗口隐藏。

  • 理解WindowState:

从WindowManagerService.addWindow()函数的实现看到,当WMS添加一个窗口时,WMS会为其创建一个WindowState。WindowState表示一个窗口的所有实行,所以他是WMS中事实上的窗口。 
类似于WindowToken,WindowState在显示组件一侧也有对应的类型:IWindow.Stub 。这个借口提供了窗口管理相关的通知回调,如尺寸的变化、焦点的变化等。WindowState被保存到mWindowMap中,键值为IWindow的Bp端。mWindowMap是整个系统所有窗口的一个全集

这篇关于WindowManagerService的理解的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1033547

相关文章

认识、理解、分类——acm之搜索

普通搜索方法有两种:1、广度优先搜索;2、深度优先搜索; 更多搜索方法: 3、双向广度优先搜索; 4、启发式搜索(包括A*算法等); 搜索通常会用到的知识点:状态压缩(位压缩,利用hash思想压缩)。

【生成模型系列(初级)】嵌入(Embedding)方程——自然语言处理的数学灵魂【通俗理解】

【通俗理解】嵌入(Embedding)方程——自然语言处理的数学灵魂 关键词提炼 #嵌入方程 #自然语言处理 #词向量 #机器学习 #神经网络 #向量空间模型 #Siri #Google翻译 #AlexNet 第一节:嵌入方程的类比与核心概念【尽可能通俗】 嵌入方程可以被看作是自然语言处理中的“翻译机”,它将文本中的单词或短语转换成计算机能够理解的数学形式,即向量。 正如翻译机将一种语言

【C++高阶】C++类型转换全攻略:深入理解并高效应用

📝个人主页🌹:Eternity._ ⏩收录专栏⏪:C++ “ 登神长阶 ” 🤡往期回顾🤡:C++ 智能指针 🌹🌹期待您的关注 🌹🌹 ❀C++的类型转换 📒1. C语言中的类型转换📚2. C++强制类型转换⛰️static_cast🌞reinterpret_cast⭐const_cast🍁dynamic_cast 📜3. C++强制类型转换的原因📝

深入理解RxJava:响应式编程的现代方式

在当今的软件开发世界中,异步编程和事件驱动的架构变得越来越重要。RxJava,作为响应式编程(Reactive Programming)的一个流行库,为Java和Android开发者提供了一种强大的方式来处理异步任务和事件流。本文将深入探讨RxJava的核心概念、优势以及如何在实际项目中应用它。 文章目录 💯 什么是RxJava?💯 响应式编程的优势💯 RxJava的核心概念

如何通俗理解注意力机制?

1、注意力机制(Attention Mechanism)是机器学习和深度学习中一种模拟人类注意力的方法,用于提高模型在处理大量信息时的效率和效果。通俗地理解,它就像是在一堆信息中找到最重要的部分,把注意力集中在这些关键点上,从而更好地完成任务。以下是几个简单的比喻来帮助理解注意力机制: 2、寻找重点:想象一下,你在阅读一篇文章的时候,有些段落特别重要,你会特别注意这些段落,反复阅读,而对其他部分

深入理解数据库的 4NF:多值依赖与消除数据异常

在数据库设计中, "范式" 是一个常常被提到的重要概念。许多初学者在学习数据库设计时,经常听到第一范式(1NF)、第二范式(2NF)、第三范式(3NF)以及 BCNF(Boyce-Codd范式)。这些范式都旨在通过消除数据冗余和异常来优化数据库结构。然而,当我们谈到 4NF(第四范式)时,事情变得更加复杂。本文将带你深入了解 多值依赖 和 4NF,帮助你在数据库设计中消除更高级别的异常。 什么是

分布式系统的个人理解小结

分布式系统:分的微小服务,以小而独立的业务为单位,形成子系统。 然后分布式系统中需要有统一的调用,形成大的聚合服务。 同时,微服务群,需要有交流(通讯,注册中心,同步,异步),有管理(监控,调度)。 对外服务,需要有控制的对外开发,安全网关。

Java IO 操作——个人理解

之前一直Java的IO操作一知半解。今天看到一个便文章觉得很有道理( 原文章),记录一下。 首先,理解Java的IO操作到底操作的什么内容,过程又是怎么样子。          数据来源的操作: 来源有文件,网络数据。使用File类和Sockets等。这里操作的是数据本身,1,0结构。    File file = new File("path");   字

理解java虚拟机内存收集

学习《深入理解Java虚拟机》时个人的理解笔记 1、为什么要去了解垃圾收集和内存回收技术? 当需要排查各种内存溢出、内存泄漏问题时,当垃圾收集成为系统达到更高并发量的瓶颈时,我们就必须对这些“自动化”的技术实施必要的监控和调节。 2、“哲学三问”内存收集 what?when?how? 那些内存需要回收?什么时候回收?如何回收? 这是一个整体的问题,确定了什么状态的内存可以

理解分类器(linear)为什么可以做语义方向的指导?(解纠缠)

Attribute Manipulation(属性编辑)、disentanglement(解纠缠)常用的两种做法:线性探针和PCA_disentanglement和alignment-CSDN博客 在解纠缠的过程中,有一种非常简单的方法来引导G向某个方向进行生成,然后我们通过向不同的方向进行行走,那么就会得到这个属性上的图像。那么你利用多个方向进行生成,便得到了各种方向的图像,每个方向对应了很多