這篇主要分析 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.
* <p>Throws {@link android.view.WindowManager.BadTokenException} for certain programming
* errors, such as adding a second view to a window without removing the first view.
* <p>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<View> mViews = new ArrayList<View>();
private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
private final ArrayList<WindowManager.LayoutParams> mParams =
new ArrayList<WindowManager.LayoutParams>();
private final ArraySet<View> mDyingViews = new ArraySet<View>();
//單例模式
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;