这篇主要分析 Window 的工作机制,WindowManager 添加 Window (View) 的主要流程
以下是Android9.0 的源码
首先看一下 WindowManager.java
xref: /frameworks/base/core/java/android/view/WindowManager.java
package android.view;
......
@SystemService(Context.WINDOW_SERVICE)
public interface WindowManager extends ViewManager {
......
public static class LayoutParams extends ViewGroup.LayoutParams implements Parcelable {
/**
* X position for this window. With the default gravity it is ignored.
* When using {@link Gravity#LEFT} or {@link Gravity#START} or {@link Gravity#RIGHT} or
* {@link Gravity#END} it provides an offset from the given edge.
*/
@ViewDebug.ExportedProperty
public int x;
/**
* Y position for this window. With the default gravity it is ignored.
* When using {@link Gravity#TOP} or {@link Gravity#BOTTOM} it provides
* an offset from the given edge.
*/
@ViewDebug.ExportedProperty
public int y;
......
}
......
}
WindowManager 是一个接口,该接口继承了 ViewManager,内部类 LayoutParams 继承了 ViewGroup 的 LayoutParams
通过注释可以看到,当我们使用 WindowManager.LayoutParams 的 x、y参数时需要通过类似 mParams.gravity=Gravity.LEFT | Gravity.TOP;为其指明 gravity 参数,否则会出现问题(比如无法拖动悬浮按钮)
看一下 ViewManager 接口
xref: /frameworks/base/core/java/android/view/ViewManager.java
package android.view; /** Interface to let you add and remove child views to an Activity. To get an instance * of this class, call {@link android.content.Context#getSystemService(java.lang.String) Context.getSystemService()}. */ public interface ViewManager { /** * Assign the passed LayoutParams to the passed View and add the view to the window. *
Throws {@link android.view.WindowManager.BadTokenException} for certain programming * errors, such as adding a second view to a window without removing the first view. *
Throws {@link android.view.WindowManager.InvalidDisplayException} if the window is on a * secondary {@link Display} and the specified display can't be found * (see {@link android.app.Presentation}). * @param view The view to be added to this window. * @param params The LayoutParams to assign to view. */ public void addView(View view, ViewGroup.LayoutParams params); public void updateViewLayout(View view, ViewGroup.LayoutParams params); public void removeView(View view); }
WindowManager 是一个接口,谁实现了这个接口呢(可以搜索关键字 "implements WindowManager")
xref: /frameworks/base/core/java/android/view/WindowManagerImpl.java
package android.view;
......
public final class WindowManagerImpl implements WindowManager {
private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
private final Context mContext;
private final Window mParentWindow;
private IBinder mDefaultToken;
public WindowManagerImpl(Context context) {
this(context, null);
}
......
@Override
public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.addView(view, params, mContext.getDisplay(), mParentWindow);
}
@Override
public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
applyDefaultToken(params);
mGlobal.updateViewLayout(view, params);
}
......
@Override
public void removeView(View view) {
mGlobal.removeView(view, false);
}
@Override
public void removeViewImmediate(View view) {
mGlobal.removeView(view, true);
}
添加、删除 View的操作是通过WindowManagerGlobal来实现的,进入 WindowManagerGlobal
xref: /frameworks/base/core/java/android/view/WindowManagerGlobal.java
package android.view;
......
public final class WindowManagerGlobal {
private static final String TAG = "WindowManager";
......
private final Object mLock = new Object();
/*
以下几个变量比较重要
mViews 存储所有 Winow 所对应的 View
mRoots 存储所有 Window 所对应的 ViewRootImpl
mParams 存储所有 Window 对应的布局参数
mDyingViews 存储正在被删除的 View 对象(已经调用 removeView 方法但还未完成删除)
*/
private final ArrayList mViews = new ArrayList();
private final ArrayList mRoots = new ArrayList();
private final ArrayList mParams =
new ArrayList();
private final ArraySet mDyingViews = new ArraySet();
//单例模式
public static WindowManagerGlobal getInstance() {
synchronized (WindowManagerGlobal.class) {
if (sDefaultWindowManager == null) {
sDefaultWindowManager = new WindowManagerGlobal();
}
return sDefaultWindowManager;
}
}
//获取 WindowManagerService 服务
public static IWindowManager getWindowManagerService() {
synchronized (WindowManagerGlobal.class) {
if (sWindowManagerService == null) {
sWindowManagerService = IWindowManager.Stub.asInterface(
ServiceManager.getService("window"));
try {
if (sWindowManagerService != null) {
ValueAnimator.setDurationScale(
sWindowManagerService.getCurrentAnimatorScale());
}
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
return sWindowManagerService;
}
}
//获取 WindowSession
public static IWindowSession getWindowSession() {
synchronized (WindowManagerGlobal.class) {
if (sWindowSession == null) {
try {
InputMethodManager imm = InputMethodManager.getInstance();
IWindowManager windowManager = getWindowManagerService();
sWindowSession = windowManager.openSession(
new IWindowSessionCallback.Stub() {
@Override
public void onAnimatorScaleChanged(float scale) {
ValueAnimator.setDurationScale(scale);
}
},
imm.getClient(), imm.getInputContext());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
return sWindowSession;
}
}
......
public void addView(View view, ViewGroup.LayoutParams params,
Display display, Window parentWindow) {
//首先检查参数是否合法
if (view == null) {
throw new IllegalArgumentException("view must not be null");
}
if (display == null) {
throw new IllegalArgumentException("display must not be null");
}
if (!(params instanceof WindowManager.LayoutParams)) {
throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
}
final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams) params;
......
//定义ViewRootImpl
ViewRootImpl root;
View panelParentView = null;
......
int index = findViewLocked(view, false);//获取要添加的View的索引
//这里检查索引值,因为如果是第一次添加,返回的索引应该为 -1
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.
}
......
root = new ViewRootImpl(view.getContext(), display);//创建ViewRootImpl
view.setLayoutParams(wparams);//为View设置布局参数
//将Window一系列对象添加到列表
mViews.add(view);
mRoots.add(root);
mParams.add(wparams);
//通过ViewRootImpl的 setView 方法来更新界面,完成Window的添加过程
// do this last because it fires off messages to start doing things
try {
root.setView(view, wparams, panelParentView);
} catch (RuntimeException e) {
// BadTokenException or InvalidDisplayException, clean up.
if (index >= 0) {
removeViewLocked(index, true);
}
throw e;
}
}
}
进入 ViewRootImpl
xref: /frameworks/base/core/java/android/view/ViewRootImpl.java
package android.view;
......
public final class ViewRootImpl implements ViewParent,
View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks {
private static final String TAG = "ViewRootImpl";
......
public ViewRootImpl(Context context, Display display) {
mContext = context;
//在构造方法中获取WindowSession实例
mWindowSession = WindowManagerGlobal.getWindowSession();
mDisplay = display;
......
mFirst = true; // true for the first time the view is added
mAdded = false;
......
}
......
/**
* We have one child
*/
public void setView(View view, WindowManager.LayoutParams attrs, View panelParentView) {
synchronized (this) {
if (mView == null) {
mView = view;//getView方法中会直接返回mView,可以通过getView直接获取该View
mAttachInfo.mDisplayState = mDisplay.getState();
mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);
mAttachInfo.mDisplayState = mDisplay.getState();
mDisplayManager.registerDisplayListener(mDisplayListener, mHandler);
......
if (panelParentView != null) {
mAttachInfo.mPanelParentWindowToken
= panelParentView.getApplicationWindowToken();
}
mAdded = true;
int res; /* = WindowManagerImpl.ADD_OKAY; */
// Schedule the first layout -before- adding to the window
// manager, to make sure we do the relayout before receiving
// any other events from the system.
//通过requestLayout()来完成异步刷新请求
requestLayout();
......
try {
mOrigWindowType = mWindowAttributes.type;
mAttachInfo.mRecomputeGlobalAttributes = true;
collectViewAttributes();
//通过WindowSession最终完成 Window 的添加过程
res = mWindowSession.addToDisplay(mWindow, mSeq, mWindowAttributes,
getHostVisibility(), mDisplay.getDisplayId(), mWinFrame,
mAttachInfo.mContentInsets, mAttachInfo.mStableInsets,
mAttachInfo.mOutsets, mAttachInfo.mDisplayCutout, mInputChannel);
} catch (RemoteException e) {
mAdded = false;
mView = null;
......
mWindowSession 是 IWindowSession,是一个 Binder 对象,addToDisplay 是Window添加过程中的一次IPC调用
看一下IWindowSession是由谁来实现的(通过搜索 "IWindowSession.Stub")
可以看到 从 frameworks/base/core 进入了 frameworks/base/services/core
xref: /frameworks/base/services/core/java/com/android/server/wm/Session.java
位于 com.android.server.wm 包下
package com.android.server.wm;
......
/**
* This class represents an active client session. There is generally one
* Session object per process that is interacting with the window manager.
*/
class Session extends IWindowSession.Stub implements IBinder.DeathRecipient {
......
final WindowManagerService mService;//定义了WindowManagerService
final IWindowSessionCallback mCallback;
//构造函数中初始化 mService
public Session(WindowManagerService service, IWindowSessionCallback callback,
IInputMethodClient client, IInputContext inputContext) {
mService = service;
......
}
......
@Override
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) {
//最终通过 WindowMangerService 添加Window
return mService.addWindow(this, window, seq, attrs, viewVisibility, displayId, outFrame,outContentInsets, outStableInsets, outOutsets, outDisplayCutout, outInputChannel);
}
......
}
WindowManagerService 也和 Session在同一路径下
xref: /frameworks/base/services/core/java/com/android/server/wm/WindowManagerService.java
public class WindowManagerService extends IWindowManager.Stub
implements Watchdog.Monitor, WindowManagerPolicy.WindowManagerFuncs {
private static final String TAG = TAG_WITH_CLASS_NAME ? "WindowManagerService" : TAG_WM;