Android 12系统源码_输入事件(一)HOME按键事件响应流程

前言

本文我们现在主要分析一下android系统对HOME按键的响应过程,HOME按键事件是属于系统级别的按键事件监听,而在Android系统中,系统级别的按键处理逻辑都在PhoneWindowManager这个类中。

一、interceptKeyBeforeDispatching方法分发按键事件

1、PhoneWindowManager的dispatchUnhandledKey方法是最早收到系统级别的按键事件的。

frameworks/base/services/core/java/com/android/server/policy/PhoneWindowManager.java

public class PhoneWindowManager implements WindowManagerPolicy {@Overridepublic KeyEvent dispatchUnhandledKey(IBinder focusedToken, KeyEvent event, int policyFlags) {KeyEvent fallbackEvent = null;if ((event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {final KeyCharacterMap kcm = event.getKeyCharacterMap();final int keyCode = event.getKeyCode();final int metaState = event.getMetaState();final boolean initialDown = event.getAction() == KeyEvent.ACTION_DOWN&& event.getRepeatCount() == 0;// Check for fallback actions specified by the key character map.final FallbackAction fallbackAction;if (initialDown) {fallbackAction = kcm.getFallbackAction(keyCode, metaState);} else {fallbackAction = mFallbackActions.get(keyCode);}if (fallbackAction != null) {if (DEBUG_INPUT) {Slog.d(TAG, "Fallback: keyCode=" + fallbackAction.keyCode+ " metaState=" + Integer.toHexString(fallbackAction.metaState));}final int flags = event.getFlags() | KeyEvent.FLAG_FALLBACK;fallbackEvent = KeyEvent.obtain(event.getDownTime(), event.getEventTime(),event.getAction(), fallbackAction.keyCode,event.getRepeatCount(), fallbackAction.metaState,event.getDeviceId(), event.getScanCode(),flags, event.getSource(), event.getDisplayId(), null);//在这里进一步触发interceptFallback方法if (!interceptFallback(focusedToken, fallbackEvent, policyFlags)) {fallbackEvent.recycle();fallbackEvent = null;}if (initialDown) {mFallbackActions.put(keyCode, fallbackAction);} else if (event.getAction() == KeyEvent.ACTION_UP) {mFallbackActions.remove(keyCode);fallbackAction.recycle();}}}return fallbackEvent;}private boolean interceptFallback(IBinder focusedToken, KeyEvent fallbackEvent,int policyFlags) {int actions = interceptKeyBeforeQueueing(fallbackEvent, policyFlags);if ((actions & ACTION_PASS_TO_USER) != 0) {//进一步调用interceptKeyBeforeDispatchinglong delayMillis = interceptKeyBeforeDispatching(focusedToken, fallbackEvent, policyFlags);if (delayMillis == 0) {return true;}}return false;}
}

PhoneWindowManager的dispatchUnhandledKey方法会继续触发另一个关键方法interceptFallback,而interceptFallback方法又会进一步调用interceptKeyBeforeDispatching方法。

2、PhoneWindowManager的interceptKeyBeforeDispatching方法如下所示。

public class PhoneWindowManager implements WindowManagerPolicy {private final SparseArray<DisplayHomeButtonHandler> mDisplayHomeButtonHandlers = new SparseArray<>();@Overridepublic long interceptKeyBeforeDispatching(IBinder focusedToken, KeyEvent event,int policyFlags) {final boolean keyguardOn = keyguardOn();final int keyCode = event.getKeyCode();//按键编码final int repeatCount = event.getRepeatCount();final int metaState = event.getMetaState();final int flags = event.getFlags();final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;final boolean canceled = event.isCanceled();final int displayId = event.getDisplayId();//屏幕设备idfinal long key_consumed = -1;...代码省略...switch(keyCode) {case KeyEvent.KEYCODE_HOME://系统主要是在这里对HOME按键事件进行响应的//从缓存集合中获取HandlerDisplayHomeButtonHandler handler = mDisplayHomeButtonHandlers.get(displayId);if (handler == null) {//如果没有缓存,则创建Handler并添加到缓存集合中handler = new DisplayHomeButtonHandler(displayId);mDisplayHomeButtonHandlers.put(displayId, handler);}return handler.handleHomeButton(focusedToken, event);case KeyEvent.KEYCODE_MENU://菜单按键事件...代码省略...break;case KeyEvent.KEYCODE_APP_SWITCH://应用切换按键事件...代码省略...return key_consumed;...代码省略...}if (isValidGlobalKey(keyCode)&& mGlobalKeyManager.handleGlobalKey(mContext, keyCode, event)) {return key_consumed;}if ((metaState & KeyEvent.META_META_ON) != 0) {return key_consumed;}return 0;}

interceptKeyBeforeDispatching方法会通过switch的各个case分支,分别来响应各种按键事件,我们这里主要关注HOME按键事件的响应过程,系统首先从mDisplayHomeButtonHandlers集合中获取DisplayHomeButtonHandler类型的缓存对象,如果不存在缓存对象则创建DisplayHomeButtonHandler对象并添加到集合中,反正最终会调用DisplayHomeButtonHandler的handleHomeButton方法。

3、DisplayHomeButtonHandler是PhoneWindowManager的内部类,handleHomeButton方法如下所示。

public class PhoneWindowManager implements WindowManagerPolicy {private class DisplayHomeButtonHandler {private final int mDisplayId;private boolean mHomeDoubleTapPending;private boolean mHomePressed;private boolean mHomeConsumed;private final Runnable mHomeDoubleTapTimeoutRunnable = new Runnable() {@Overridepublic void run() {if (mHomeDoubleTapPending) {mHomeDoubleTapPending = false;handleShortPressOnHome(mDisplayId);}}};DisplayHomeButtonHandler(int displayId) {mDisplayId = displayId;}int handleHomeButton(IBinder focusedToken, KeyEvent event) {final boolean keyguardOn = keyguardOn();final int repeatCount = event.getRepeatCount();final boolean down = event.getAction() == KeyEvent.ACTION_DOWN;//是否是按下事件final boolean canceled = event.isCanceled();//一个按键事件大多都有两种Action,按下(ACTION_DOWN)和抬起(ACTION_UP)//!down意味着只有用户触发按键抬起的时候这里才会做响应回到首页if (!down) {if (mDisplayId == DEFAULT_DISPLAY) {cancelPreloadRecentApps();}mHomePressed = false;if (mHomeConsumed) {mHomeConsumed = false;return -1;}if (canceled) {Log.i(TAG, "Ignoring HOME; event canceled.");return -1;}if (mDoubleTapOnHomeBehavior != DOUBLE_TAP_HOME_NOTHING) {mHandler.removeCallbacks(mHomeDoubleTapTimeoutRunnable); // just in casemHomeDoubleTapPending = true;mHandler.postDelayed(mHomeDoubleTapTimeoutRunnable,ViewConfiguration.getDoubleTapTimeout());return -1;}//为了避免阻塞输入管道,这里通过Handler的post方法切换到了主线程mHandler.post(() -> handleShortPressOnHome(mDisplayId));return -1;}...代码省略...return -1;}}private void handleShortPressOnHome(int displayId) {// Turn on the connected TV and switch HDMI input if we're a HDMI playback device.final HdmiControl hdmiControl = getHdmiControl();if (hdmiControl != null) {hdmiControl.turnOnTv();}// If there's a dream running then use home to escape the dream// but don't actually go home.if (mDreamManagerInternal != null && mDreamManagerInternal.isDreaming()) {mDreamManagerInternal.stopDream(false /*immediate*/);return;}// Go home!launchHomeFromHotKey(displayId);}
}

handleHomeButton方法首先获取按键的Action类型是否为按下,并且进行了条件判断,只有当按键抬起的时候才会触发返回首页的相关操作,为了避免阻塞输入管道,这里通过Handler的post方法将当前线程切换到了主线程,并进一步调用handleShortPressOnHome方法,该方法又进一步调用launchHomeFromHotKey方法。

4、PhoneWindowManager的launchHomeFromHotKey方法如下所示。

public class PhoneWindowManager implements WindowManagerPolicy {void launchHomeFromHotKey(int displayId) {launchHomeFromHotKey(displayId, true /* awakenFromDreams */, true /*respectKeyguard*/);}/*** A home key -> launch home action was detected.  Take the appropriate action* given the situation with the keyguard.*/void launchHomeFromHotKey(int displayId, final boolean awakenFromDreams,final boolean respectKeyguard) {if (respectKeyguard) {if (isKeyguardShowingAndNotOccluded()) {return;}if (!isKeyguardOccluded() && mKeyguardDelegate.isInputRestricted()) {//当处于锁屏模式的时候,首先应该解锁然后才能打开首页mKeyguardDelegate.verifyUnlock(new OnKeyguardExitResult() {@Overridepublic void onKeyguardExitResult(boolean success) {if (success) {startDockOrHome(displayId, true /*fromHomeKey*/, awakenFromDreams);}}});return;}}//判断最近任务是否可见if (mRecentsVisible) {try {//如果最近任务视图可见,则会先停止应用切换功能ActivityManager.getService().stopAppSwitches();} catch (RemoteException e) {}if (awakenFromDreams) {awakenDreams();}//隐藏最近任务hideRecentApps(false, true);} else {//否则,打开首页startDockOrHome(displayId, true /*fromHomeKey*/, awakenFromDreams);}}}

launchHomeFromHotKey方法先是判断当前是否处于锁屏状态,如果处于锁屏状体则必须先解锁然后才能打开首页。然后会判断最近任务是否可见:

  • 如果可见则会调用AMS的stopAppSwitches方法停止应用切换功能,该方法的目的主要是为了暂停后台打开Activity的操作,避免打扰用户的操作.比如这时候我们在后台打开一个新的App,那么由于要回到home页面,所以需要先延时打开。在停止应用切换功能之后还会调用hideRecentApps隐藏最近任务。
  • 如果最近任务不可见,则会直接调用startDockOrHome方法打开首页。

二、startDockOrHome方法打开首页

1、PhoneWindowManager的startDockOrHome方法如下所示。

public class PhoneWindowManager implements WindowManagerPolicy {void startDockOrHome(int displayId, boolean fromHomeKey, boolean awakenFromDreams,String startReason) {try {//先停止应用切换功能ActivityManager.getService().stopAppSwitches();} catch (RemoteException e) {}//关闭系统当前存在的各种弹窗sendCloseSystemWindows(SYSTEM_DIALOG_REASON_HOME_KEY);if (awakenFromDreams) {awakenDreams();}if (!mHasFeatureAuto && !isUserSetupComplete()) {Slog.i(TAG, "Not going home because user setup is in progress.");return;}//创建桌面意图对象Intent dock = createHomeDockIntent();if (dock != null) {//如果桌面意图对象不为空则打开该意图对象try {if (fromHomeKey) {dock.putExtra(WindowManagerPolicy.EXTRA_FROM_HOME_KEY, fromHomeKey);}startActivityAsUser(dock, UserHandle.CURRENT);return;} catch (ActivityNotFoundException e) {}}if (DEBUG_WAKEUP) {Log.d(TAG, "startDockOrHome: startReason= " + startReason);}//调用ATMS的startHomeOnDisplay方法打开首页mActivityTaskManagerInternal.startHomeOnDisplay(mCurrentUserId, startReason,displayId, true /* allowInstrumenting */, fromHomeKey);}
}

startDockOrHome方法先是调用ActivityManager.getService().stopAppSwitches()暂停掉应用切换,然后调用sendCloseSystemWindows方法关闭系统当前存在的各种弹窗,然后调用createHomeDockIntent方法创建桌面意图对象,如果创建成功则直接打开该意图对象,否则会调用ATMS的startHomeOnDisplay方法打开首页。

2.1、先来看下ActivityManagerService的stopAppSwitches方法。

public class ActivityManagerService extends IActivityManager.Stubimplements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback, ActivityManagerGlobalLock {public ActivityTaskManagerService mActivityTaskManager;@Overridepublic void stopAppSwitches() {mActivityTaskManager.stopAppSwitches();}
}

ActivityManagerService的stopAppSwitches方法什么都没做,只是进一步调用ActivityTaskManagerService的stopAppSwitches方法。
2.2、ActivityTaskManagerService 的stopAppSwitches方法。

public class ActivityTaskManagerService extends IActivityTaskManager.Stub {//停止后台打开应用的操作@Overridepublic void stopAppSwitches() {mAmInternal.enforceCallingPermission(STOP_APP_SWITCHES, "stopAppSwitches");synchronized (mGlobalLock) {mAppSwitchesState = APP_SWITCH_DISALLOW;//纪录当前时间mLastStopAppSwitchesTime = SystemClock.uptimeMillis();mH.removeMessages(H.RESUME_FG_APP_SWITCH_MSG);mH.sendEmptyMessageDelayed(H.RESUME_FG_APP_SWITCH_MSG, RESUME_FG_APP_SWITCH_MS);}}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/50403.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

IC芯片 trustzone学习

搭建Airplay TA环境需要在IC的TrustZone中进行。TrustZone是一种安全技术&#xff0c;用于隔离安全和非安全环境&#xff0c;并保护敏感文件。在TrustZone中&#xff0c;我们需要编写一个叫做TA&#xff08;Trusted Application&#xff09;的应用程序来控制这些私密文档。 &am…

SpringBoot一些困惑及梳理

Spring中常用的classpath前缀到底指向哪里? classpath实际就是和java命令行运行时指定的classpath是同一个概念&#xff0c;在ideamaven中也就是指向target/classes目录。不要被网上哪些复制粘贴的文章所迷惑。classpath: 和 classpath*: 到底什么区别? classpath: 实际就是当…

Python土力学与基础工程计算.PDF-土的三项组成

5.3 Python求解 Python 求解代码如下&#xff1a; 1. # 定义已知参数 2. G_s 2.7 # 比重 3. w 0.2 # 含水量 4. e 0.6 # 孔隙比 5. gamma_w 9.81 # 水的重度 6. 7. # 根据公式计算饱和度 8. S_r G_s * w / e 9. print("饱和度为", S_r) 10. 11.…

js将搜索的关键字加颜色

js将搜索的关键字加颜色 使用正则匹配关键字并加入span标签&#xff0c;页面渲染时使用v-html渲染即可 // 文本框内容 let searchCont 测试;const reg new RegExp((${searchCont.value}), g); let data 图片保存测试A; data data.replace(reg, <span style"color:…

vscode+ros开发环境搭建

目录 介绍 前提 vscode安装 vscode插件安装 工作空间准备 打开vscode 创建catkin包 编写cpp代码 编译 运行 启动ros服务 监听话题 启动ros测试 介绍 ros开发是机器人开发中必不可少的工作&#xff0c;语言选择可以是c,也可以是python。工具的话&#xff0c;不能像wi…

基于YOLOv8模型的五类动物目标检测系统(PyTorch+Pyside6+YOLOv8模型)

摘要&#xff1a;基于YOLOv8模型的五类动物目标检测系统可用于日常生活中检测与定位动物目标&#xff08;狼、鹿、猪、兔和浣熊&#xff09;&#xff0c;利用深度学习算法可实现图片、视频、摄像头等方式的目标检测&#xff0c;另外本系统还支持图片、视频等格式的结果可视化与…

Github 博客搭建

Github 博客搭建 准备工作 准备一个 github 账号建立 github 仓库&#xff0c;仓库名为 username.github.io设置仓库为 publicclone 仓库&#xff0c;写入一个 index.html 文件&#xff0c;推送到仓库&#xff08;许多网上的教程会有 choose theme&#xff0c;但是新版本的 g…

「Java」《Java集合框架详解:掌握常用集合类,提升开发效率》

Java集合框架详解&#xff1a;掌握常用集合类&#xff0c;提升开发效率 摘要&#xff1a;一、 引言1.1 Java 集合框架的作用和重要性&#xff0c;在开发中的应用场景 二. 集合框架概述2.1 集合框架的基本目标和设计思想2.2 层次结构图示&#xff0c;各个集合接口之间的关系 三.…

AI 绘画Stable Diffusion 研究(十四)SD 图生图+剪映制作人物说话视频

大家好&#xff0c;我是风雨无阻。 前一篇&#xff0c;我们详细介绍了使用 SadTlaker制作数字人视频案例&#xff0c;感兴趣的朋友请前往查看:AI 绘画Stable Diffusion 研究&#xff08;十三&#xff09;SD数字人制作工具SadTlaker使用教程。 对于没有安装 SadTlaker 插件的朋友…

CH583/2构建工程教程

CH583/2构建工程教程 绪论资源移植步骤准备移植步骤一步骤二 工程配置修改工程名修改前修改后 工程配置修改资源文件 修改C/C general修改C/C构建修改汇编交叉编译修改C交叉编译修改GNU RISC-V Cross Linker 修改跟编译 移植注意事项 绪论 资源 CH583/2的SDK下载 移植步骤 …

Java课题笔记~ 整合第三方技术

1. 整合JUnit 问题导入 回忆一下Spring整合JUnit的步骤&#xff1f; 1.1 Spring整合JUnit&#xff08;复习&#xff09; 1.2 SpringBoot整合JUnit 【第一步】添加整合junit起步依赖(可以直接勾选) <dependency><groupId>org.springframework.boot</groupId…

flutter对数组中某个数据二次加工成单独的数组

如何将数据[2,1,2,2,2,1,2,2,3,2,2,2,2,3,2,2,2,2,2,3,2,4,2,2,1,2,3,2,4,2]加工成 [[2], 1, [2, 2, 2], 1, [2, 2], 3, [2, 2, 2, 2], 3, [2, 2, 2, 2, 2], 3, [2], 4, [2, 2], 1, [2], 3, [2], 4, [2]]。这是实际工作中遇到的问题&#xff0c;UI要求将某一类型数据&#xff…

Android View截屏长图拼接(RecyclerView)

Android页面View截图&#xff08;分享、保存相册&#xff09; Android View 截屏上下拼接 Android View截屏长图拼接&#xff08;RecyclerView&#xff09; Android View截屏长图拼接&#xff08;NestedScrollView&#xff09; RecyclerView截屏&#xff0c;其实就是遍历每…

前端实习day30

今天又是一个繁忙的一天&#xff0c;加功能&#xff0c;改样式&#xff0c;改得头皮发麻&#xff0c;预定的任务还是没能完成&#xff0c;改起来真得太头疼&#xff0c;代码太乱了&#xff01;&#xff01;昨天那个bug&#xff0c;今天问了一下同事&#xff0c;不到五分钟就解决…

无涯教程-PHP - intval() 函数

PHP 7引入了一个新函数 intdiv()&#xff0c;该函数对其操作数执行整数除法并将该除法返回为int。 <?php$valueintdiv(10,3);var_dump($value);print(" ");print($value); ?> 它产生以下浏览器输出- int(3) 3 PHP - intval() 函数 - 无涯教程网无涯教程网…

nodejs根据pdf模板填入中文数据并生成新的pdf文件

导入pdf-lib库和fontkit npm install pdf-lib fs npm install pdf-lib/fontkit 具体代码 const { PDFDocument, StandardFonts } require(pdf-lib); const fs require(fs); const fontkit require(pdf-lib/fontkit) let pdfDoc let font async function fillPdfForm(temp…

Ubuntu Touch OTA-2 推出,支持 Fairphone 3 和 F(x)tec Pro1 X

导读UBports 基金会近日宣布为基于 Ubuntu 20.04 LTS (Focal Fossa) 的 Ubuntu Touch 移动操作系统发布并全面提供 OTA-2 软件更新。 Ubuntu Touch OTA-2 在首次 OTA 更新整整四个月后发布&#xff0c;支持新设备&#xff0c;包括 Fairphone 3、F(x)tec Pro1 X 和 Vollaphone X…

Ubuntu部署PHP7.4

系统版本&#xff1a;Ubuntu22.04 PHP版本: 7.4 Mysql版本&#xff1a;8.0 Nginx版本: 最新 1. 更新系统 首先&#xff0c;确保系统包是最新的&#xff1a; sudo apt update && sudo apt upgrade -y2. 安装 Nginx Nginx 在默认的 Ubuntu 仓库中&#xff0c;因此安装…

libdrm全解析八 —— 源码全解析(5)

接前一篇文章&#xff1a;libdrm全解析七 —— 源码全解析&#xff08;4&#xff09; 本文参考以下博文&#xff1a; DRM 驱动程序开发&#xff08;VKMS&#xff09; 特此致谢&#xff01; 本文继续对include/drm/drm.h中实际功能宏定义进行讲解。 7. DRM_IOCTL_GET_STATS …

HarmonyOS开发第一步,熟知开发工具DevEco Studio

俗话说的好&#xff0c;工欲善其事&#xff0c;必先利其器&#xff0c;走进HarmonyOS第一步&#xff0c;开发工具必须先行&#xff0c;当然了&#xff0c;关于开发工具的使用&#xff0c;官网和其他的博客也有很多的讲解&#xff0c;但是并没有按照常用的功能进行概述&#xff…