窗口事件:Window Server Events

本文介绍了Symbian操作系统中WindowServer的事件处理机制,包括通用事件、重画事件及优先键事件的处理方式,详细解释了TWsEvent、TWsRedrawEvent和TWsPriorityKeyEvent类的使用。

Window Server中的Events(事件)被应用程序获取并处理。类似于微软Windows操作系统中的消息,事件通常源于用户的输入,由Wserv生成发送给客户端程序,如请求窗体重画。客户端程序通过WServ 会话来发送请求并获取事件。大部分事件(如键盘和鼠标事件)都封装为TWsEvent类。重画事件告诉应用程序屏幕的哪个区域需要重画,这类事件被封装在TWsRedrawEvent类中。

 

事件类型

 

Symbian OS 中的Window server有三种事件类型,通用事件、重画事件及优先键事件,它们分别封装在TWsEvent、TWsRedrawEvent、TWsPriorityKeyEvent类中。如下图所示:

 

 event type

 

 

 

                                                 图 (1)

 

这三种事件分别通过RWsSession的GetEvent()、GetRedraw()、GetPriorityKey()来获取。

其中通用事件又可以通过TWsEvent的Type()方法来获取事件码以区分不同的事件,如KeyUp、KeyDown事件等;通过TWsEvent的Pointer()方法来获取指针事件(TPointerEvent),TWsEvent的Key()方法来获取按键事件(TKeyEvent)。

 

需要指出的是,这里的Pointer(指针)是泛指指向屏幕的设备,如鼠标,输入笔,手指等。

 

下面对着三种类型的事件分别作一简单的介绍。

 

通用事件

通用事件对应的类是TWsEvent,代表了用户输入事件如指针和按键事件,这类事件用活动对象以标准优先级来处理。除重画和优先键事件外的通用事件由Window Server生成TWsEvent并发送给客户端,客户端通过异步方法RWsSession::EventReady()来请求事件,此执行方法结束后,会触发CActive::RunL(),在RunL函数里,使用RWsSession::GetEvent()来获取TWsEvent所代表的事件。然后通过TWsEvent::Type()和TWsEvent::EventData()来识别事件代码并获取相应的事件数据,从而进行处理。先表给出了不同的事件代码对应的事件数据和窗体句柄:

 

TWsEvent::Type() 对应w32std.h中的enum TEventCode

 

重画事件

 

重画事件对应的类是TWsRedrawEvent,这类事件用活动对象以较低优先级来处理。TWsRedrawEvent通过RWsSession::RedrawReady()来请求,请求返回后,重画事件可以通过RWsSession::GetRedraw()来获得事件。重画事件数据中包括了要重画的窗体的句柄及重画区域。

 

优先键事件

 

优先键事件对应的类是TWsPriorityKeyEvent,这类事件用活动对象以较高优先级来处理。在使用优先键事件机制前需要先用RWindowGroup::AddPriorityKey()来配置优先键。优先键通常是为应用程序提供“放弃(Abort)”或“退出(Escape)”键,即应用程序遇到这种事件要优先响应。

所有的优先键事件由Window Server生成并以TWsPriorityKeyEvent发送给客户端,客户端通过RWsSession::GetPriorityKey()来获取事件。

 

事件的处理(Handling Events)

 

所有的Window Server应用程序都处理标准事件,也几乎都处理重画事件,然而,却很少处理优先键事件。很少有程序不处理重画事件,除非程序所有窗体都是backed-up 窗体。事件总是伴随着窗体的,如按键事件伴随Window group;指针事件伴随可画窗体(drawable window)。这些事件都包含一个句柄,用来识别窗体,这个句柄就是窗体在创建时的句柄。在处理事件时,Symbian推荐不同的时间在不同的活动对象(Active Object)中处理,这样可以保证事件在不同优先级下分别被处理。

 

Window Server在服务端为应用程序维护一个事件队列缓冲区,每个应用程序有自己的事件队列区(这仅仅是对于通用事件来说,重画和优先键事件是另一种不同的处理机制)。应用程序必须适当地处理好事件。应用程序在处理时,首先通过RWsSeeeion::GetEvent()来获取事件,然后在分析处理事件。事件必须被快速处理以相应用户的输入,如果事件的处理比较复杂费时,应该将处理分成多个小的处理块,没个处理块在不同的活动对象中,且运行优先级低于GetEvent()。下面分类介绍事件的具体处理方法。

 

在处理事件时我们要先定义一个活动对象类,从CActive派生,自己实现RunL()。下面的代码段声明了一个处理事件的活动对象类CClientEventExample:

 

/* 活动对象类中包含一个 Window Server session. */

class CClientEventExample : public CActive   

    {

public:   

    // 继承自CActive

    void RunL ();

    void DoCancel();

    // 向Window Server请求事件

    void IssueRequest();

private:

    // Access to Window Server session

    RWsSession& iWs;

    // Access to screen device

    CWsScreenDevice& iScreen;

    // 用来保存收到的事件

    TWsEvent iWsEvent;

    };

一旦活动对象被创建,就可以发送请求。我们可以调用IssueRequest()来发送一个异步请求,这个请求前面提到过,使用RWsSession的EventReady()方法,其中参数TRequestStatus是活动对象的成员变量iStatus,如下代码段所示:

/* 向 Window Server请求事件 */

void CClientEventExample::IssueRequest()

    {

    // 请求事件

    iWs.EventReady(&iStatus);

    // 设置活动对象为active,即当这个异步请求返回时,Active scheduler会调用RunL

    SetActive();

    }

 

当客户端程序事件请求返回后,活动对象的RunL()函数会被调用。前面提到过,在RunL()获取事件对象,并根据不同事件代码和事件数据进行不同的处理。如下代码所示:

 

/*  当有事件发生时,Active scheduler调用RunL */

void CClientEventExample::RunL()

    {  

    // 用 Window Server session iWs来获取事件

    iWs.GetEvent(iWsEvent);

    // 获取事件代码,事件代码在 TEventCode中定义

    TInt eventType=iWsEvent.Type();

    // 处理事件

    switch (eventType)

       {

       /* 根据不同的事件代码来处理不同的事件 */

       /* 如按键事件 */

       case EEventKey:

case EEventKeyUp:

case EEventKeyDown:

    {

    // 这几个事件对应的事件数据类型是TKeyEvent

// 获取 key event

    TKeyEvent& keyEvent=*iWsEvent.Key();

    // 从 TKeyEvent中获得是哪个按键

    TUint code = keyEvent.iCode;

    break;

    }

/* 如控制或换档键事件(e.g. SHIFT) */

    case EEventModifiersChanged:

       {

       // 这个事件对应的事件数据类型是TModifiersChangedEvent

       // 获取modifier keys

       TModifiersChangedEvent& keyEvent=*iWsEvent.ModifiersChanged();

       // 获取具体的换挡或控制键(在TEventModifier中有定义)

       TUint modifiers = keyEvent.iModifiers;

       break;

}

        // 处理用户自定义的事件

       case EEventUser:

           {

           // 获取事件数据

           TAny* data = iWsEvent.EventData();

           break;

           }

       ...

       }

    }

 

无论是窗体、窗体组还是系统事件,都在如上所示的RunL中处理。具体如何获取事件数据和窗体句柄,查w32std.h中的enum TEventCode即可。

 

另外需要说明的是,还有类事件即TRawEvent,它是最原始的系统事件,来自硬件设备如键盘,鼠标或手写笔等。这类事件从硬件产生,送到系统内核的事件队列中,由内核处理后再生成前面讲的应用层的事件。

<think>嗯,用户要求我查找关于Android InputDispatcher无法找到触摸窗口的解决方案。这个问题看起来是Android系统层级的输入事件分发故障。 首先需要理解InputDispatcher的工作原理。InputDispatcher是Android输入系统的核心组件,负责将输入事件(如触摸事件)分发给正确的窗口。当出现"not find touched window"错误时,通常意味着系统无法确定哪个窗口应该接收当前触摸事件。 根据Android系统源码分析,这类问题可能涉及多个层面:窗口管理、焦点处理、权限配置或系统资源状态。常见触发点包括窗口焦点丢失、Z-order混乱、窗口未注册或SurfaceFlinger异常等。 用户可能是Android系统开发者或Framework工程师,正在调试系统级触摸失效问题。这类问题通常较棘手,需要系统化排查。用户情绪可能比较焦虑,因为触摸失效会直接影响设备基本功能。 我准备从四个维度构建解决方案:诊断流程、窗口管理、权限配置和系统健康度。诊断部分要强调关键日志的获取方法,特别是"InputDispatcher"和"WindowManager"标签的日志分析。窗口管理部分要解释焦点与Z-order的关联性,比如如何通过dumpsys window验证。权限部分需提醒检查SYSTEM_ALERT_WINDOW等敏感权限。系统健康度则涉及SurfaceFlinger和内存状态检查。 考虑到用户需要实际操作指导,每个方案都要包含可执行的命令或代码修改建议。比如使用adb shell dumpsys input验证触摸点状态,或者修改WindowManagerService的debug参数。 最后需要设置风险提示,因为部分调试操作需要系统权限或可能影响系统稳定性。特别是涉及SEAndroid策略调整时,要强调谨慎操作。 相关问题设计要覆盖关联场景:如其他输入事件异常、多窗口场景特例、ROM定制影响等,帮助用户扩展排查视野。特别加入"如何模拟复现"的问题,这对开发阶段的问题定位很重要。</think>以下是针对 **Android InputDispatcher 无法找到触摸窗口** 的系统级解决方案,结合底层原理和调试方法逐步分析: --- ### **问题根源分析** 当 `InputDispatcher` 报告 `not find touched window` 时,通常由以下原因导致: 1. **窗口焦点丢失** - 目标窗口未获得焦点(如 `FLAG_NOT_FOCUSABLE` 设置错误) 2. **Z-Order 混乱** - 窗口层级(Z-Order)未正确更新,导致触摸事件被错误遮挡 3. **窗口未注册** - `WindowManagerService` 未正确注册窗口(常见于 `addView()` 失败) 4. **SurfaceFlinger 异常** - 窗口的 Surface 未就绪或合成失败(如 GPU 资源耗尽) --- ### **解决方案** #### **1. 诊断窗口状态 (ADB 命令)** ```bash adb shell dumpsys window windows | grep -E "mCurrentFocus|mTouchedWindow" adb shell dumpsys input | grep "Focusability" ``` - **关键字段检查**: - `mCurrentFocus`:当前焦点窗口的 `Activity/WindowToken` - `mTouchedWindow`:最后一次接收触摸事件窗口 - 若输出为空,说明窗口未注册到 `WindowManagerService`[^1] #### **2. 修复窗口焦点问题** 在 `View` 或 `Window` 初始化代码中: ```java // 确保窗口可获取焦点 getWindow().addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE); // ❌ 避免使用 getWindow().clearFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE); // ✅ 正确设置 ``` #### **3. 强制更新 Z-Order** 在 `onResume()` 中触发窗口层级更新: ```java View decorView = getWindow().getDecorView(); decorView.getViewTreeObserver().dispatchOnGlobalLayout(); // 强制刷新布局 decorView.bringToFront(); // 确保置于顶层 ``` #### **4. 检查 SurfaceFlinger 状态** ```bash adb shell dumpsys SurfaceFlinger | grep -A 5 "Invalid" ``` - 若出现 `Invalid layer` 或 `BufferQueue abandoned`,表明 Surface 未就绪: - 解决方案:在 `SurfaceHolder.Callback.surfaceCreated()` 中延迟触摸事件处理 #### **5. 启用 InputDispatcher 调试日志** ```java // 在系统层修改(需 root) adb shell setprop log.tag.InputDispatcher DEBUG adb shell stop && adb shell start ``` - 日志路径:`adb logcat -b events | grep "InputDispatcher"` --- ### **关键日志分析** 当问题发生时,检查以下日志标签: ```log W/InputDispatcher: Failed to find touched window. D/InputDispatcher: Focus request: reason=FocusRequest [reason=TOUCH, ...] E/WindowManager: Token null is not valid; is your activity running? ``` - **错误 `Token null`**:表明 `Activity` 的 `WindowToken` 已被销毁(如 Activity 泄漏)[^2] --- ### **高级修复方案** 1. **Hook `InputDispatcher` 分发逻辑** 通过 Xposed 模块拦截 `InputDispatcher.findTouchedWindow()` 方法,强制指定窗口(需系统权限): ```java XposedHelpers.findAndHookMethod("com.android.server.input.InputDispatcher", lpparam.classLoader, "findTouchedWindow", ...); ``` 2. **重写 `WindowManagerService` 的焦点策略** 修改 `WMS.updateFocusedWindowLocked()` 的焦点分配算法(仅限 AOSP 定制系统) --- ### **相关问题** 1. **如何避免 `FLAG_NOT_FOCUSABLE` 导致触摸失效?** > 解决方案:在 `Dialog` 或 `PopupWindow` 显示后调用 `window.setFocusable(true)` 并手动请求焦点。 2. **`SurfaceFlinger` 合成失败如何影响触摸事件?** > 原理:若窗口的 Surface 未合成,`InputDispatcher` 会跳过该层级的触摸检测[^3]。 3. **多显示屏场景下触摸事件如何正确路由?** > 关键点:通过 `Display.getDisplayId()` 匹配 `InputChannel`,需在 `InputManagerService` 中校验显示 ID。 4. **如何模拟复现 "not find touched window" 错误?** > 方法:在开发者选项中启用 **"强制将活动设为可调整大小"**,并快速旋转屏幕触发窗口重建竞争。 --- > **风险提示**:修改系统级输入分发逻辑可能导致全局触摸失效,建议在刷机前备份系统镜像。若为 OEM 设备,优先检查厂商定制的 `WindowManagerPolicy` 实现。 [^1]: 参考 Android AOSP `WindowManagerService` 窗口注册流程 [^2]: 见 `ActivityRecord.onWindowRemoved()` 中的 `WindowToken` 清理机制 [^3]: `SurfaceFlinger` 合成状态与 `InputDispatcher` 的图层可见性同步协议
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值