Android 系统启动系列
Android 深入四大组件系列
Android 应用进程启动过程系列
Android 解析 WindowManager 系列
前言
在本系列文章中, 我提到过: Window 的操作分为两大部分, 一部分是 WindowManager 处理部分, 另一部分是 WMS 处理部分, Window 的删除过程也不例外, 本篇文章会介绍 Window 的删除过程, 包括了两大处理部分的内容
Window 的删除过程
和 Android 解析 WindowManagerService(二)WMS 的重要成员和 Window 的添加过程这篇文章中 Window 的创建和更新过程类似, 要删除 Window 需要先调用 WindowManagerImpl 的 removeView 方法, removeView 方法中又会调用 WindowManagerGlobal 的 removeView 方法, 我们就从这里开始讲起为了表述的更易于理解, 本文将要删除的 Window(View)简称为 VWindowManagerGlobal 的 removeView 方法如下所示
- frameworks/base/core/java/android/view/WindowManagerGlobal.java
- public void removeView(View view, boolean immediate) {
- if (view == null) {
- throw new IllegalArgumentException("view must not be null");
- }
- synchronized (mLock) {
- int index = findViewLocked(view, true);//1
- View curView = mRoots.get(index).getView();
- removeViewLocked(index, immediate);//2
- if (curView == view) {
- return;
- }
- throw new IllegalStateException("Calling with view" + view
- + "but the ViewAncestor is attached to" + curView);
- }
- }
注释 1 处找到要 V 在 View 列表中的索引, 在注释 2 处调用了 removeViewLocked 方法并将这个索引传进去, 如下所示
- frameworks/base/core/java/android/view/WindowManagerGlobal.java
- private void removeViewLocked(int index, boolean immediate) {
- ViewRootImpl root = mRoots.get(index);//1
- View view = root.getView();
- if (view != null) {
- InputMethodManager imm = InputMethodManager.getInstance();//2
- if (imm != null) {
- imm.windowDismissed(mViews.get(index).getWindowToken());//3
- }
- }
- boolean deferred = root.die(immediate);//4
- if (view != null) {
- view.assignParent(null);
- if (deferred) {
- mDyingViews.add(view);
- }
- }
- }
注释 1 处根据传入的索引在 ViewRootImpl 列表中获得 V 的 ViewRootImpl 注释 2 处得到 InputMethodManager 实例, 如果 InputMethodManager 实例不为 null 则在注释 3 处调用 InputMethodManager 的 windowDismissed 方法来结束 V 的输入法相关的逻辑注释 4 处调用 ViewRootImpl 的 die 方法, 如下所示
- frameworks/base/core/java/android/view/ViewRootImpl.java
- boolean die(boolean immediate) {
- //die 方法需要立即执行并且此时 ViewRootImpl 不在执行 performTraversals 方法
- if (immediate && !mIsInTraversal) {//1
- doDie();//2
- return false;
- }
- if (!mIsDrawing) {
- destroyHardwareRenderer();
- } else {
- Log.e(mTag, "Attempting to destroy the window while drawing!\n" +
- "window=" + this + ", title=" + mWindowAttributes.getTitle());
- }
- mHandler.sendEmptyMessage(MSG_DIE);
- return true;
- }
注释 1 处如果 immediate 为 ture(需要立即执行), 并且 mIsInTraversal 值为 false 则执行注释 2 处的代码, mIsInTraversal 在执行 ViewRootImpl 的 performTraversals 方法时会被设置为 true, 在 performTraversals 方法执行完时被设置为 false, 因此注释 1 处可以理解为 die 方法需要立即执行并且此时 ViewRootImpl 不在执行 performTraversals 方法注释 2 处的 doDie 方法如下所示
- frameworks/base/core/java/android/view/ViewRootImpl.java
- void doDie() {
- // 检查执行 doDie 方法的线程的正确性
- checkThread();//1
- if (LOCAL_LOGV) Log.v(mTag, "DIE in" + this + "of" + mSurface);
- synchronized (this) {
- if (mRemoved) {//2
- return;
- }
- mRemoved = true;//3
- if (mAdded) {//4
- dispatchDetachedFromWindow();//5
- }
- if (mAdded && !mFirst) {//6
- destroyHardwareRenderer();
- if (mView != null) {
- int viewVisibility = mView.getVisibility();
- boolean viewVisibilityChanged = mViewVisibility != viewVisibility;
- if (mWindowAttributesChanged || viewVisibilityChanged) {
- try {
- if ((relayoutWindow(mWindowAttributes, viewVisibility, false)
- & WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {
- mWindowSession.finishDrawing(mWindow);
- }
- } catch (RemoteException e) {
- }
- }
- mSurface.release();
- }
- }
- mAdded = false;
- }
- WindowManagerGlobal.getInstance().doRemoveView(this);//7
- }
注释 1 处用于检查执行 doDie 方法的线程的正确性, 注释 1 的内部会判断执行 doDie 方法线程是否是创建 V 的原始线程, 如果不是就会抛出异常, 这是因为只有创建 V 的原始线程才能够操作 V 注释 2 到注释 3 处的代码用于防止 doDie 方法被重复调用注释 4 处 V 有子 View 就会调用 dispatchDetachedFromWindow 方法来销毁 View 注释 6 处如果 V 有子 View 并且不是第一次被添加, 就会执行后面的代码逻辑注释 7 处的 WindowManagerGlobal 的 doRemoveView 方法, 如下所示
- frameworks/base/core/java/android/view/WindowManagerGlobal.java
- void doRemoveView(ViewRootImpl root) {
- synchronized (mLock) {
- final int index = mRoots.indexOf(root);//1
- if (index >= 0) {
- mRoots.remove(index);
- mParams.remove(index);
- final View view = mViews.remove(index);
- mDyingViews.remove(view);
- }
- }
- if (ThreadedRenderer.sTrimForeground && ThreadedRenderer.isAvailable()) {
- doTrimForeground();
- }
- }
WindowManagerGlobal 中维护了和 Window 操作相关的三个列表, doRemoveView 方法会从这三个列表中清除 V 对应的元素注释 1 处找到 V 对应的 ViewRootImpl 在 ViewRootImpl 列表中的索引, 接着根据这个索引从 ViewRootImpl 列表布局参数列表和 View 列表中删除与 V 对应的元素
我们接着回到 ViewRootImpl 的 doDie 方法, 查看注释 5 处的 dispatchDetachedFromWindow 方法里做了什么:
- frameworks/base/core/java/android/view/ViewRootImpl.java
- void dispatchDetachedFromWindow() {
- ...
- try {
- mWindowSession.remove(mWindow);
- } catch (RemoteException e) {
- }
- ...
- }
dispatchDetachedFromWindow 方法中主要调用了 IWindowSession 的 remove 方法, IWindowSession 在 Server 端的实现为 Session,Session 的 remove 方法如下所示
- frameworks/base/services/core/java/com/android/server/wm/Session.java
- public void remove(IWindow window) {
- mService.removeWindow(this, window);
- }
接着查看 WMS 的 removeWindow 方法:
- frameworks/base/services/core/java/com/android/server/wm/WindowManagerService .java
- void removeWindow(Session session, IWindow client) {
- synchronized(mWindowMap) {
- WindowState win = windowForClientLocked(session, client, false);//1
- if (win == null) {
- return;
- }
- win.removeIfPossible();//2
- }
- }
注释 1 处用于获取 Window 对应的 WindowState,WindowState 用于保存窗口的信息, 在 WMS 中它用来描述一个窗口接着在注释 2 处调用 WindowState 的 removeIfPossible 方法, 如下所示
- frameworks/base/services/core/java/com/android/server/wm/WindowState.java
- Override
- void removeIfPossible() {
- super.removeIfPossible();
- removeIfPossible(false /*keepVisibleDeadWindow*/);
- }
又会调用 removeIfPossible 方法, 如下所示
- frameworks/base/services/core/java/com/android/server/wm/WindowState.java
- private void removeIfPossible(boolean keepVisibleDeadWindow) {
... 条件判断过滤, 满足其中一个条件就会 return, 推迟删除操作
- removeImmediately();//1
- if (wasVisible && mService.updateOrientationFromAppTokensLocked(false, displayId)) {
- mService.mH.obtainMessage(SEND_NEW_CONFIGURATION, displayId).sendToTarget();
- }
- mService.updateFocusedWindowLocked(UPDATE_FOCUS_NORMAL, true /*updateInputWindows*/);
- Binder.restoreCallingIdentity(origId);
- }
removeIfPossible 方法和它的名字一样, 并不是直接执行删除操作, 而是进行多个条件判断过滤, 满足其中一个条件就会 return, 推迟删除操作比如这时 V 正在运行一个动画, 这时就得推迟删除操作, 直到动画完成通过这些条件判断过滤就会执行注释 1 处的 removeImmediately 方法:
- frameworks/base/services/core/java/com/android/server/wm/WindowState.java
- @Override
- void removeImmediately() {
- super.removeImmediately();
- if (mRemoved) {//1
- if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
- "WS.removeImmediately:" + this + "Already removed...");
- return;
- }
- mRemoved = true;//2
- ...
- mPolicy.removeWindowLw(this);//3
- disposeInputChannel();
- mWinAnimator.destroyDeferredSurfaceLocked();
- mWinAnimator.destroySurfaceLocked();
- mSession.windowRemovedLocked();//4
- try {
- mClient.asBinder().unlinkToDeath(mDeathRecipient, 0);
- } catch (RuntimeException e) {
- }
- mService.postWindowRemoveCleanupLocked(this);//5
- }
removeImmediately 方法如同它的名字一样, 用于立即进行删除操作注释 1 处的 mRemoved 为 true 意味着正在执行删除 Window 操作, 注释 1 到注释 2 处之间的代码用于防止重复删除操作注释 3 处如果当前要删除的 Window 是 StatusBar 或者 NavigationBar 就会将这个 Window 从对应的控制器中删除注释 4 处会将 V 对应的 Session 从 WMS 的
ArraySet<Session> mSessions
中删除并清除 Session 对应的 SurfaceSession 资源 (SurfaceSession 是 SurfaceFlinger 的一个连接, 通过这个连接可以创建 1 个或者多个 Surface 并渲染到屏幕上 ) 注释 5 处调用了 WMS 的 postWindowRemoveCleanupLocked 方法用于对 V 进行一些集中的清理工作, 这里就不在继续深挖下去, 有兴趣的同学可以自行查看源码
Window 的删除过程就讲到这里, 虽然删除的操作逻辑比较复杂, 但是可以简单的总结为以下 4 点:
检查删除线程的正确性, 如果不正确就抛出异常
从 ViewRootImpl 列表布局参数列表和 View 列表中删除与 V 对应的元素
判断是否可以直接执行删除操作, 如果不能就推迟删除操作
执行删除操作, 清理和释放与 V 相关的一切资源
来源: http://liuwangshu.cn/framework/wms/3-wms-remove.html