WindowManager 详解

  • 一、概述
  • 二、WindowManager 体系
    • 1. 类图
    • 2. 源码分析
      • 2.1 `ViewManager` 的结构
      • 2.2 PhoneWindow 对象何时创建?
      • 2.3 WindowManagerImpl 类
  • 三、Window 的属性 (类型和显示次序)
  • 四、Window 的操作
    • 1. Window 的添加过程
    • 2. Window 的更新过程
    • 3. Window 的删除过程
    • 4. IWindowSession 对象的获取流程
    • 5. ViewRootImpl.scheduleTraversals() 执行流程
  • 五、不同类型窗口的操作
    • 1. 系统窗口的添加过程
    • 2. Activity 的添加过程

一、概述

由于 WindowManager 是与 WindowManagerService (WMS) 关联紧密的类。因此,在分析 WMS前,我们先来分析一下 WindowManager 体系的相关知识。

下面从如下3个部分进行分析:

  1. WindowManager体系
  2. Window 的属性
  3. Window 的操作

版本: Android SDK 29

关联文章:

  1. 《源码分析 — Context 详解》
  2. 《源码分析 — Activity的启动流程》
  3. 《源码分析 — SystemServer》

二、WindowManager 体系

1. 类图

WindowManager 体系如上图所示 (图片来源于网络)。

概念梳理:

  1. Window 是一个抽象概念,代表一个窗口,其具体的实现类为 PhoneWindow ,它对 View进行管理。
  2. WindowManager 是一个接口类,继承自接口 ViewManager,它是用来管理 Window 的。它的具体实现类为 WindowManagerImpI。
  3. WindowManagerGlobal 是实际操作的类,是一个单例,每个进程中只有一个实例对象,该实例对象在 WindowManagerGlobal 中。
  4. 在 WindowManagerGlobal 中,会创建 ViewRootImpl 实例对象,每个根 View 对应一个 ViewRootImpl 实例对象。
  5. 想要对 Window (View)进行添加、更新和删除操作,可以使用 WindowManager 来执行。最终的操作是通过 Binder 交给 WMS 来执行的。

2. 源码分析

2.1 ViewManager 的结构

可以看到,ViewManager 提供的三个方法都是将 View 作为参数传递进去的。

// 可以看到,ViewManager 提供的三个方法都是将 View 作为参数传递进去的。
public interface ViewManager{public void addView(View view, ViewGroup.LayoutParams params);public void updateViewLayout(View view, ViewGroup.LayoutParams params);public void removeView(View view);
}

2.2 PhoneWindow 对象何时创建?

问: 已知 Window 的具体实现类是 PhoneWindow ,那它是何时创建的呢?
.
答:Activity.attach() 方法中创建了 PhoneWindow 对象, Activity.attach() 方法又是在 ActivityThread.performLaunchActivity() 方法中被触发,这部分可以参考 《源码分析 — Activity的启动流程》 。

Activity

// Activity
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) {attachBaseContext(context);mFragments.attachHost(null /*parent*/);// 1.创建了PhoneWindow对象。mWindow = new PhoneWindow(this, window, activityConfigCallback);mWindow.setWindowControllerCallback(this);// 2.Activity 实现了 Window.Callback接口,会触发 Window 操作的相关回调(如 dispatchTouchEvent方法)。mWindow.setCallback(this);// ...省略代码...// 3.将 Window 与 WindowManager 关联起来。mWindow.setWindowManager((WindowManager)context.getSystemService(Context.WINDOW_SERVICE),mToken, mComponent.flattenToString(),(info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);if (mParent != null) {mWindow.setContainer(mParent.getWindow());}// 4.在 Activity 中持有 WindowManager,便于在 Activity 中使用 WindowManager。mWindowManager = mWindow.getWindowManager();// ...省略代码...
}

Window

// Window.class
public void setWindowManager(WindowManager wm, IBinder appToken, String appName,boolean hardwareAccelerated) {mAppToken = appToken;mAppName = appName;mHardwareAccelerated = hardwareAccelerated;if (wm == null) {/* * 1.如果传入的 WindowManager 为空,就从服务里面获取。* 注:* Context.WINDOW_SERVICE = "window"。* getSystemService()真正的执行逻辑在ContextImpl中。* context.getSystemService(Context.WINDOW_SERVICE) 获取到的是 WindowManagerImpl 实例对象。*/wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);}// 2.这里会创建一个WindowManagerImpl对象。mWindowManager = ((WindowManagerImpl)wm).createLocalWindowManager(this);
}

ContextImpl

// ContextImpl.class
public Object getSystemService(String name) {// 获取名字为 "window" 的服务return SystemServiceRegistry.getSystemService(this, name);
}

SystemServiceRegistry

// SystemServiceRegistry.class
final class SystemServiceRegistry {// 根据服务类型,保存服务名称static Map<Class<?>, String> SYSTEM_SERVICE_NAMES = new ArrayMap<Class<?>, String>();// 根据服务名称,获取实际的服务static Map<String, ServiceFetcher<?>> SYSTEM_SERVICE_FETCHERS = new ArrayMap<String, ServiceFetcher<?>>();static {// ...省略一系列的服务注册代码...registerService(Context.WINDOW_SERVICE, WindowManager.class, new CachedServiceFetcher<WindowManager>() {@Overridepublic WindowManager createService(ContextImpl ctx) {// 实际获取到的是 WindowManagerImpl 实例对象,注意这的构造参数没有关联Window,在createLocalWindowManager过程中会与此处有关联。return new WindowManagerImpl(ctx);}});}// 根据服务名称来获取服务public static Object getSystemService(ContextImpl ctx, String name) {// ServiceFetcher<?> fetcher = SYSTEM_SERVICE_FETCHERS.get(name);return fetcher != null ? fetcher.getService(ctx) : null;}// 注册服务时,将服务保存在两个 Map 集合中。private static <T> void registerService(String serviceName, Class<T> serviceClass,ServiceFetcher<T> serviceFetcher) {SYSTEM_SERVICE_NAMES.put(serviceClass, serviceName);SYSTEM_SERVICE_FETCHERS.put(serviceName, serviceFetcher);}
}

WindowManagerImpl

// WindowManagerImpl.class
public WindowManagerImpl createLocalWindowManager(Window parentWindow) {return new WindowManagerImpl(mContext, parentWindow);
}

小结:

问: 在执行 ((WindowManagerImpl)wm).createLocalWindowManager(this) 方法时,参数 wm 已经是 WindowManagerImpl 对象了,为什么还要通过 createLocalWindowManager() 重新创建一个 WindowManagerImpl 对象呢?
.
答: wm 对象内部没有关联当前的 Window,而重新创建的 WindowManagerImpl 对象会将当前的 Window 作为参数传入,因此新的 WindowManagerImpl 对象可以对 Window 进行操作。

2.3 WindowManagerImpl 类

public final class WindowManagerImpl implements WindowManager {// 单例private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();@Overridepublic void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {// 添加默认tokenapplyDefaultToken(params);// WindowManagerImpl 类相当于 WindowManagerGlobal 的代理。mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);}
}

三、Window 的属性 (类型和显示次序)

Window 的类型有很多种,总的来说分为三大类型,分别是 Application Window(应用程序窗口)Sub Window (子窗口)System Window (系统窗口)

窗口类型 Type 值
应用程序窗口 1 ~ 99
子窗口 1000 ~ 1999
系统窗口 2000 ~ 2999

在一般情况下, Type 值越大则 Z-Oder 排序越靠前,就越靠近用户。


四、Window 的操作

我们知道,系统对 Window 进行添加、更新、删除操作,都是通过 WindowManager 来操作的。WindowManager 对 Window 的操作最后都交给 WindowManagerGlobal 来执行。

下面我们来分析一下 WindowManagerGlobal 是如何进行 Window 的添加、更新、删除操作的。

1. Window 的添加过程

时序图:

代码流程分析:
WindowManagerImpl

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

WindowManagerGlobal

// WindowManagerGlobal.class
ArrayList<View> mViews = new ArrayList<View>();
ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
ArrayList<WindowManager.LayoutParams> mParams = new ArrayList<WindowManager.LayoutParams>();public void addView(View view, ViewGroup.LayoutParams params,Display display, Window parentWindow) {// ...省略代码...// 1.根节点的LayoutParams必须为WindowManager.LayoutParams类型,因为确定根View的大小需要使用。if (!(params instanceof WindowManager.LayoutParams)) {throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");}final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;if (parentWindow != null) {// 2.如果这个窗口有父窗口,则需要调整 wparams 的大小,使 wparams 的大小不超过父容器的大小。parentWindow.adjustLayoutParamsForSubWindow(wparams);} else {// ...省略代码...}ViewRootImpl root;View panelParentView = null;synchronized (mLock) {// ...省略代码...// 3.将传入的根View添加到ViewRootImpl对象中(一个根View 对应一个 ViewRootImpl)。root = new ViewRootImpl(view.getContext(), display);// 4.将调整后的 wparams 赋值给根 View。view.setLayoutParams(wparams);// 5.将根 View、根View对应的ViewRootImpl、根View的布局参数LayoutParams分别存入三个集合中。mViews.add(view);mRoots.add(root);mParams.add(wparams);try {// 6.执行 ViewRootImpl.setView() 方法。root.setView(view, wparams, panelParentView);} catch (RuntimeException e) {// ...省略代码...}}
}

ViewRootImpl

ViewRootImpl 类的主要职责:

  1. View 树的根并管理 View 树。
  2. 触发 View 的测量、 布局和绘制。
  3. 输入事件的中转站 。
  4. 管理 Surface 。
  5. 负责与 WMS 进行进程间通信 。
// ViewRootImpl.class
// 用于远程通信的Binder
IWindowSession mWindowSession = WindowManagerGlobal.getWindowSession();public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {synchronized (this) {if (mView == null) {// ...省略代码...// 1.调用requestLayout方法进行绘制。requestLayout();try {mOrigWindowType = mWindowAttributes.type;mAttachInfo.mRecomputeGlobalAttributes = true;collectViewAttributes();// 2.获取远程服务进行通信(IWindowSession对象的获取在第4部分分析)res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,getHostVisibility(), mDisplay.getDisplayId(), mTmpFrame,mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel,mTempInsets);setFrame(mTmpFrame);} catch (RemoteException e) {// ...省略代码...}// ...省略代码...}}
}public void requestLayout() {if (!mHandlingLayoutInLayoutRequest) {checkThread();mLayoutRequested = true;// 该方法的分析过程请看本文 “scheduleTraversals() 执行流程” 部分。scheduleTraversals();}
}

从代码中我们可以看到,在 setView() 方法中,会调用 mWindowSession.addToDisplay() 来与远程服务进行通信 (mWindowSession 是一个 Binder 对象)。那 mWindowSession 对象又是如何获取的呢?这个在 Window 操作 - IWindowSession 对象的获取流程 分析。

Session

// Session.class
public int addToDisplay(IWindow window, int seq, WindowManager.LayoutParams attrs,int viewVisibility, int displayId, Rect outFrame, Rect outContentInsets,Rect outStableInsets, Rect outOutsets,DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,InsetsState outInsetsState) {// mService 就是 WMS,这里在操作 WMS.addView()过程中,将Session也传递进去,目的告诉WMS要操作哪个应用。// 每个应用都对应一个唯一的Session,系统服务就是通过Session来区分不同的应用的。return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outFrame,outContentInsets, outStableInsets, outOutsets, outDisplayCutout, outInputChannel,outInsetsState);
}

WindowManagerService

// WindowManagerService.class
public int addWindow(Session session, IWindow client, int seq,LayoutParams attrs, int viewVisibility, int displayId, Rect outFrame,Rect outContentInsets, Rect outStableInsets, Rect outOutsets,DisplayCutout.ParcelableWrapper outDisplayCutout, InputChannel outInputChannel,InsetsState outInsetsState) {// ...省略代码...
}

到这里,Window 在应用进程的添加逻辑就已经分析完了,在 WMS 部分的逻辑将在 WMS 分析中介绍。


2. Window 的更新过程

WindowManagerImpl

// WindowManagerImpl.class
public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {applyDefaultToken(params);mGlobal.updateViewLayout(view, params);
}

WindowManagerGlobal

// WindowManagerGlobal.class
public void updateViewLayout(View view, ViewGroup.LayoutParams params) {if (view == null) {throw new IllegalArgumentException("view must not be null");}// 1.校验 LayoutParams 类型if (!(params instanceof WindowManager.LayoutParams)) {throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");}// 将 LayoutParams 添加到根 View 中。final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;view.setLayoutParams(wparams);synchronized (mLock) {// 3.替换 View 相关的参数(添加 View 时将 View,ViewRootImpl,LayoutParams 添加到三个集合中)int index = findViewLocked(view, true);ViewRootImpl root = mRoots.get(index);mParams.remove(index);mParams.add(index, wparams);// 这里调用 ViewRootImpl.setLayoutParams 进行更新root.setLayoutParams(wparams, false);}
}
// 查询指定 View 在 mViews 集合中的位置。
private int findViewLocked(View view, boolean required) {final int index = mViews.indexOf(view);if (required && index < 0) {throw new IllegalArgumentException("View=" + view + " not attached to window manager");}return index;
}

ViewRootImpl

// ViewRootImpl.class
void setLayoutParams(WindowManager.LayoutParams attrs, boolean newView) {synchronized (this) {// ...省略代码...// 这里的操作与 Window 的添加过程一样。scheduleTraversals();}
}

到这里,Window 在应用进程的更新逻辑就已经分析完了,在 WMS 部分的逻辑将在 WMS 分析中介绍。


3. Window 的删除过程

WindowManagerImpl

// WindowManagerImpl.class
public void removeView(View view) {mGlobal.removeView(view, false);
}

WindowManagerGlobal

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);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);}
}private void removeViewLocked(int index, boolean immediate) {ViewRootImpl root = mRoots.get(index);View view = root.getView();if (view != null) {InputMethodManager imm = view.getContext().getSystemService(InputMethodManager.class);if (imm != null) {imm.windowDismissed(mViews.get(index).getWindowToken());}}// 这里执行了 ViewRootImpl.die() 逻辑。boolean deferred = root.die(immediate);if (view != null) {view.assignParent(null);if (deferred) {mDyingViews.add(view);}}
}

ViewRootImpl

// ViewRootImpl.class
boolean die(boolean immediate) {// Make sure we do execute immediately if we are in the middle of a traversal or the damage// done by dispatchDetachedFromWindow will cause havoc on return.if (immediate && !mIsInTraversal) {doDie();return false;}// ...省略代码...// 这里使用Handler发消息,其实也是调用了 doDie() 方法。mHandler.sendEmptyMessage(MSG_DIE);return true;
}void doDie() {// ...省略代码...synchronized (this) {// 使用 mRemoved 标记位,防止进行重复移除。if (mRemoved) {return;}mRemoved = true;if (mAdded) {// 1.将 View 从 Window 中移除。dispatchDetachedFromWindow();}if (mAdded && !mFirst) {destroyHardwareRenderer();if (mView != null) {int viewVisibility = mView.getVisibility();boolean viewVisibilityChanged = mViewVisibility != viewVisibility;if (mWindowAttributesChanged || viewVisibilityChanged) {// If layout params have been changed, first give them// to the window manager to make sure it has the correct// animation info.try {// 2.这个方法内部会与 WMS 进行通信。if ((relayoutWindow(mWindowAttributes, viewVisibility, false)& WindowManagerGlobal.RELAYOUT_RES_FIRST_TIME) != 0) {// 3.通过 IWindowSession 与 WMS 通信。mWindowSession.finishDrawing(mWindow);}} catch (RemoteException e) {}}// 4.释放 Surface 资源。destroySurface();}}mAdded = false;}// 5.将 ViewRootImpl 从 WindowManagerGlobal 中移除。WindowManagerGlobal.getInstance().doRemoveView(this);
}void dispatchDetachedFromWindow() {// ...省略代码...try {// 通过 IWindowSession 调用远程 WMS 移除 Window。mWindowSession.remove(mWindow);}// ...省略代码...unscheduleTraversals();
}void unscheduleTraversals() {if (mTraversalScheduled) {mTraversalScheduled = false;mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);// 这一段代码与 Choreographer.postCallback() 方法类似(参考本文 "scheduleTraversals 执行流程" 部分的分析)。mChoreographer.removeCallbacks(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);}
}

WindowManagerGlobal

void doRemoveView(ViewRootImpl root) {synchronized (mLock) {final int index = mRoots.indexOf(root);if (index >= 0) {// 移除 root 相关的信息。mRoots.remove(index);mParams.remove(index);final View view = mViews.remove(index);mDyingViews.remove(view);}}if (ThreadedRenderer.sTrimForeground && ThreadedRenderer.isAvailable()) {doTrimForeground();}
}

到这里,Window 在应用进程的移除逻辑就已经分析完了,在 WMS 部分的逻辑将在 WMS 分析中介绍。


4. IWindowSession 对象的获取流程

IWindowSession 是一个 Binder 对象,用于进行进程间通信。IWindowSession 是 Client 端的代理 (运行在应用进程),它的 Server端的实现为 Session (运行在 WMS 所在进程)。

时序图:

代码流程分析:

// WindowManagerGlobal.class
public static IWindowSession getWindowSession() {synchronized (WindowManagerGlobal.class) {if (sWindowSession == null) {try {InputMethodManager.ensureDefaultInstanceForDefaultDisplayIfNecessary();// 1.获取 WMS 远程代理。IWindowManager windowManager = getWindowManagerService();// 2.通过远程代理类获取WindowSession远程代理。sWindowSession = windowManager.openSession(new IWindowSessionCallback.Stub() {@Overridepublic void onAnimatorScaleChanged(float scale) {ValueAnimator.setDurationScale(scale);}});} catch (RemoteException e) {throw e.rethrowFromSystemServer();}}return sWindowSession;}
}// WindowManagerGlobal.class
public static IWindowManager getWindowManagerService() {synchronized (WindowManagerGlobal.class) {if (sWindowManagerService == null) {// 通过AIDL方式获取 WMS 的远程代理(WMS是在SystemServer.startOtherServices()中启动的)。sWindowManagerService = IWindowManager.Stub.asInterface(ServiceManager.getService("window"));// ...省略代码...}return sWindowManagerService;}
}// WindowManagerService.class
public IWindowSession openSession(IWindowSessionCallback callback) {return new Session(this, callback);
}// Session.class
public Session(WindowManagerService service, IWindowSessionCallback callback) {mService = service;mCallback = callback;// ...省略代码...// 构建一个Session,每个应用都对应一个唯一的Session,系统服务就是通过Session来区分不同的应用的。StringBuilder sb = new StringBuilder();sb.append("Session{");sb.append(Integer.toHexString(System.identityHashCode(this)));sb.append(" ");sb.append(mPid);if (mUid < Process.FIRST_APPLICATION_UID) {sb.append(":");sb.append(mUid);} else {sb.append(":u");sb.append(UserHandle.getUserId(mUid));sb.append('a');sb.append(UserHandle.getAppId(mUid));}sb.append("}");mStringName = sb.toString();try {mCallback.asBinder().linkToDeath(this, 0);} catch (RemoteException e) {// The caller has died, so we can just forget about this.// Hmmm, should we call killSessionLocked()??}
}

Session 的作用: 每个应用都对应一个唯一的Session,系统服务就是通过Session来区分不同的应用的。


5. ViewRootImpl.scheduleTraversals() 执行流程

方法调用流程: scheduleTraversals() --> TraversalRunnable.run() --> doTraversal() --> performTraversals()

performTraversals() 方法中,会触发 View 的 measure、layout、draw 三个过程,如下图所示 (图片来源于网络):

代码流程分析:

ViewRootImpl

// ViewRootImpl.class
void scheduleTraversals() {if (!mTraversalScheduled) {mTraversalScheduled = true;mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();// postCallback 添加回调,这个添加的回调将在下一帧被渲染时执行。// Choreographer 用了接收显示系统的 VSync 信号。mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);if (!mUnbufferedInputDispatch) {scheduleConsumeBatchedInput();}notifyRendererOfFramePending();pokeDrawLockIfNeeded();}
}final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
final class TraversalRunnable implements Runnable {@Overridepublic void run() {// 执行操作doTraversal();}
}void doTraversal() {if (mTraversalScheduled) {mTraversalScheduled = false;mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);if (mProfile) {Debug.startMethodTracing("ViewAncestor");}// 执行操作performTraversals();if (mProfile) {Debug.stopMethodTracing();mProfile = false;}}
}public final WindowManager.LayoutParams mWindowAttributes = new WindowManager.LayoutParams();private void performTraversals() {// ...省略代码...WindowManager.LayoutParams lp = mWindowAttributes;// ...省略代码...if (mFirst || windowShouldResize || insetsChanged ||viewVisibilityChanged || params != null || mForceNextWindowRelayout) {// ...省略代码...try {// ...省略代码...// 1.触发 WMS 的 Window 更新操作。relayoutResult = relayoutWindow(params, viewVisibility, insetsPending);// ...省略代码...}if (!mStopped || mReportNextDraw) {if (条件判断) {int childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);int childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);// ...省略代码...// 2.从根 View 开始测量操作performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);// ...省略代码...// 如果WindowManager.LayoutParams有权重,需进行二次测量。boolean measureAgain = false;// ...省略代码...if (measureAgain) {performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);}layoutRequested = true;}}}// ...省略代码...final boolean didLayout = layoutRequested && (!mStopped || mReportNextDraw);if (didLayout) {// 3.从根 View 开始计算 View 的位置。performLayout(lp, mWidth, mHeight);// ...省略代码...}// ...省略代码...boolean cancelDraw = mAttachInfo.mTreeObserver.dispatchOnPreDraw() || !isViewVisible;if (!cancelDraw) {// ...省略代码...// 4.从根 View 开始绘制 View。performDraw();} else {// ...省略代码...}mIsInTraversal = false;
}private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,boolean insetsPending) throws RemoteException {// ...省略代码...// 通过 IWindowSession 与远程服务进行通信。int relayoutResult = mWindowSession.relayout(mWindow, mSeq, params,(int) (mView.getMeasuredWidth() * appScale + 0.5f),(int) (mView.getMeasuredHeight() * appScale + 0.5f), viewVisibility,insetsPending ? WindowManagerGlobal.RELAYOUT_INSETS_PENDING : 0, frameNumber,mTmpFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets,mPendingStableInsets, mPendingOutsets, mPendingBackDropFrame, mPendingDisplayCutout,mPendingMergedConfiguration, mSurfaceControl, mTempInsets);// ...省略代码...return relayoutResult;
}private void performMeasure(int childWidthMeasureSpec, int childHeightMeasureSpec) {// ...省略代码...// mView 是 DecorView,从根 View 开始测量 View 的大小。mView.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}private void performLayout(WindowManager.LayoutParams lp, int desiredWindowWidth,int desiredWindowHeight) {// ...省略代码...final View host = mView;// host 是 DecorView,从根 View 开始计算 View 的位置。host.layout(0, 0, host.getMeasuredWidth(), host.getMeasuredHeight());// ...省略代码...
}private void performDraw() {// ...省略代码...try {boolean canUseAsync = draw(fullRedrawNeeded);// ...省略代码...} // ...省略代码...
}private boolean draw(boolean fullRedrawNeeded) {Surface surface = mSurface;// ...省略代码...if (!dirty.isEmpty() || mIsAnimating || accessibilityFocusDirty) {if (条件判断) {// ...省略代码...} else {// ...省略代码...// 执行 View 的绘制逻辑。if (!drawSoftware(surface, mAttachInfo, xOffset, yOffset,scalingRequired, dirty, surfaceInsets)) {return false;}}}// ...省略代码...return useAsyncReport;
}private boolean drawSoftware(Surface surface, AttachInfo attachInfo, int xoff, int yoff,boolean scalingRequired, Rect dirty, Rect surfaceInsets) {// Draw with software renderer.final Canvas canvas;// ...省略代码...try {// ...省略代码...// 1.从 Surface 中获取画布,其实真正的视图是渲染在 Surface 上的。canvas = mSurface.lockCanvas(dirty);// TODO: Do this in nativecanvas.setDensity(mDensity);}// ...省略代码...try {// ...省略代码...// 2.mView是 DecorView,从根 View 开始绘制。mView.draw(canvas);drawAccessibilityFocusedDrawableIfNeeded(canvas);}// ...省略代码...return true;
}

小结:

  1. performTraversals() 方法中,会触发 View 的 measure、layout、draw 三个过程。
  2. relayoutWindow() 方法会触发 WMS 的 Window 操作。

五、不同类型窗口的操作

Window 的类型主要分为三种:Application Window(应用程序窗口)Sub Window (子窗口)System Window (系统窗口)。而不同的窗口类型,它的操作流程也有一些不同。下面分析一下系统窗口和应用窗口的添加过程。

1. 系统窗口的添加过程

我们以 StatusBar 为例来分析系统窗口的添加过程。

// com.android.systemui.statusbar.phone.StatusBar.class
// 这是将 StatusBar 添加到 Window 的方法
public void createAndAddWindows(@Nullable RegisterStatusBarResult result) {// 1.创建一个 StatusBarViewmakeStatusBarView(result);// Dependency.get() 的操作和前面的 mContext.getSystemService(Context.WINDOW_SERVICE) 有点类似,// 都是预先在一个服务里面注册,然后在使用的地方通过指定类型来获取的。mStatusBarWindowController = Dependency.get(StatusBarWindowController.class);// 2.将 StatusBar 和它的高度值传给 StatusBarWindowController控制器(MVC模式)mStatusBarWindowController.add(mStatusBarWindow, getStatusBarHeight());
}

StatusBarWindowController

// StatusBarWindowController.class
public void add(ViewGroup statusBarView, int barHeight) {// 1.创建一个窗口的 LayoutParams 参数mLp = new WindowManager.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,barHeight, //指定的高度值WindowManager.LayoutParams.TYPE_STATUS_BAR, //这里指定窗口的类型为StatusBarWindowManager.LayoutParams.FLAG_NOT_FOCUSABLE| WindowManager.LayoutParams.FLAG_TOUCHABLE_WHEN_WAKING| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH| WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS,PixelFormat.TRANSLUCENT);mLp.token = new Binder();mLp.gravity = Gravity.TOP;mLp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;mLp.setTitle("StatusBar");mLp.packageName = mContext.getPackageName();mLp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;mStatusBarView = statusBarView;mBarHeight = barHeight;// 2.将创建的StatusBar 通过 WindowManager添加到 Window 中。mWindowManager.addView(mStatusBarView, mLp);mLpChanged.copyFrom(mLp);onThemeChanged();
}

代码中第2步,调用 WindowManager.addView() 方法来添加系统的 Window,这个过程在上面已经分析过了,此处不再赘述。


2. Activity 的添加过程

在应用程序中,Activity 是最为常见,我们以 Activity 为例来分析应用窗口的添加过程。

当界面要与用户进行交互时,会调用 ActivityThread. handleResumeActivity() 方法。

ActivityThread

// ActivityThread.class
public void handleResumeActivity(IBinder token, boolean finalStateRequest, boolean isForward, String reason) {// ...省略代码...// TODO Push resumeArgs into the activity for consideration// 1.将 Activity 回复到 RESUME 状态。final ActivityClientRecord r = performResumeActivity(token, finalStateRequest, reason);// ...省略代码...final Activity a = r.activity;// ...省略代码...if (r.window == null && !a.mFinished && willBeVisible) {// 2.获取在 Activity.attach() 方法中就创建了 PhoneWindow 对象。r.window = r.activity.getWindow();View decor = r.window.getDecorView();// 这里使 Decor 不可见。decor.setVisibility(View.INVISIBLE);// 3.获取 Activity 中持有的 WindowManager。ViewManager wm = a.getWindowManager();WindowManager.LayoutParams l = r.window.getAttributes();a.mDecor = decor;l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;l.softInputMode |= forwardBit;if (r.mPreserveWindow) {a.mWindowAdded = true;r.mPreserveWindow = false;ViewRootImpl impl = decor.getViewRootImpl();if (impl != null) {impl.notifyChildRebuilt();}}if (a.mVisibleFromClient) {if (!a.mWindowAdded) {//将 Activity.WindowAdded 标记为true,避免在 Activity.makeVisible() 是重复进行 Window 添加操作。a.mWindowAdded = true;// 将根 View(DecorView)通过 WindowManager 添加到 Window 中。wm.addView(decor, l);} else {a.onWindowAttributesChanged(l);}}}// ...省略代码...// The window is now visible if it has been added, we are not// simply finishing, and we are not starting another activity.if (!r.activity.mFinished && willBeVisible && r.activity.mDecor != null && !r.hideForNow) {// ...省略代码...r.activity.mVisibleFromServer = true;mNumVisibleActivities++;if (r.activity.mVisibleFromClient) {// 这个方法内部会是 DecorView 可见。r.activity.makeVisible();}}
}public ActivityClientRecord performResumeActivity(IBinder token, boolean finalStateRequest, String reason) {// 1.每一个 ActivityClientRecord 都代表着一个 Activity 。final ActivityClientRecord r = mActivities.get(token);// ...省略代码...try {r.activity.onStateNotSaved();r.activity.mFragments.noteStateNotSaved();checkAndBlockForNetworkAccess();if (r.pendingIntents != null) {// 这里会触发 Activity.onNewIntent()方法。deliverNewIntents(r, r.pendingIntents);r.pendingIntents = null;}if (r.pendingResults != null) {// 这里会触发 Activity.onActivityResult()方法。deliverResults(r, r.pendingResults, reason);r.pendingResults = null;}// 这里会触发 Activity.onResume()方法。r.activity.performResume(r.startsNotResumed, reason);r.state = null;r.persistentState = null;// 这里将当前 Activity 的生命周期状态设置为 ON_RESUME。r.setState(ON_RESUME);reportTopResumedActivityChanged(r, r.isTopResumedActivity, "topWhenResuming");} catch (Exception e) {// ...省略代码...}return r;
}

Activity

// Activity.class
void makeVisible() {if (!mWindowAdded) {ViewManager wm = getWindowManager();wm.addView(mDecor, getWindow().getAttributes());mWindowAdded = true;}// 将 DecorView设置为可见。mDecor.setVisibility(View.VISIBLE);
}

在 Activity 执行 Resume 生命周期时,也会触发 Window 的添加操作。


Window系列 (一) — WindowManager 详解相关推荐

  1. DELPHI 中 Window 消息大全使用详解

    Window 消息大全使用详解 导读: Delphi是Borland公司的一种面向对象的可视化软件开发工具. Delphi集中了Visual C++和Visual Basic两者的优点:容易上手.功能 ...

  2. Docker系列07—Dockerfile 详解

    Docker系列07-Dockerfile 详解 1.认识Dockerfile 1.1 镜像的生成途径 基于容器制作  dockerfile,docker build 基于容器制作镜像,已经在上篇Do ...

  3. Window 消息大全使用详解(无聊没事做)

    Window 消息大全使用详解(无聊没事做) 楼主zhangqu_980371(能坚持一辈子的东西太少)2004-12-19 16:35:23 在 VC/MFC / 基础类 提问     消息,就是指 ...

  4. mongo 3.4分片集群系列之六:详解配置数据库

    这个系列大致想跟大家分享以下篇章: 1.mongo 3.4分片集群系列之一:浅谈分片集群 2.mongo 3.4分片集群系列之二:搭建分片集群--哈希分片 3.mongo 3.4分片集群系列之三:搭建 ...

  5. ftm模块linux驱动,飞思卡尔k系列_ftm模块详解.doc

    飞思卡尔k系列_ftm模块详解 1.5FTM模块1.5.1 FTM模块简介FTM模块是一个多功能定时器模块,主要功能有,PWM输出.输入捕捉.输出比较.定时中断.脉冲加减计数.脉冲周期脉宽测量.在K1 ...

  6. React Native按钮详解|Touchable系列组件使用详解

    转载自:http://www.devio.org/2017/01/10/React-Native按钮详解-Touchable系列组件使用详解/ 在做App开发过程中离不了的需要用户交互,说到交互,我们 ...

  7. Material Design系列之BottomNavigationView详解

    Material Design系列之BottomNavigationView详解 Material Design官方文档Bottom navigation的介绍 BottomNavigationVie ...

  8. React 源码系列 | React Context 详解

    目前来看 Context 是一个非常强大但是很多时候不会直接使用的 api.大多数项目不会直接使用 createContext 然后向下面传递数据,而是采用第三方库(react-redux). 想想项 ...

  9. Window任务计划命令详解

    window任务计划命令详解 Schtasks 安排命令和程序定期运行或在指定时间内运行.从计划表中添加和删除任务,按需要启动和停止任务,显示和更改计划任务. 若要查看该命令语法,请单击以下命令: s ...

  10. Landsat系列数据级别详解

    Landsat系列数据级别详解 转载自此文:https://www.cnblogs.com/icydengyw/p/12056211.html 一.Landsat Collection 1 Lands ...

最新文章

  1. opencv 图像雾检测_OpenCV图像处理-基于OpenPose的关键点检测
  2. as3中使用字符串调用函数。
  3. 数据仓库专题(6)-数据仓库、主题域、主题概念与定义
  4. idea输入法候选区不跟随光标
  5. FPGA RAM存储器设计
  6. python爬虫酷狗_python爬虫教程:爬取酷狗音乐,零基础小白也能爬取哦
  7. 经典机器学习系列(十)【变分推断】
  8. Fish 环境下如何安装 nvm
  9. Unity之A星算法
  10. 林达华推荐的数学知识
  11. hud.java_什么是HUD
  12. 软件测试与游戏测试文章合集录
  13. HTML5实现动态视频背景
  14. 365资讯简报 每日精选12条新闻简报 每天一分钟 知晓天下事10月12日
  15. C++ 后室·无限#1 游戏制作实录(RPG类型游戏)
  16. android studio导入as项目,Android Studio(AS)--导入项目
  17. asynchronous socket error 10053错误及解决方法
  18. Java里面的同步和异步
  19. js-高德地图规划路线
  20. 利用语义分割算法做指针式仪表的读数识别

热门文章

  1. SpringCloud-网关统一配置跨域
  2. 开源软件、自由软件、Copyleft、CC都是啥,傻傻分不清楚?
  3. 应届毕业生在面试的时候应该如何谈薪资待遇?
  4. matlab费曼编码输入,多点格林函数数值积分(费曼参数积分)的程序分析及应用
  5. 【Alpha】第二次Daily Scrum Meeting
  6. P5514 [MtOI2019]永夜的报应
  7. OTT广告系统设计与实现
  8. 企业级shel高级l常用命令
  9. PHP操作MongoDB技術總結
  10. 自我管理 - 希望2015年自己能够做到的几点目标