浮窗开发之窗口层级

简介: 最近在项目中遇到了这样的需求:需要在特定的其他应用之上悬浮自己的UI交互(拖动、输入等复杂的UI交互),和九游的浮窗类似,不过我们的比九游的体验更好,我们越过了很多授权的限制。 很多人都知道如何去实现一个简单的浮窗,但是却很少有人去深入的研究背后的流程机制,由于项目中浮窗交互比较复杂,遇到了些

最近在项目中遇到了这样的需求:需要在特定的其他应用之上悬浮自己的UI交互(拖动、输入等复杂的UI交互),和九游的浮窗类似,不过我们的比九游的体验更好,我们越过了很多授权的限制。

浮窗效果
很多人都知道如何去实现一个简单的浮窗,但是却很少有人去深入的研究背后的流程机制,由于项目中浮窗交互比较复杂,遇到了些坑查看了很多资料,故总结浮窗涉及到的知识点:

  • 窗口层级关系(浮窗是如何“浮”的)?
  • 浮窗有哪些限制,如何越过用户授权实现浮窗功能?
  • 窗口与用户输入系统(Activity是如何接收到touch事件的)?

本章我们来研究第一个问题:浮窗为何会浮。浮窗之所以叫浮窗,是因为它能悬浮于应用或者桌面窗口之上,能脱离Activity而存在。为了研究其中区别,我们先来看看我们最熟悉的Activity是怎么显示出来的。

Activity是怎么显示出来的?

要弄清这个问题答案,我们先从Activity的setContentView()这个方法的源码开始找起,在Activity中看到setCententView的源码:

public void setContentView(int layoutResID) {
    getWindow().setContentView(layoutResID);
    initWindowDecorActionBar();
}

getWindow是返回返回Activity的mWindow变量,指向一个Window的对象,Window是一个抽象类,这里返回的是PhoneWindow对象(PhoneWindow是Window的子类),PhoneWindow中有一个DecorView对象,decorView成员,这是一个FrameLayout,setContentView的子布局最终会添加到decorView中,这个decorView就是当前窗口的根视图,这个根视图是如何最终被绘制出来的?在ActivityThread中有这样一段代码:

            l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
            l.softInputMode |= forwardBit;
            if (a.mVisibleFromClient) {
                a.mWindowAdded = true;
                wm.addView(decor, l);
            }

这个decorView,最终会被WindowManager.addView添加到绘制系统中,并类型是WindowManager.LayoutParams.TYPE_BASE_APPLICATION,这个参数决定了要绘制的窗口的z轴层次,为了避免思维栈过深,这里就不贴出详细的源码跟踪过程了,直接给结论。
先来看看Activity和window的关系:

Activity-window.jpg
再来window和View的关系:
window-view.jpg
Activity窗口显示过程:

Activity的显示过程.png
说Activity是怎么显示出来的,其实是说Activity管理的View是怎么显示出来的。最后再来总结一下:

一、Activity通过setContentView设置的视图是添加到PhoneWindow的根视图decor中。

二、Window是一个抽象的概念,Window关了了一个View(根视图),最终被WindowManager管理的还是一个View(根视图)和它的LayoutParams,视图绘制刷新都是通过WindowManager(WindowManagerGlobal)与WindowManagerServiceIPC交互调用底层绘制的。

三、Activity是四大组件中唯一和窗体紧密联系的组件(这是为什么会有初学者把Activity直接理解为绘制界面的原因),所有掌管的视图只不过是一种window和Dialog、Toast、墙纸所掌管的Window类型不一样。

浮窗为什么会“浮”?

上面讲到Activity的显示过程其实已经揭示了通用界面的显示过程,浮窗的显示过程更为简单:

通用窗体绘制过程.png
做过浮窗的同学应该都明白了,为啥浮窗能脱离Activity而显示,本质上我们是把一个View交给WindowManager来管理了,LayoutParams.type类型决定了这个View显示窗口的类型,不同类型显示的窗口层次(z轴)是不一样的。大方面来讲可以分为应用窗口(APPLICATION_WINDOW)、子窗口(SUB_WINDOW)、系统窗口(SYSTEM_WINDOW)三种类型,应用窗口z轴范围是1~99,子窗口的范围是1001~1999,系统窗口是(2000~2999),所以要实现浮动窗口我们只能在系统窗口范围中实现。


窗体类型

到这里我们对Android系统的窗口层次有个大致的了解了,Activity是Android应用的四大组件之一,描述的是应用的活动状态和周期,受ActivityManagerService的管理;Window/View是图形窗口的抽象模型,描述的是窗口的绘制信息,受WindowManagerService的管理;Activity聚合Window来和图形窗口产生联系。文章旨在理解一下Android窗体系统的一个雏形,能力有限不能详细跟踪整个窗口体系的源码,有兴趣的可以自己深入,下一篇文章:《越过授权使用浮窗》。

作者声明:本人只在简书云栖本人博客中发表文章,转载需联系本人取得同意!读者阅读时若出现排版混乱、广告等影响阅读体验说明不是原文,建议到简书云栖本人博客中阅读原文以取得更好的阅读体验。

目录
相关文章
|
API C# Windows
Winform控件优化之无边框窗体及其拖动、调整大小和实现最大最小化关闭功能的自定义标题栏效果
Winform中实现无边框窗体只需要设置FormBorderStyle = FormBorderStyle.None,但是无边框下我们需要保留移动窗体、拖拽调整大小、自定义美观好看的标题栏等...
3502 0
Winform控件优化之无边框窗体及其拖动、调整大小和实现最大最小化关闭功能的自定义标题栏效果
Qml实用技巧:在可视元素之前半透明覆盖一个可视元素,阻止鼠标透(界面)传(防止点击到被遮挡的按钮)
Qml实用技巧:在可视元素之前半透明覆盖一个可视元素,阻止鼠标透(界面)传(防止点击到被遮挡的按钮)
Qml实用技巧:在可视元素之前半透明覆盖一个可视元素,阻止鼠标透(界面)传(防止点击到被遮挡的按钮)
|
2月前
|
前端开发 JavaScript
原生撸移动端顶部滚动菜单栏,实现可滚动控制滚动边界动态样式
本文介绍了如何使用原生HTML、CSS和JavaScript创建一个移动端可滚动的顶部菜单栏。文章提供了详细的HTML结构、CSS样式和JavaScript代码,实现了菜单项的横向滚动、边界控制和动态样式变化。同时,还展示了如何通过触摸事件监听来控制菜单项的滚动和激活状态。
56 2
原生撸移动端顶部滚动菜单栏,实现可滚动控制滚动边界动态样式
|
3月前
|
开发框架 前端开发 搜索推荐
在WInform开发中实现工具栏/菜单的动态呈现
在WInform开发中实现工具栏/菜单的动态呈现
|
6月前
|
定位技术 iOS开发
在地图页面,自动布局控件开始是隐藏或在屏幕外需要正常显示时再为正常的显示状态的,需要在显示之前加入
在地图页面,自动布局控件开始是隐藏或在屏幕外需要正常显示时再为正常的显示状态的,需要在显示之前加入
49 0
|
6月前
简单讲述ondragstart、drag、ondragend、ondragenter、ondragover、ondrop、ondragleave七个与拖拽相关的监听事件,并运用实现拖拽过程放置样式变化
简单讲述ondragstart、drag、ondragend、ondragenter、ondragover、ondrop、ondragleave七个与拖拽相关的监听事件,并运用实现拖拽过程放置样式变化
|
6月前
【sgDragMove】自定义组件:自定义拖拽组件,仅支持拖拽、设置吸附屏幕边界距离。
【sgDragMove】自定义组件:自定义拖拽组件,仅支持拖拽、设置吸附屏幕边界距离。
|
算法 API C#
Winform控件优化之圆角按钮【各种实现中的推荐做法】(下)
最终优化实现ButtonPro按钮(继承自Button),既提供Button原生功能,又提供扩展功能,除了圆角以外,还实现了圆形、圆角矩形的脚尖效果、边框大小和颜色、背景渐变颜色...
1968 0
Winform控件优化之圆角按钮【各种实现中的推荐做法】(下)
|
前端开发 Android开发 iOS开发
Andorid分组Item顶部悬停 + 交互同步
  项目中某些页面中的分组数据的顶部需要悬停,并且悬停的View要与ItemView中同样布局的View进行操作同步,也就是相互同步。大家都知道,Android中有"The specified child already has a parent. You must call removeView() on the child's parent first."这个异常,意味着同一个View对象不能有两个Parent。我们就不能简单粗暴的将同一个View对象添加进两个parent了,需要另谋出路。
|
C# 图形学 Windows
Winform控件优化之圆角按钮【各种实现中的推荐做法】(上)
Windows 11下所有控件已经默认采用圆角,其效果更好、相对有着更好的优化...尝试介绍很常见的圆角效果,通过重写控件的OnPaint方法实现绘制,并在后面进一步探索对应的优化和可能的问题
1423 0
Winform控件优化之圆角按钮【各种实现中的推荐做法】(上)