【WMS学习】从悬浮窗的添加来看窗口的add和update

article/2023/6/4 14:41:54

这里我们从一个悬浮窗应用来查看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

http://www.ngui.cc/article/show-1007670.html

相关文章

领域驱动设计(Domain-Driven Design, DDD)

领域驱动设计&#xff08;Domain Driven Design&#xff0c;简称DDD&#xff09;是一种面向对象软件开发方法&#xff0c;它强调将软件系统的设计和实现过程与业务领域紧密结合&#xff0c;通过深入理解和建模业务领域&#xff0c;从而达到高内聚、低耦合的目的。 领域驱动设计…

【ChatGPT】比尔·盖茨最新分享:ChatGPT的发展,不止于此

✅作者简介&#xff1a;在读博士&#xff0c;伪程序媛&#xff0c;人工智能领域学习者&#xff0c;深耕机器学习&#xff0c;交叉学科实践者&#xff0c;周更前沿文章解读&#xff0c;提供科研小工具&#xff0c;分享科研经验&#xff0c;欢迎交流&#xff01;&#x1f4cc;个人…

【学习总结】IMU噪声的连续形式与离散形式

乱七八糟的&#xff0c;查了半天资料&#xff0c;整理如下。 &#xff08;网上其他地方的资料也很混乱&#xff0c;这篇总结是我综合比对&#xff0c;得出的结论&#xff09; 统一符号 连续形式&#xff1a; gyroscope white noise: σg\sigma_gσg​ accelerator white nois…

[puzzle-5]目标图形中拼图块能够存放的位置

有如下的八种拼图块,每块都是由八块小正方块构成, 这些拼图块刚好可以某种方式拼合放入给定的目标形状, 请以C或C++编程,自动求解 一种拼图方式 目标拼图: 从拼图块和目标图形中我们可以发现目标图形是8*8=64个方块,也就是目标图形需要使用上述8中拼图进行拼接,每个使…

CentOS挂载U盘拷贝文件

1.登录linux操作系统&#xff0c;将U盘插入主机 2.新建一个目录将U盘挂载到该目录 使用命令: mkdir /mnt/usb 3.查看可用的挂载点 使用命令&#xff1a; fdisk -l 4. 将U盘挂载到刚才建立的目录下 使用命令: mount /dev/sdb4 /mnt/usb 5.查看U盘识别情况 使用命令 &#x…

【生活工作经验 十】ChatGPT模型对话初探

最近探索了下全球大火的ChatGPT&#xff0c;想对此做个初步了解 一篇博客 当今社会&#xff0c;自然语言处理技术得到了迅速的发展&#xff0c;人工智能技术也越来越受到关注。其中&#xff0c;基于深度学习的大型语言模型&#xff0c;如GPT&#xff08;Generative Pre-train…

ES6技术总结与测试用例

一、介绍 ES6全称是ECMAScript ECMAScript 和 JavaScript 的关系 一个常见的问题是&#xff0c;ECMAScript 和 JavaScript 到底是什么关系&#xff1f; 要讲清楚这个问题&#xff0c;需要回顾历史。1996 年 11 月&#xff0c;JavaScript 的创造者 Netscape 公司&#xff0c…

【Autoware规控】mpc_follower模型预测控制节点

文章目录1. 技术原理2. 代码实现1. 技术原理 MPC&#xff0c;即Model Predictive Control&#xff08;模型预测控制&#xff09;&#xff0c;是一种基于动态模型的控制算法。MPC算法通过建立系统的数学模型&#xff0c;根据当前状态和一定时间内的预测&#xff0c;优化未来的控…

【蓝桥杯集训·每日一题】AcWing 3662. 最大上升子序列和

文章目录一、题目1、原题链接2、题目描述二、解题报告1、思路分析2、时间复杂度3、代码详解三、知识风暴树状数组一、题目 1、原题链接 3662. 最大上升子序列和 2、题目描述 给定一个长度为 n 的整数序列 a1,a2,…,an。 请你选出一个该序列的严格上升子序列&#xff0c;要求所…

Oracle 启动后一会儿就挂掉故障处理—ORA-600 17182----惜分飞

一例正常运行的数据库突然节点不停重启(因为是rac,启动一会儿就crash,然后又被crs给启动起来,然后有crash,依次循环),告警日志类似:Fri Mar 24 13:36:07 2023QMNC started with pid124, OS id188397 ARC3: Archival startedARC0: STARTING ARCH PROCESSES COMPLETECompleted: A…