Android WMS工作原理浅析(二)

本文深入探讨了WMS的工作原理,包括窗口管理、动画及事件派发机制。详细解析了addWindow与removeWindow流程,以及事件派发的核心类InputReader与InputDispatcher。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

前面一篇文章对WMS做了一些简单的介绍,如果您对WMS还没有初步的认知,可以先移步前一篇文章Android WMS工作原理浅析(一)

WMS的职责

WMS的职责

WMS中重要的一些属性释义

WMS中的属性
从WMS类中包含的属性也可以看出WMS的主要职责,窗口管理、窗口动画以及监听输入进行事件派发。

WMS中addWindow源码分析

在分析addWindow之前,先了解几个类;
** WindowToken**:WindowToken具有令牌的作用,是对应用组件的行为进行规范管理的一个手段。WindowToken由应用组件或其管理者负责向WMS声明并持有。应用组件在需要新的窗口时,必须提供WindowToken以表明自己的身份,并且窗口的类型必须与所持有的WindowToken的类型一致,同时它将属于同一个应用组件的窗口组织在了一起;

DisplayContent:如果说WindowToken按照窗口之间的逻辑关系将其分组,那么DisplayContent则根据窗口的显示位置将其分组。隶属于同一个DisplayContent的窗口将会被显示在同一个屏幕中。每一个DisplayContent都对应这一个唯一的ID,在添加窗口时可以通过指定这个ID决定其将被显示在那个屏幕中。DisplayContent是一个非常具有隔离性的一个概念。处于不同DisplayContent的两个窗口在布局、显示顺序以及动画处理上不会产生任何耦合。因此,就这几个方面来说,DisplayContent就像一个孤岛,所有这些操作都可以在其内部独立执行。因此,这些本来属于整个WMS全局性的操作,变成了DisplayContent内部的操作了。

WindowState:表示一个窗口的所有属性,所以它是WMS中事实上的窗口;当向WMS添加一个窗口时,WMS会为其创建一个WindowState。另外WMS.addWindow()函数中看到新的WindowState被保存到mWindowMap中,键值为IWindow的Bp端。mWindowMap是整个系统所有窗口的一个全集。

WMS.addWindow()流程浅析
.......
//窗口检查,对要添加的窗口进行检查,如果窗口不满足条件,则直接返回
 int res = mPolicy.checkAddPermission(attrs.type, isRoundedCornerOverlay, attrs.packageName,appOp);
 if (res != WindowManagerGlobal.ADD_OKAY) {
      return res;
}
.......
//WindowToken相关处理,有的窗口类型需要提供WindowToken
WindowToken token = displayContent.getWindowToken(hasParent ? parentWindow.mAttrs.token : attrs.token);
// If this is a child window, we want to apply the same type checking rules as the
// parent window type.
final int rootType = hasParent ? parentWindow.mAttrs.type : type;
boolean addToastWindowRequiresToken = false;
if (token == null) {
     if (!unprivilegedAppCanCreateTokenWith(parentWindow, callingUid, type,
                        rootType, attrs.token, attrs.packageName)) {
                    return WindowManagerGlobal.ADD_BAD_APP_TOKEN;
        }
      final IBinder binder = attrs.token != null ? attrs.token : client.asBinder();
      token = new WindowToken(this, binder, type, false, displayContent, session.mCanAddInternalSystemWindow, isRoundedCornerOverlay);
       } else if (rootType >= FIRST_APPLICATION_WINDOW
          && rootType <= LAST_APPLICATION_WINDOW) {}
......
//创建windowState
final WindowState win = new WindowState(this, session, client, token, parentWindow,appOp[0], seq, attrs, viewVisibility, session.mUid, userId,session.mCanAddInternalSystemWindow);
......
//调用WMP的方法,此方法会根据窗口的Type对LayoutParams的一些成员进行修改
displayPolicy.adjustWindowParamsLw(win, win.mAttrs, callingPid, callingUid);
.......
// 将窗口添加到系统中
res = displayPolicy.validateAddingWindowLw(attrs, callingPid, callingUid);
.......
// windowState保存到Map中
mWindowMap.put(client.asBinder(), win);
.......
// 绑定Token和WindowState关系
win.mToken.addWindow(win);
WMS.removeWindow()流程
void removeWindow(Session session, IWindow client) {
        synchronized (mGlobalLock) {
           	//获取WindowState
            WindowState win = windowForClientLocked(session, client, false);
            if (win != null) {
                //执行删除
                win.removeIfPossible();
                return;
            }

            // Remove embedded window map if the token belongs to an embedded window
            mEmbeddedWindowController.remove(client);
        }
    }

win.removeIfPossible方法和它的名字一样,并不是直接执行删除操作,而是进行多个条件判断过滤,满足其中一个条件就会return,推迟删除操作。比如View正在运行一个动画,这是就会推迟删除操作直到动画完成。然后调用removeImmediately方法。

事件派发

事件输入监听以及分发

事件分发图解
EventHub:1.使用inotify监听输入设备的添加和移除;2.使用epoll机制监听输入设备的数据变化;3.读取设备文件数据;4.将原始数据返回给InputReader;
InputReader:不断读取由EventHub监听到的input事件,将多个事件组合成一个可供上层消费的事件(比如将一组触摸事件合并成一个action_down事件),然后交给InputDispatcher进行事件分发;
InputDispatcher:拿到InputReader获取的事件后,对事件进行包装,寻找并分发到目标窗口,对应inputChannel输入;

Android系统是由事件驱动的,而input是常见的事件之一,点击、滑动、长按等操作,都属于input事件,核心类就是InputReader和InputDispatcher;
java层事件分发时序图

解释:
ViewRootImpl#WindowInputEventReceiver:从下面源码可以看出,此类用于InputChannel输入事件接收以及处理输入事件分发;

 final class WindowInputEventReceiver extends InputEventReceiver {
 		//构造方法,接受inputchannel输入事件
        public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
            super(inputChannel, looper);
        }

        @Override
        public void onInputEvent(InputEvent event) {
         	...
            if (processedEvents != null) {
                if (processedEvents.isEmpty()) {
                    // InputEvent consumed by mInputCompatProcessor
                    finishInputEvent(event, true);
                } else {
                    for (int i = 0; i < processedEvents.size(); i++) {
                    	//对输入事件进行分发
                        enqueueInputEvent(
                                processedEvents.get(i), this,
                                QueuedInputEvent.FLAG_MODIFIED_FOR_COMPATIBILITY, true);
                    }
                }
            } else {
                enqueueInputEvent(event, this, 0, true);
            }
        }

ViewRootImpl#InputStage:抽象类,主要用来将事件的处理分成若干个阶段(stage)进行,如果该事件没有被处理,则该stage就会调用onProcess方法处理,然后调用forward执行下一个stage的处理;如果该事件被标识为处理则直接调用forward,执行下一个stage的处理,直到没有下一个stage;

ViewPostImeInputStage:InputStage的子类,将输入事件传递到上层视图;
至此,输入事件一层层向上传递,最终交由具体的view进行处理;

结语

如果以上文章对您有一点点帮助,希望您不要吝啬的点个赞加个关注,您每一次小小的举动都是我坚持写作的不懈动力!ღ( ´・ᴗ・` )

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值