这里我们从一个悬浮窗应用来查看WindowManager的addView使用,从这里作为突破口来认识窗口的添加,和窗口的位置大小更新方法updateViewLayout,使用WindowManager的addView方法来添加窗口非常的直观,因为Activity的显示中,窗口的添加调用封装的太深了,
以下代码参考自https://blog.csdn.net/liufish992/article/details/122338344
private void initUi() {
DisplayMetrics metrics = getApplicationContext().getResources().getDisplayMetrics();
int width = metrics.widthPixels;
int height = metrics.heightPixels;
windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
LayoutInflater inflater = (LayoutInflater) getBaseContext().getSystemService(LAYOUT_INFLATER_SERVICE);
floatView = (ViewGroup) inflater.inflate(R.layout.float_layout, null);
int layoutType;
layoutType = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
WindowManager.LayoutParams floatLp = new WindowManager.LayoutParams(
(int) (width * (0.4f)),
(int) (height * (0.3f)),
layoutType,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT
);
mLp = floatLp;
floatLp.gravity = Gravity.LEFT;//Gravity.CENTER;
floatLp.x = 0;
floatLp.y = 0;
windowManager.addView(floatView, floatLp);
位置变化设置
void move() {
mLp.x = 100;
mLp.y = 100;
windowManager.updateViewLayout(floatView, mLp);
}
使用dumpsys window windows查看
悬浮窗的信息,ty=APPLICATION_OVERLAY,appop=SYSTEM_ALERT_WINDOW
Window #8 Window{d3f21db u0 com.example.myapplication}:
mDisplayId=0 rootTaskId=1 mSession=Session{e489994 10075:u0a10159} mClient=android.os.BinderProxy@1cdc2d5
mOwnerUid=10159 showForAllUsers=false package=com.example.myapplication appop=SYSTEM_ALERT_WINDOW
mAttrs={(100,100)(432x646) gr=CENTER sim={adjust=pan} ty=APPLICATION_OVERLAY fmt=TRANSLUCENT
fl=NOT_FOCUSABLE HARDWARE_ACCELERATED
pfl=USE_BLAST INSET_PARENT_FRAME_BY_IME
bhv=DEFAULT
fitTypes=STATUS_BARS NAVIGATION_BARS CAPTION_BAR}
Requested w=432 h=646 mLayoutSeq=1429
mBaseLayer=111000 mSubLayer=0 mToken=WindowToken{bb70aea type=2038 android.os.BinderProxy@1cdc2d5}
Activity的信息,ty=BASE_APPLICATION
Window #11 Window{e656d83 u0 com.example.myapplication/com.example.myapplication.MainActivity}:
mDisplayId=0 rootTaskId=34 mSession=Session{e489994 10075:u0a10159} mClient=android.os.BinderProxy@3ae1c32
mOwnerUid=10159 showForAllUsers=false package=com.example.myapplication appop=NONE
mAttrs={(0,0)(fillxfill) sim={adjust=pan forwardNavigation} ty=BASE_APPLICATION wanim=0x10302fd
fl=LAYOUT_IN_SCREEN LAYOUT_INSET_DECOR SPLIT_TOUCH HARDWARE_ACCELERATED DRAWS_SYSTEM_BAR_BACKGROUNDS
pfl=NO_MOVE_ANIMATION FORCE_DRAW_STATUS_BAR_BACKGROUND USE_BLAST FIT_INSETS_CONTROLLED
bhv=DEFAULT
fitSides=}
Requested w=1080 h=2220 mLayoutSeq=1429
mBaseLayer=21000 mSubLayer=0 mToken=ActivityRecord{34f5722 u0 com.example.myapplication/.MainActivity} t34}
mActivityRecord=ActivityRecord{34f5722 u0 com.example.myapplication/.MainActivity} t34}
布局参数LayoutParams中最直观的就是窗口位置和大小,如果是全屏的,就不像是悬浮窗了。
Activity的显示中,也是会走到WindowManager的addView这里。
WindowManager的addView方法调用WindowManagerGlobal的addView,里面创建了ViewRootImpl对象,

注意这里布局参数LayoutParms的使用,ViewRootImpl在绘图时,使用了这个参数来构造悬浮窗。
修改窗口位置的调用updateViewLayout调用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);
}
}
通过 root.setLayoutParams(wparams, false) 方法,更新了 ViewRootImpl 的 LayoutParams
在 ViewRootImpl 的 setLayoutParams 方法中,会通过 scheduleTraversals 方法来对 View 重新布局和绘制,包括测量、布局、重绘这三个过程。
// ViewRootImpl.java:
void setLayoutParams(WindowManager.LayoutParams attrs, boolean newView) {
scheduleTraversals();
}
scheduleTraversals 调用PerformTraversals, 又调用relayoutWindow方法,
应用程序调用relayoutWindow来请求WMS为其创建一个Surface对象。frameworks\base\core\Java\android\view\ViewRootImpl.Java
private int relayoutWindow(WindowManager.LayoutParams params, int viewVisibility,
boolean insetsPending) throws RemoteException {
float appScale = mAttachInfo.mApplicationScale;
boolean restore = false;
if (params != null && mTranslator != null) {
restore = true;
params.backup();
mTranslator.translateWindowLayout(params);
}
if (params != null) {
if (DBG) Log.d(mTag, "WindowLayout in layoutWindow:" + params);
}
mPendingMergedConfiguration.getMergedConfiguration().seq = 0;
//Log.d(mTag, ">>>>>> CALLING relayout");
if (params != null && mOrigWindowType != params.type) {
// For compatibility with old apps, don't crash here.
if (mTargetSdkVersion < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
Slog.w(mTag, "Window type can not be changed after "
+ "the window is added; ignoring change of " + mView);
params.type = mOrigWindowType;
}
}
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,
mWinFrame, mPendingOverscanInsets, mPendingContentInsets, mPendingVisibleInsets,
mPendingStableInsets, mPendingOutsets, mPendingBackDropFrame,
mPendingMergedConfiguration, mSurface);//这里传入变量mSurface
mPendingAlwaysConsumeNavBar =
(relayoutResult & WindowManagerGlobal.RELAYOUT_RES_CONSUME_ALWAYS_NAV_BAR) != 0;
//Log.d(mTag, "<<<<<< BACK FROM relayout");
if (restore) {
params.restore();
}
if (mTranslator != null) {
mTranslator.translateRectInScreenToAppWinFrame(mWinFrame);
mTranslator.translateRectInScreenToAppWindow(mPendingOverscanInsets);
mTranslator.translateRectInScreenToAppWindow(mPendingContentInsets);
mTranslator.translateRectInScreenToAppWindow(mPendingVisibleInsets);
mTranslator.translateRectInScreenToAppWindow(mPendingStableInsets);
}
return relayoutResult;
}
WMS服务在响应应用程序进程请求relayout调用时,首先在当前进程空间创建一个Surface对象,然后调用Session的relayout()函数进一步完成窗口布局过程,最后将WMS服务中创建的Surface返回给应用程序。
WMS中
frameworks\base\services\core\Java\com\android\server\wm\WindowManagerService.Java

同时,WMS的处理中也调用addWindow方法创建了对应的WindowState窗口对象。

猜想:在多窗口调用中,设置的ActivityOptions的bounds信息,在调用流程中,会传递给LayoutParams来控制窗口的位置和大小。
Intent it = new Intent(MainActivity.this, MainActivity2.class);
ActivityOptions ops = ActivityOptions.makeBasic();
ops.setLaunchBounds(new Rect(100, 100, 600, 700));
startActivity(it, ops.toBundle());
参考资料:
【Android】Window 机制_android window_一场雪ycx的博客-CSDN博客
https://blog.csdn.net/yangwen123/article/details/80674965?spm=1001.2014.3001.5502
https://blog.csdn.net/liufish992/article/details/122338344