【Android】Window 机制

前言

Window,即窗口,在 Android 中,它是一个抽象的东西,我们在日常开发中,直接接触到的东西是 View,而 View 是附着在 Window 之上的。所以我们也可以说 Window 是 View 的直接管理者。

在了解 window 机制之前,我们需要对 Android 系统中的显示部分涉及到的东西有一个大致的了解。

基础概念

Surface

一个 Surface 就是一个对象,Surface 对象持有一群像素,这些像素是要被组合到一起显示到屏幕上的。在 Android 设备上看到的每一个 Window,都有一个自己的 Surface,Window 将自己的内容绘制到该Surface 中。Surface Flinger 根据各个 Surface 在 Z轴 上的顺序 (Z-order) 将它们渲染到最终的显示屏上。

一个 Surface 通常有两个缓冲区以实现双缓冲绘制:当应用正在一个缓冲区中绘制自己下一个 UI 状态时,Surface Flinger 可以将另一个缓冲区中的数据合成显示到屏幕上,而不用等待应用绘制完成。

View

一个 View 就是一个 Window 中可交互的 UI 元素。每个 Window 都有唯一一个附着于它的 view hierarchy

当一个 Window 需要重绘时,比如一个 View 通过 invalidate 方法使自己失效了,就要进入到 Window 的 Surface 中去完成了。首先,该 Window 的 Surface 会被锁定,锁定的同时会返回一个 canvas,该 canvas 可被用来在 Surface 上绘制内容。该 canvas 会沿着 view hierarchy 遍历传递给每一个 View,好让每个 View 绘制自己的 UI 部分。当这个过程完成时,Surface 将会被解锁和提交,提交的目的是将刚刚绘制好的缓冲区交换到前台,然后让 Surface Flinger 利用该缓冲区的数据刷新 Window 的显示。

值的一提的是,开发中我的遇到的 SurfaceView 实际上也是一个 View,与普通 View 不同的是,它拥有一个自己专门的 Surface,以便让应用直接在里面绘制内容,SurfaceView 是独立于其所属 Window 的 view hierarchy 的

Window

Window 拥有唯一一个用以绘制自己的内容的 Surface。应用通过 WindowManager 创建一个 Window,WindowManager 为每一个 Window 创建一个 Surface,并把该 Surface 传递给应用以便应用在上面绘制。应用可以在 Surface 上任意进行绘制。

Window 的 type 属性:

Window 的 type 属性决定了 Window 的显示次序。

Window 是有分类的,不同类别的显示高度范围不同,Window 有一个变量 Z-Order,它决定了 Window 的高度,Z-Order 越大,Window 越靠近用户,也就显示越高,高度高的 Window 会覆盖高度低的 Window。

Window 一共分为三类:

  • Application Window:应用程序窗口,Activity 就是一个典型的应用程序窗口,它一般位于最底层,Z-Order 在 1-99 之间
  • Sub Window:子窗口,不能独立存在,需要依附于其他窗口,典型的例子是 PopupWindow,Z-Order 在 1000-1999 之间
  • System Window:系统窗口,Toast 、输入法窗口、系统音量窗口、系统错误窗口都属于系统窗口,系统级窗口一般位于最顶层,不会被其他的 Window 遮住,Z-Order 在 2000-2999 之间

Window 的 flag 参数:

通过设置 Window 的 flag 参数,我们可以控制 Window 在各种情景下的显示逻辑(锁屏,游戏等)还有触控事件的处理逻辑。

flag 参数在 WindowManager 的内部类 LayoutParams 中定义。

为 Window 添加 Flag 可以通过 addFlags 方法和 setFlags 方法:

window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
// or
window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);

也可以通过 WindowManager.LayoutParams 去设置:

val lp = WindowManager.LayoutParams()
lp.flags = WindowManager.LayoutParams.FLAG_FULLSCREEN
val tv = TextView(context)
windowManager.addView(tv, lp)

Window 的 solfInputMode 属性:

这一部分就是当软件盘弹起来的时候,Window 的处理逻辑,这在日常中也经常遇到,如:我们在微信聊天的时候,点击输入框,当软键盘弹起来的时候输入框也会被顶上去。如果你不想被顶上去,也可以设置为被软键盘覆盖。

这些内容可以在 AndroidManifest.xml 中的 Activity 的属性设置:

android:windowSoftInputMode

也可以通过代码去进行设置:

window.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST

Window 在 Android 中是一个抽象类,它的具体实现类为 PhoneWindow.java,它的职责是对 View 进行管理。

值得注意的是:每个 Window 都有自己的唯一的一个 WindowManager 对象,并且我们可以通过 setWindowManager 方法来设置 Window 的 WindowManager,代码如下所示:

public abstract class Window {
    
    
    // ......
    private WindowManager mWindowManager;
	// ......
    public void setWindowManager(WindowManager wm, IBinder appToken, String appName) {
    
    
        setWindowManager(wm, appToken, appName, false);
    }

    public void setWindowManager(WindowManager wm, IBinder appToken, String appName, boolean hardwareAccelerated) {
    
    
        mAppToken = appToken;
        mAppName = appName;
        mHardwareAccelerated = hardwareAccelerated;
        if (wm == null) {
    
    
            wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
        }
        mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
    }
}

PhoneWindow.java 类如下所示:

public class PhoneWindow extends Window implements MenuBuilder.Callback {
    
    
    ......
}

WindowManager

WindowManager 负责维护它所在的 Window 里面的内容,在 Android 中,WindowManager 是一个接口,继承自 ViewManager 接口。

public interface WindowManager extends ViewManager {
    
    
    ......
}

在 ViewManager 中,定义了三个对 window 进行管理的方法,如下所示:

public interface ViewManager {
    
    
    public void addView(View view, ViewGroup.LayoutParams params);
    public void updateViewLayout(View view, ViewGroup.LayoutParams params);
    public void removeView(View view);
}

WindowManager 的实现类是 WindowManagerImpl.java,而 WindowManagerImpl 又会把具体的功能实现委托给 WindowManagerGlobal.java。WindowManagerGlobal 是一个单例,一个进程只有一个 WindowManagerGlobal 实例

public final class WindowManagerImpl implements WindowManager {
    
    
    ......
}

DecorView

DecorView 是一个 View,它继承自 FrameLayout:

public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {
    
    
	......
}

DecorView 是整个 view hierarchy 的最顶层 View。

ViewRootImpl

ViewRootImpl 主要是负责与其他服务联络,并指导 view hierarchy 的绘制工作。

注意:一个 Window 只对应一个 ViewRootImpl

如果我们要在界面上显示 view hierarchy ,就需要通知 WMS 来处理,而这个通知工作在是在 ViewRootImpl 之中完成。

例如我们通过 WindowManager 的 addView 方法,可以添加一个 View 到 Window 上,在 addView 方法中会创建一个 ViewRootImpl,来对 view 进行操作(调用 setView 方法),最后将 View 渲染到屏幕的窗口上。

WMS

WMS,即 WindowManagerService,它是 Android 系统中的一个重要的服务,WindowManager 是由 WMS 管理的。它是 Window 的真正管理者,所有的 Window 创建最终都要经过 WindowManagerService,它还决定了屏幕所有的 Window 该如何显示如何分发点击事件等等,因此它在 Android 系统中扮演着相当重要的角色。

以上涉及到的名词的关系可以从这张图中体现:

在这里插入图片描述

Window 的创建

为了讲解 Window 的创建,我们以 Activity 的 PhoneWindow 为例,

我们知道,每个 Activity 都有一个属于自己的 Window,在 Activity 启动时,ActivityThread 会调用 performLaunchActivity() 方法创建一个 Activity 实例:

// ActivityThread.java
private Activity performLaunchActivity(ActivityClientRecord r, Intent customIntent) {
    
    
   Activity activity = null;
   // ...
   java.lang.ClassLoader cl = appContext.getClassLoader();
   activity = mInstrumentation.newActivity(cl, component.getClassName(), r.intent);
   Application app = r.packageInfo.makeApplication(false, mInstrumentation);
   // ...
   activity.attach(appContext, this, getInstrumentation(), r.token,
            r.ident, app, r.intent, r.activityInfo, title, r.parent,
            r.embeddedID, r.lastNonConfigurationInstances, config,
            r.referrer, r.voiceInteractor, window, r.configCallback,
            r.assistToken);
}

performLaunchActivity 方法中,会调用 Activity 的 attach() 方法:

// Activity.java
final void attach(Context context, ActivityThread aThread,
    Instrumentation instr, IBinder token, int ident,
    Application application, Intent intent, ActivityInfo info,
    CharSequence title, Activity parent, String id,
    NonConfigurationInstances lastNonConfigurationInstances,
    Configuration config, String referrer, IVoiceInteractor voiceInteractor,
    Window window, ActivityConfigCallback activityConfigCallback, IBinder assistToken) {
    
    
  ... 
  // 创建 PhoneWindow 对象
  mWindow = new PhoneWindow(this, window, activityConfigCallback);
  ...
  // 为 PhoneWindow 设置 WindowManager
  mWindow.setWindowManager(
    (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
    mToken, mComponent.flattenToString(),
    (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
 
  mWindowManager = mWindow.getWindowManager();
}

由此可知,Activity 专属的 Window(PhoneWindow) 对象在 Activity 的 attach 方法中创建。

上文中我们说到,每个 Window 都有唯一一个附着于它的 view hierarchy,那么 view hierarchy 又是何时创建的呢?或者说 DecorView 是何时创建的呢?

这一点大部分读者都比较熟悉,在 Activity 中调用 setContentView() 时,DecorView 会被创建。

// Activity.java
public void setContentView(@LayoutRes int layoutResID) {
    
    
  getWindow().setContentView(layoutResID);
  initWindowDecorActionBar();
}

// PhoneWondow.java
@Override
public void setContentView(int layoutResID) {
    
    
  if (mContentParent == null) {
    
    
    // 创建 DecorView
    installDecor();
  } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
    
    
    mContentParent.removeAllViews();
  }
  ...
}
 
private void installDecor() {
    
    
  mForceDecorInstall = false;
  if (mDecor == null) {
    
    
    // 创建 DecorView 并赋值给 PhoneWindow 的成员变量 mDecor
    mDecor = generateDecor(-1);
    ...
  }
  ...
}
 
protected DecorView generateDecor(int featureId) {
    
    
  Context context;
  if (mUseDecorContext) {
    
    
    Context applicationContext = getContext().getApplicationContext();
    if (applicationContext == null) {
    
    
      context = getContext();
    } else {
    
    
      context = new DecorContext(applicationContext, this);
      if (mTheme != -1) {
    
    
        context.setTheme(mTheme);
      }
    }
  } else {
    
    
    context = getContext();
  }
  // 新建 DecorView 对象,并将 PhoneWindow 实例传入其中
  return new DecorView(context, featureId, this, getAttributes());
}

由此可知,当我们在 Activity 中调用了 setContentView() 方法后,PhoneWindow 也就有自己的 DecorView 了。

不过到目前为止,此时的 Activity 仍然不会在界面上显示任何东西

要在界面上显示 view hierarchy ,需要通知 WMS 来处理,而这个通知工作在是在 ViewRootImpl 之中完成,因此,还需要再创建一个 ViewRootImpl 对象。

ViewRootImpl 主要是负责与其他服务联络,并指导 view hierarchy 的绘制工作。

那么,ViewRootImpl 是何时创建并与 view hierarchy 关联的呢?

一句话简要概况:当 WindowManagerImpl 决定管理 view hierarchy 时,会给它关联一个 ViewRootImpl 实例。从代码层面来说就是 WindowManagerImpl 调用 addView() 方法将 View 树添加到 View 列表中时。

在 Activity 的启动流程中,会执行 ActivityThread.handleResumeActivity() 方法:

// ActivityThread.java
public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward,
        String reason) {
    
    
  ...
  // 内部会调用Activity的onResume方法
  final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);
 
  final Activity a = r.activity;
 
  final int forwardBit = isForward
          ? WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0;

  boolean willBeVisible = !a.mStartedActivity;
  if (!willBeVisible) {
    
    
    try {
    
    
      willBeVisible = ActivityTaskManager.getService().willActivityBeVisible(
              a.getActivityToken());
    } catch (RemoteException e) {
    
    
      throw e.rethrowFromSystemServer();
    }
  }
  // 当窗口没有被添加到 WindowManager 中时 willBeVisible 为 true
  if (r.window == null && !a.mFinished && willBeVisible) {
    
    
    r.window = r.activity.getWindow();
    View decor = r.window.getDecorView();
    decor.setVisibility(View.INVISIBLE);
    ViewManager wm = a.getWindowManager();
    WindowManager.LayoutParams l = r.window.getAttributes();
    a.mDecor = decor;
    // 设置窗口类型
    l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
 
    if (a.mVisibleFromClient) {
    
    
      if (!a.mWindowAdded) {
    
    
        a.mWindowAdded = true;
        // WindowManagerImpl 将 View 添加到自己的管理队列中
        wm.addView(decor, l);
      } else {
    
    
        a.onWindowAttributesChanged(l);
      }
    }
  }
}	

ActivityThread.handleResumeActivity() 方法中,WindowManagerImpl.addView() 被调用,并把 DecorView 对象传入。

那么,接下来再来看一下 WindowManagerImpl 的 addView() 方法:

// WindowManagerImpl.java

// 持有单例 WindowManagerGlobal
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
 
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
    
    
  applyDefaultToken(params);
  mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}

可以看到,在 WindowManagerImpl.addView 方法中,调用了 WindowManagerGlobal.addView 方法了,我们进入 WindowManagerGlobal 的 addView() 方法:

// WindowManagerGlobal.java
private static IWindowSession sWindowSession;
 
private final ArrayList<View> mViews = new ArrayList<View>();
@UnsupportedAppUsage
private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
@UnsupportedAppUsage
private final ArrayList<WindowManager.LayoutParams> mParams =
      new ArrayList<WindowManager.LayoutParams>();
 
public void addView(View view, ViewGroup.LayoutParams params,
      Display display, Window parentWindow) {
    
    
  ...
  final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
  ...
  ViewRootImpl root;
  View panelParentView = null;
 
  synchronized (mLock) {
    
    
    ...
    // 创建 ViewRootImpl 实例
    root = new ViewRootImpl(view.getContext(), display);
    // 给 View 设置属性
    view.setLayoutParams(wparams);
 
    mViews.add(view);
    mRoots.add(root);
    mParams.add(wparams);
    ...
    // 将 ViewRootImp 于 View 进行绑定
    root.setView(view, wparams, panelParentView);
    ...
  }
}

可以看到当 WindowManagerGlobal 的 addView() 方法中,创建了 ViewRootImpl 对象,并将 ViewRootImpl 对象与 DecorView 进行绑定,也就是执行 ViewRootImpl.setView 方法。由此,ViewRootImpl 便与 view hierarchy 建立了关联。

接下里继续看看 ViewRootImpl.setView 方法:

// ViewRootImpl.java
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
    
    
    synchronized (this) {
    
    
        ...
        try {
    
    
            mOrigWindowType = mWindowAttributes.type;
            mAttachInfo.mRecomputeGlobalAttributes = true;
            collectViewAttributes();
            // 这里调用了 `mWindowSession.addToDisplay` 方法,其实是调用 WMS 的方法,让 WMS 来处理 View 的显示
            res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
                    getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame,
                    mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
                    mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel,
                    mTempInsets);
            setFrame(mTmpFrame);
        } 
        ...
    }
}

可以看到,这里调用了 mWindowSession.addToDisplay 方法,其实是调用 WMS 的方法,让 WMS 来处理 View 的显示。

这里重点关注 mWindowSession 这个对象,它是在 ViewRootImpl 的构造函数中被创建出来的:

// ViewRootImpl.java
public ViewRootImpl(Context context, Display display) {
    
    
	...
 	mWindowSession = WindowManagerGlobal.getWindowSession();
 	...
}

// WindowManagerGlobal.java
public static IWindowSession getWindowSession() {
    
    
 synchronized (WindowManagerGlobal.class) {
    
    
     if (sWindowSession == null) {
    
    
         try {
    
    
             ...
             sWindowSession = windowManager.openSession(
                     new IWindowSessionCallback.Stub() {
    
    
                         ...
                     });
         } 
         ...
     }
     return sWindowSession;
 }
}

可以看出来,这里 sWindowSession 是一个单例,因此我们知道,一个应用只有一个 WindowSession 对象

mWindowSession.addToDisplay 方法的逻辑就是交给 WMS 去处理了。本文篇幅有限,就不继续深入了。

Window 的更新

通过执行 WindowManagerImpl.updateViewLayout 方法来进行 Window 的更新:

// WindowManagerImpl.java:
@Override
public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
    
    
    android.util.SeempLog.record_vg_layout(384,params);
    applyDefaultToken(params);
    mGlobal.updateViewLayout(view, params);
}

实际调用的是 WindowManagerGlobal 对应的方法 updateViewLayout:

// WindowManagerGlobal.java:
public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
    
    
        if (view == null) {
    
    
            throw new IllegalArgumentException("view must not be null");
        }
        if (!(params instanceof WindowManager.LayoutParams)) {
    
    
            throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
        }

        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;
		// view 设置新的 LayoutParams
        view.setLayoutParams(wparams);

        synchronized (mLock) {
    
    
            int index = findViewLocked(view, true);
            ViewRootImpl root = mRoots.get(index);
            mParams.remove(index);
            mParams.add(index, wparams);
            root.setLayoutParams(wparams, false);
        }
}

这里通过 view.setLayoutParams(wparams) 方法,更新了参数 view 的 LayoutParams ,

然后通过 root.setLayoutParams(wparams, false) 方法,更新了 ViewRootImpl 的 LayoutParams:

// ViewRootImpl.java:
void setLayoutParams(WindowManager.LayoutParams attrs, boolean newView) {
    
    
    scheduleTraversals();
}

在 ViewRootImpl 的 setLayoutParams 方法中,会通过 scheduleTraversals 方法来对 View 重新布局,包括测量、布局、重绘这三个过程 。

除了 View 本身的重绘以外,ViewRootImpl 还会通过 WindowSession 来更新 Window 的视图,这个过程最终是由 WMS 的 relayoutWindow() 来具体实现的,它同样是一个 IPC 过程。

Window 的删除

Window 的删除过程通过 WindowManager.removeView 方法进行:

// WindowManagerImpl.java:
@Override
public void removeView(View view) {
    
    
    mGlobal.removeView(view, false);
}

然后调用到 WindowManagerGlobal.removeView 方法:

// WindowManagerGlobal.java:
@UnsupportedAppUsage
    public void removeView(View view, boolean immediate) {
    
    
        if (view == null) {
    
    
            throw new IllegalArgumentException("view must not be null");
        }
        synchronized (mLock) {
    
    
			// 查找待删除的 View 的索引
            int index = findViewLocked(view, true);
            // 找出对应的 ViewRootImpl
            View curView = mRoots.get(index).getView();
            // 删除方法
            removeViewLocked(index, immediate);
            if (curView == view) {
    
    
                return;
            }

            throw new IllegalStateException("Calling with view " + view
                    + " but the ViewAncestor is attached to " + curView);
        }
    }

首先通过 findViewLocked 方法 view 取查找待删除的 View 的索引,然后从 mRoots 中根据索引找出对应的 ViewRootImpl ,

然后调用 removeViewLocked 方法进行删除:

// WindowManagerGlobal.java:
private void removeViewLocked(int index, boolean immediate) {
    
    
        ViewRootImpl root = mRoots.get(index);
    	// 获取 ViewRootImpl 和 View 对象
        View view = root.getView();

        if (view != null) {
    
    
            InputMethodManager imm = view.getContext().getSystemService(InputMethodManager.class);
            if (imm != null) {
    
    
                // 结束该 Window 的输入法相关的逻辑
                imm.windowDismissed(mViews.get(index).getWindowToken());
            }
        }
    	// 执行 die 方法
        boolean deferred = root.die(immediate);
        if (view != null) {
    
    
            view.assignParent(null);
            if (deferred) {
    
    
                mDyingViews.add(view);
            }
        }
    }

首先获取 ViewRootImpl 和 view 对象,如果 view 对象不为 null,

然后调用 InputMethodManager.windowDismissed 方法来结束该 Window 的输入法相关的逻辑。

然后调用 ViewRootImpl 的 die 方法,继续跟进 root.die 方法:

// ViewRootImpl.java:
boolean die(boolean immediate) {
    
    
    	// 需要立即执行并且此时 ViewRootImpl 不在执行 performTraversals 方法
        if (immediate && !mIsInTraversal) {
    
    
            doDie();
            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;
    }

如果 immediate 为 ture(需要立即执行),且 mIsInTraversal 值为 false 则执行 doDie() 方法,

mIsInTraversal 在执行 ViewRootImpl 的 performTraversals 方法时会被设置为 true,在 performTraversals 方法执行完时被设置为false。

继续跟进 doDie() 方法:

// ViewRootImpl.java:
void doDie() {
    
    
    	// 检查执行 doDie 方法的线程的正确性
        checkThread();
        if (LOCAL_LOGV) Log.v(mTag, "DIE in " + this + " of " + mSurface);
        synchronized (this) {
    
    
            if (mRemoved) {
    
    
                return;
            }
            mRemoved = true;
            // Window 有 view 就会调用 dispatchDetachedFromWindow 方法来销毁 view
            if (mAdded) {
    
    
                dispatchDetachedFromWindow();
            }
			// 如果 Window 有 View 并且不是第一次被添加,就会执行后面的代码逻辑
            if (mAdded && !mFirst) {
    
    
                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) {
    
    
                        }
                    }

                    destroySurface();
                }
            }

            mAdded = false;
        }
    	// 执行 WindowManagerGlobal 的 doRemoveView 方法
        WindowManagerGlobal.getInstance().doRemoveView(this);
    }

doDie 方法中,有以下流程:

1、首先检查执行 doDie 方法的线程的正确性,即判断执行 doDie 方法线程是否是创建 Window 的原始线程,如果不是就会抛出异常,这是因为只有创建 Window 的原始线程才能够操作 Window。

2、如果 Window 有 view,那么就会调用 dispatchDetachedFromWindow 方法来销毁 view

3、执行 WindowManagerGlobal 的 doRemoveView 方法

先来看看 doRemoveView 方法:

// WindowManagerGlobal.java:
void doRemoveView(ViewRootImpl root) {
    
    
        synchronized (mLock) {
    
    
            final int index = mRoots.indexOf(root);
            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 操作相关的三个列表:

  • mRoots:ViewRootImpl 列表
  • mParams:布局参数列表
  • mViews:View 列表

这里的 doRemoveView 方法会从这三个列表中清除 Window 对应的元素,首先,找到 Window 对应的 ViewRootImpl 的索引,然后根据这个索引从 mRoots、mParams 和 mViews 中删除与 Window 对应的元素。

继续回到 doDie 方法,跟进其中调用的 dispatchDetachedFromWindow 方法:

// ViewRootImpl.java:
void dispatchDetachedFromWindow() {
    
    
        // ...
        try {
    
    
            mWindowSession.remove(mWindow);
        } catch (RemoteException e) {
    
    
        }
		// ...
    }

dispatchDetachedFromWindow 方法中主要调用了 IWindowSession 的 remove 方法,

IWindowSession 在 Server 端的实现为 Session,位于 frameworks/base/services/core/java/com/android/server/wm/Session.java

我们进入 Session 的 remove方法:

// Session.java:
@Override
public void remove(IWindow window) {
    
    
    mService.removeWindow(this, window);
}

这里的 mService 即为 WMS,接着查看 WMS 的 removeWindow 方法:

// WindowManagerService .java
void removeWindow(Session session, IWindow client) {
    
    
    synchronized (mGlobalLock) {
    
    
        // 获取 WindowState 对象
        WindowState win = windowForClientLocked(session, client, false);
        if (win == null) {
    
    
            return;
        }
        // 调用 WindowState.removeIfPossible 方法
        win.removeIfPossible();
    }
}

windowForClientLocked 方法用于获取 Window 对应的 WindowState 对象。

WindowState 用于保存窗口的信息,在 WMS 中它用来描述一个窗口

最后,调用 WindowState.removeIfPossible 方法,继续跟进该方法:

// WindowState.java:
@Override
void removeIfPossible() {
    
    
    super.removeIfPossible();
    removeIfPossible(false /*keepVisibleDeadWindow*/);
}

  private void removeIfPossible(boolean keepVisibleDeadWindow) {
    
    
       // ... 条件判断过滤,满足其中一个条件就会return,推迟删除操作
      
       // removeImmediately 立即删除 Window
	   removeImmediately();
	   // ...
}

removeIfPossible 方法和它的名字一样,并不是直接执行删除操作,而是进行多个条件判断过滤,满足其中一个条件就会 return,推迟删除操作,

比如这时 Window 正在执行一个动画,这时就得推迟删除操作,直到动画完成。

通过这些条件判断过滤就会执行 removeImmediately 方法立即删除 Window:

// WindowState.java:
@Override
    void removeImmediately() {
    
    
        super.removeImmediately();
		// 防止重复删除操作
        if (mRemoved) {
    
    
            // Nothing to do.
            if (DEBUG_ADD_REMOVE) Slog.v(TAG_WM,
                    "WS.removeImmediately: " + this + " Already removed...");
            return;
        }
        mRemoved = true;

        mWillReplaceWindow = false;
        if (mReplacementWindow != null) {
    
    
            mReplacementWindow.mSkipEnterAnimationForSeamlessReplacement = false;
        }

        final DisplayContent dc = getDisplayContent();
        if (isInputMethodTarget()) {
    
    
            dc.computeImeTarget(true /* updateImeTarget */);
        }

        final int type = mAttrs.type;
        if (WindowManagerService.excludeWindowTypeFromTapOutTask(type)) {
    
    
            dc.mTapExcludedWindows.remove(this);
        }
        if (mTapExcludeRegionHolder != null) {
    
    
            dc.mTapExcludeProvidingWindows.remove(this);
        }
        dc.getDisplayPolicy().removeWindowLw(this);

        disposeInputChannel();

        mWinAnimator.destroyDeferredSurfaceLocked();
        mWinAnimator.destroySurfaceLocked();
        // 将 Window 对应的 Session 从 WMS 的 ArraySet<Session> mSessions 中删除并清除 Session 对应的 SurfaceSession 资源
        mSession.windowRemovedLocked();
        try {
    
    
            mClient.asBinder().unlinkToDeath(mDeathRecipient, 0);
        } catch (RuntimeException e) {
    
    
        }
		// 调用了 WMS 的 postWindowRemoveCleanupLocked 方法用于对 Window 进行一些集中的清理工作
        mWmService.postWindowRemoveCleanupLocked(this);
    }

mRemoved 为 true 意味着正在执行删除 Window 操作,如果正在执行删除,则该方法不需要执行。

mSession.windowRemovedLocked() 方法会将 Window 对应的 Session 从 WMS 的 ArraySet<Session> mSessions 中删除并清除 Session 对应的 SurfaceSession 资源。

SurfaceSession 是 SurfaceFlinger 的一个连接,通过这个连接可以创建 1 个或者多个 Surface 并渲染到屏幕上

mWmService.postWindowRemoveCleanupLocked 方法即通过 WMS,对 Window 进行一些集中的清理工作。

猜你喜欢

转载自blog.csdn.net/yang553566463/article/details/127656260