Android Framework 框架系列之 PhoneWindowManager

本文详细介绍Android开发中按键客制化的方法,包括修改实体按键功能、调整虚拟导航栏、实现多窗口模式及解决启动窗口问题。适用于Android开发者提升应用用户体验。

640?wx_fmt=gif

640?wx_fmt=gif

极力推荐Android 开发大总结文章:欢迎收藏程序员Android 力荐 ,Android 开发者需要的必备技能

640?wx_fmt=jpeg

Android 中会有以下5个按键(BackHomeMenuPowerVolume)与用户进行交互,Framework层中实现按键功能,因此,从手机系统定制的角度,可以满足客户的客制化要求。本文主要从Framework层浅析这些客制化需求的实现。

640?wx_fmt=png

Back、Home、Menu、Power、Volume 按键图

本篇文章主要介绍 Android 开发中的部分知识点,通过阅读本篇文章,您将收获以下内容:

  1. Android 按键修改相关的类

  2. PhoneWindowManager 简介

  3. 如何打开 或者 关闭 Navigation Bar

  4. 如何长按Home 键启动Google Now

  5. 如何长按实体Menu键进入多窗口模式

  6. 如何点击 Menu键进入调出最近任务列表

  7. 如何让App拿到Power key 值

  8. 如何修Activity启动是的窗口(app启动白屏,黑屏问题)

  9. WindowManagerPolicy 简介

1. Android 按键修改相关的类

MTK 平台为例,按键客制化的代码主要存放在以下类中

  1. PhoneWindowManager

PhoneWindowManager代码路径如下:

\alps\frameworks\base\services\core\java\com\android\server\policy\PhoneWindowManager.java
  1. WindowManagerPolicy

PhoneWindowManager 实现 的接口类WindowManagerPolicy 代码路径如下:

alps\frameworks\base\core\java\android\view\WindowManagerPolicy.java

2. PhoneWindowManager 简介

PhoneWindowManager 类实现接口如下:

java.lang.Object
    ↳  android.view.WindowManagerPolicy.java
         ↳ com.android.server.policy.PhoneWindowManager.java

640?wx_fmt=png

PhoneWindowManager 类实现关系

PhoneWindowManager主要用于实现各种实体或虚拟按键处理,如需特殊处理按键,请修改源码。

3. 如何打开 或者 关闭 Navigation Bar

640?wx_fmt=png

虚拟导航栏

解决方法:

1. 修改config.xml 文件中

搜索关键字config_showNavigationBar, 查看 config_showNavigationBar 值true 表示显示,false 表示不显示

  <!-- Whether a software navigation bar should be shown. NOTE: in the future this may be
         autodetected from the Configuration. -->
    <bool name="config_showNavigationBar">true</bool>

参考路径如下:alps\frameworks\base\core\res\res\values\config.xml

2. 修改 system.prop 文件

查询关键字 qemu.hw.mainkeys,并查看值,1表示关闭0.表示开启 。

# temporary enables NAV bar (soft keys)qemu.hw.mainkeys=1

不同项目文件存放地址不一样,可以使用以下命令查找

find 路径 -name "文件名.java"

或者直接查找文件中的字符串

 find 路径 -type f -name "文件名" | xargs grep "文件中的字符串"

3. 修改PhoneWindowManager代码

如果上面两个修改都不生效(搜索关键字config_showNavigationBarqemu.hw.mainkeys),请在PhoneWindowManager 查看setInitialDisplaySize方法中mHasNavigationBar 的值是否被写死,true表示会显示、false表示不显示导航栏。

640?wx_fmt=png

底部导航卡显示代码控制

4. 如何长按Home 键启动Google Now

1. 预制 Google Now APK

请自行安装APK

2. 修改 PhoneWindowManager 代码

长按Home键启动Google Now,实现方法参考launchAssistLongPressAction 功能实现。

640?wx_fmt=png

PhoneWindowManager 长按Home 建启动Google Now

自己实现常按Home 键吊起Google Now方法,供在按键分发处理事件时候调用。

640?wx_fmt=png

自己实现常按Home 键吊起Google Now 方法

3. 在按键事件分发之前处理

在按键分发处理之前调用自定义长按Home键的方法

640?wx_fmt=png

自定义长按Home 键的方法

1.双击Home 键调出最近任务列表请用以下方法

phoneWindowManager.java 的interceptKeyBeforeQueueing 方法中修改

640?wx_fmt=png

双击Home 键调出最近任务列表

5. 如何长按实体Menu键进入多窗口模式

Android N上支持Multi-Window,通过recent key进入多窗口,对于没有打开虚拟导航栏,只有实体menu按键的手机,可以考虑向SystemUI发送广播的形式,进入Android 分屏多任务模式。

1. PhoneStatusBar 里注册广播

PhoneStatusBar 是SystemUI模块的代码,参考路径如下:

frameworks/base/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java

自定义广播实现可以参考系统mDemoReceiver 的实现方法

640?wx_fmt=png

自定义广播注册

自定义接收广播后,onReceive处理事件实现分屏方法如下:

640?wx_fmt=png

自定义接收广播处理

2. PhoneWindowManager 中发送广播

在 PhoneWindowManagerinterceptKeyBeforeDispatching方法中发送广播

640?wx_fmt=png

interceptKeyBeforeDispatching 发送广播

  1. Destory 方法注销广播

Destory方法中记得一定要注销广播

 mContext.unregisterReceiver(mDemoReceiver); 
 mContext.unregisterReceiver(mAppLongSwitchReceiver);

6. 如何点击 Menu键进入调出最近任务列表

如果想调出最近任务列表,需要拦截menu的事件,在PhoneWindowManagerinterceptKeyBeforeDispatching 中处理即可

640?wx_fmt=png

menu 键调出最近任务列表

如果想长按Menu调出可以使用以下方法

640?wx_fmt=png

长按menu 键调出任务列表

7. 如何让 App 拿到Power key 值

一般情况下App是拿不到PowerKey值,但通过以下方法可以实现。

1. 修改PhoneWindowManager 文件实现

PhoneWindowManager 中修改interceptKeyBeforeQueueing方法实现让特定的APP拿到Power key 值

640?wx_fmt=png

power key 启动App

2. 如果只想让某个app的某个Activity 处理

640?wx_fmt=png

Power 键启动Activity 的方法

8. 如何修Activity启动是的窗口(app启动白屏,黑屏问题)

当用户从主菜单进入其他应用程序例如时钟、联系人、文件管理等时,可能会出现屏幕闪一下黑屏、白屏等问题,这种现象在当前手机主题(Theme)是浅色(例如白色)的情况下比较明显。

此所谓的闪"黑屏",其实是应用程序的启动窗口。

  1. 仅在要启动的Activity在新的Task或者新的Process时,才可能显示启动窗口

  2. 启动窗口先于Activity窗口显示,当Activity窗口的内容准备好之后,启动窗口就会被移除掉,show出真正的activity 窗口

  3. 启动窗口和普通的Activity window类似,只是没有画任何内容,默认是一个黑色背景的窗口

正是由于启动窗口默认是黑色背景的,所以在当前的手机主题为浅色调的时候,就比较容易因为颜色的深浅对比而产生一种视觉上的闪动感。

解决方法如下:

1.去掉启动窗口

在 ActivityStack.java中将SHOW_APP_STARTING_PREVIEW设置为false既可

2. 修改启动窗口样式

在 PhoneWindowManager中的addStartingWindow方法中添加自定义样式或者背景等

640?wx_fmt=png

修改启动窗口样式

9. WindowManagerPolicy 简介

PhoneWindowManager 实现 的接口类如下:

alps\frameworks\base\core\java\android\view\WindowManagerPolicy.java

640?wx_fmt=png

WindowManagerPolicy 接口实现

WindowManagerPolicy 是一个接口类,主要对外提供一些接口。

640?wx_fmt=png

WindowState 接口

640?wx_fmt=png

WindowMangerFuncs接口

640?wx_fmt=png

Screen On 接口

640?wx_fmt=png

Keyguard 接口

至此,Android 按键客制化已经可以初步实现需求,详细请看PhoneWindowManger 源码

640?wx_fmt=jpeg

至此,本篇已结束,如有不对的地方,欢迎您的建议与指正。同时期待您的关注,感谢您的阅读,谢谢!

640?wx_fmt=jpeg

如有侵权,请联系小编,小编对此深感抱歉,届时小编会删除文章,立即停止侵权行为,请您多多包涵。

640?wx_fmt=other

既然都看到这里,领两个红包在走吧!以下两个红包每天都可以领取

1.支付宝搜索 522398497,或扫码支付宝红包海报。

支付宝扫一扫,每天领取大红包

2.微信红包,微信扫一扫即可领取红包

微信扫一扫,每天领取微信红包

小礼物走一走,来简书关注我

<think>好的,我现在需要帮助用户解决在Android 13中,当框架不处理电源键时,如何在应用程序级别拦截和处理电源键事件的问题。首先,我需要理解用户的具体需求。用户提到当framework不处理电源键的情况下,可能是指系统层已经禁用了电源键的默认行为,或者用户希望绕过系统的处理,直接在应用层捕获电源键事件。 根据提供的引用内容,用户之前可能已经了解过在系统层如何修改电源键的功能,比如引用[1]和引用[2]中提到的修改PhoneWindowManager.java来屏蔽电源键的默认行为。现在用户想在应用层实现拦截,这说明用户可能不希望修改系统框架,而是希望通过应用级别的代码来处理电源键事件。 接下来,我需要回忆Android中的事件分发机制。通常,按键事件会从底层硬件开始,经过系统服务(如PhoneWindowManager),然后分发到当前活动的应用程序。如果系统层已经处理了电源键事件,应用层通常无法捕获到。但如果系统层不处理,应用层是否可以捕获呢?需要验证这一点。 在Android中,应用层可以通过重写Activity的onKeyDown或dispatchKeyEvent方法来捕获按键事件。但对于电源键这样的特殊按键,系统通常会在框架层就处理掉,不会传递给应用。例如,电源键按下后,系统可能会直接关闭屏幕或显示关机菜单,应用无法拦截。但根据用户的问题,假设框架层已经不再处理电源键事件,这时候应用是否可以捕获到? 另外,引用[3]提到Android架构中的应用框架层,即负责处理系统服务和应用交互的部分。如果系统服务没有消耗电源键事件,应用可能有机会处理。但需要确认在Android 13中,这种情况是否可行。 接下来需要考虑如何实现应用级别的拦截。可能的步骤包括: 1. 在Activity中重写dispatchKeyEvent或onKeyDown方法,检查按键是否是电源键(KeyEvent.KEYCODE_POWER)。 2. 处理电源键事件,并返回true表示已处理,阻止事件继续传递。 3. 注意权限问题,普通应用可能没有权限处理电源键事件,需要系统级权限,或者设备需要root。 但用户可能没有系统级权限,所以需要验证普通应用是否能够捕获电源键事件。根据Android的安全策略,普通应用通常无法拦截电源键,因为这会带来安全风险。不过,如果系统层已经禁用了默认处理(如引用[2]中的修改),那么应用可能可以接收到事件。 另一个可能性是使用前台服务或无障碍服务(AccessibilityService)来监听按键事件。无障碍服务具有更高的权限,可以监听全局按键事件,但需要用户授权。 因此,可能的解决方案有两种: - 修改系统框架,禁用电源键的默认处理(如引用[1][2]中的方法),然后在应用层捕获事件。 - 使用无障碍服务在应用层监听电源键事件,但需要用户手动启用该服务。 用户的问题是在系统框架不处理电源键的情况下,应用如何拦截,因此重点应放在应用层如何捕获事件,可能需要结合系统修改。例如,先按照引用[1]的方法屏蔽系统处理,然后在应用中重写按键处理方法。 此外,需要注意Android不同版本的差异。Android 13可能对权限和事件分发机制有所调整,需要查阅相关文档确认可行性。 总结可能的步骤: 1. 修改系统层,禁用电源键的默认处理(需要系统权限或自定义ROM)。 2. 在应用中重写dispatchKeyEvent,处理电源键事件。 3. 或者使用无障碍服务监听按键事件。 最后,还需要考虑实际测试,确认方法是否有效,并处理可能的权限问题。</think>在Android 13系统中,若需在应用层拦截电源键事件,需结合系统层修改和应用程序监听的双重操作。以下是具体实现步骤和技术要点: ### 一、系统层屏蔽电源键处理 需修改`PhoneWindowManager.java`以禁用默认行为(需系统签名或root权限): ```java // frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java private void interceptPowerKeyDown(KeyEvent event) { // 注释掉原有处理逻辑 /* if (gestureService != null) { gesturedServiceIntercepted = gestureService.interceptPowerKeyDown(...); } */ mPowerKeyHandled = true; //强制标记为已处理但不执行默认动作 } ``` 此修改会阻止系统响应短按/长按电源键的默认行为(如熄屏、关机弹窗)[^1][^2]。 ### 二、应用层事件拦截方案 #### 方案1:Activity级按键监听 在目标Activity中重写事件处理方法: ```java @Override public boolean dispatchKeyEvent(KeyEvent event) { if (event.getKeyCode() == KeyEvent.KEYCODE_POWER) { if (event.getAction() == KeyEvent.ACTION_DOWN) { // 处理短按事件 Log.d("PowerKey", "Power key intercepted"); return true; //阻止事件传递 } return true; //消耗所有电源键事件 } return super.dispatchKeyEvent(event); } ``` *局限*:仅对当前Activity有效,且需系统层已禁用电源键处理。 #### 方案2:无障碍服务全局监听 1. 创建继承`AccessibilityService`的自定义服务: ```java public class PowerInterceptorService extends AccessibilityService { @Override public void onServiceConnected() { // 注册按键监听 getServiceInfo().flags |= AccessibilityServiceInfo.FLAG_REQUEST_FILTER_KEY_EVENTS; } @Override public boolean onKeyEvent(KeyEvent event) { if (event.getKeyCode() == KeyEvent.KEYCODE_POWER) { performGlobalAction(GLOBAL_ACTION_BACK); //示例:映射为返回键 return true; } return super.onKeyEvent(event); } } ``` 2. 配置`accessibility-service.xml`: ```xml <accessibility-service xmlns:android="http://schemas.android.com/apk/res/android" android:description="@string/accessibility_desc" android:accessibilityEventTypes="typeAllMask" android:accessibilityFlags="flagRequestFilterKeyEvents" android:canRequestFilterKeyEvents="true" android:settingsActivity="com.example.SettingsActivity"/> ``` *要求*:需用户手动在设置中启用无障碍服务[^3]。 ### 三、兼容性注意事项 | Android版本 | 系统层修改可行性 | 无障碍服务限制 | |------------|----------------|----------------| | Android 9+ | 需系统签名 | 需用户手动授权 | | Android 13 | SELinux策略收紧| 新增后台限制 | ### 四、验证步骤 1. 使用`adb shell dumpsys accessibility`确认服务状态 2. 通过`logcat -s PowerKey`查看拦截日志 3. 物理按键测试(短按/长按不同场景) ### 五、典型问题排查 ```bash # 查看按键事件分发流程 adb shell getevent -l # 检测窗口焦点状态 adb shell dumpsys window windows | grep -E 'mCurrentFocus|mFocusedApp' ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员Android

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值