android home键流程分析
上一篇文章中我们介绍了android系统的截屏事件,由于截屏事件是一种系统全局处理事件,所以事件的处理逻辑不是在App中执行,而是在PhoneWindowManager中执行。而本文我们现在主要讲解android系统中HOME按键的事件处理,和截屏事件类似,这里的HOME按键也是系统级别的按键事件监听,所以其处理事件的逻辑也应该和截屏事件处理流程类似,从上一篇文章的分析过冲中我们不难发现,系统级别的按键处理逻辑其实都是在PhoneWindowManager中,所以HOME按键的处理逻辑也是在PhoneWindowManager的dispatchUnhandledKey方法中执行,那么我们就从dispatchUnhandleKey方法开始分析HOME按键的处理流程。
好吧我们看一下PhoneWindowManager的dispatchUnhandleKey方法的实现:
@Overridepublic KeyEvent dispatchUnhandledKey(WindowState win, 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(), null);if (!interceptFallback(win, fallbackEvent, policyFlags)) {fallbackEvent.recycle();fallbackEvent = null;}if (initialDown) {mFallbackActions.put(keyCode, fallbackAction);} else if (event.getAction() == KeyEvent.ACTION_UP) {mFallbackActions.remove(keyCode);fallbackAction.recycle();}}}if (DEBUG_INPUT) {if (fallbackEvent == null) {Slog.d(TAG, "No fallback.");} else {Slog.d(TAG, "Performing fallback: " + fallbackEvent);}}return fallbackEvent;}
通过查看源码,我们重点看一下dispatchUnhandledKey方法中调用的interceptFallback方法,关于HOME按键的处理逻辑也是在这个方法体中的,所以继续看一下interceptFallback方法的实现:
private boolean interceptFallback(WindowState win, KeyEvent fallbackEvent, int policyFlags) {int actions = interceptKeyBeforeQueueing(fallbackEvent, policyFlags);if ((actions & ACTION_PASS_TO_USER) != 0) {long delayMillis = interceptKeyBeforeDispatching(win, fallbackEvent, policyFlags);if (delayMillis == 0) {return true;}}return false;}
通过分析源码我们知道关于HOME按键的处理逻辑主要是在interceptKeyBeforeDispatching方法的实现的,既然这样,我们看一下interceptKeyBeforeDispatching方法的实现:
@Overridepublic long interceptKeyBeforeDispatching(WindowState win, KeyEvent event, int policyFlags) {...// First we always handle the home key here, so applications// can never break it, although if keyguard is on, we do let// it handle it, because that gives us the correct 5 second// timeout.if (keyCode == KeyEvent.KEYCODE_HOME) {// If we have released the home key, and didn't do anything else// while it was pressed, then it is time to go home!if (!down) {cancelPreloadRecentApps();mHomePressed = false;if (mHomeConsumed) {mHomeConsumed = false;return -1;}if (canceled) {Log.i(TAG, "Ignoring HOME; event canceled.");return -1;}// If an incoming call is ringing, HOME is totally disabled.// (The user is already on the InCallUI at this point,// and his ONLY options are to answer or reject the call.)TelecomManager telecomManager = getTelecommService();if (telecomManager != null && telecomManager.isRinging()) {Log.i(TAG, "Ignoring HOME; there's a ringing incoming call.");return -1;}// Delay handling home if a double-tap is possible.if (mDoubleTapOnHomeBehavior != DOUBLE_TAP_HOME_NOTHING) {mHandler.removeCallbacks(mHomeDoubleTapTimeoutRunnable); // just in casemHomeDoubleTapPending = true;mHandler.postDelayed(mHomeDoubleTapTimeoutRunnable,ViewConfiguration.getDoubleTapTimeout());return -1;}handleShortPressOnHome();return -1;}// If a system window has focus, then it doesn't make sense// right now to interact with applications.WindowManager.LayoutParams attrs = win != null ? win.getAttrs() : null;if (attrs != null) {final int type = attrs.type;if (type == WindowManager.LayoutParams.TYPE_KEYGUARD_SCRIM|| type == WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG|| (attrs.privateFlags & PRIVATE_FLAG_KEYGUARD) != 0) {// the "app" is keyguard, so give it the keyreturn 0;}final int typeCount = WINDOW_TYPES_WHERE_HOME_DOESNT_WORK.length;for (int i=0; i<typeCount; i++) {if (type == WINDOW_TYPES_WHERE_HOME_DOESNT_WORK[i]) {// don't do anything, but also don't pass it to the appreturn -1;}}}// Remember that home is pressed and handle special actions.if (repeatCount == 0) {mHomePressed = true;if (mHomeDoubleTapPending) {mHomeDoubleTapPending = false;mHandler.removeCallbacks(mHomeDoubleTapTimeoutRunnable);handleDoubleTapOnHome();} else if (mLongPressOnHomeBehavior == LONG_PRESS_HOME_RECENT_SYSTEM_UI|| mDoubleTapOnHomeBehavior == DOUBLE_TAP_HOME_RECENT_SYSTEM_UI) {preloadRecentApps();}} else if ((event.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) {if (!keyguardOn) {handleLongPressOnHome(event.getDeviceId());}}return -1;}// Let the application handle the key.return 0;}
这里我们主要看一下对android系统HOME按键的处理逻辑,通过分析源码我们知道HOME按键进入launcher界面的主要逻辑是在handleShortPressOnHome();方法中执行的,所以我们继续看一下handleShortPressOnHome方法的实现。
private void handleShortPressOnHome() {// Turn on the connected TV and switch HDMI input if we're a HDMI playback device.getHdmiControl().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();}
可以看到在handleShortPressOnHome方法中调用了launchHomeFromHotKey方法,该方法的注释用于go home,所以继续看一下该方法的实现:
void launchHomeFromHotKey() {launchHomeFromHotKey(true /* awakenFromDreams */, true /*respectKeyguard*/);}
可以看到在launchHomeFromHotKey方法中我们又调用了launchHomeFromHotkey的重构方法,这样我们看一下这个重构方法的实现。
void launchHomeFromHotKey(final boolean awakenFromDreams, final boolean respectKeyguard) {if (respectKeyguard) {if (isKeyguardShowingAndNotOccluded()) {// don't launch home if keyguard showingreturn;}if (!mHideLockScreen && mKeyguardDelegate.isInputRestricted()) {// when in keyguard restricted mode, must first verify unlock// before launching homemKeyguardDelegate.verifyUnlock(new OnKeyguardExitResult() {@Overridepublic void onKeyguardExitResult(boolean success) {if (success) {try {ActivityManagerNative.getDefault().stopAppSwitches();} catch (RemoteException e) {}sendCloseSystemWindows(SYSTEM_DIALOG_REASON_HOME_KEY);startDockOrHome(true /*fromHomeKey*/, awakenFromDreams);}}});return;}}// no keyguard stuff to worry about, just launch home!try {ActivityManagerNative.getDefault().stopAppSwitches();} catch (RemoteException e) {}if (mRecentsVisible) {// Hide Recents and notify it to launch Homeif (awakenFromDreams) {awakenDreams();}sendCloseSystemWindows(SYSTEM_DIALOG_REASON_HOME_KEY);hideRecentApps(false, true);} else {// Otherwise, just launch HomesendCloseSystemWindows(SYSTEM_DIALOG_REASON_HOME_KEY);startDockOrHome(true /*fromHomeKey*/, awakenFromDreams);}}
可以发现在方法中我们首先调用了ActivityManagerNative.getDefault().stopAppSwitches();该方法主要用于暂停后台的打开Activity的操作,避免打扰用户的操作。比如这时候我们在后台打开一个新的App,那么这时候由于要回到home页面,所以需要先延时打开。方法执行这个方法之后然后执行了sendCloseSystemWindows方法,该方法主要实现了对当前系统App页面的关闭操作,下面我们先看一下ActivityManagerNative.getDefault().stopAppSwitches();方法的实现,这里的ActivityManagerNative.getDefault我们在前面已经多次说过了其是一个Binder对象,是应用进程Binder客户端用于与ActivityManagerService之间通讯,所以这里最终调用的是ActivityManagerService的stopAppsSwitches方法,这样我们就继续看一下ActivityManagerService的stopAppsSwitches方法的实现。
@Overridepublic void stopAppSwitches() {if (checkCallingPermission(android.Manifest.permission.STOP_APP_SWITCHES)!= PackageManager.PERMISSION_GRANTED) {throw new SecurityException("Requires permission "+ android.Manifest.permission.STOP_APP_SWITCHES);}synchronized(this) {mAppSwitchesAllowedTime = SystemClock.uptimeMillis()+ APP_SWITCH_DELAY_TIME;mDidAppSwitch = false;mHandler.removeMessages(DO_PENDING_ACTIVITY_LAUNCHES_MSG);Message msg = mHandler.obtainMessage(DO_PENDING_ACTIVITY_LAUNCHES_MSG);mHandler.sendMessageDelayed(msg, APP_SWITCH_DELAY_TIME);}}
可以发现这里主要是发送了一个异步消息,并且msg.what为DO_PENDING_ACTIVITY_LAUNCHES_MSG,即跳转Activity,然后我们继续我们看一下mHandler的handleMessage方法当msg.what为DO_PENDING_ACTIVITY_LAUNCHES_MSG时的操作。而且我们可以发现这里的异步消息是一个延时的异步消息,延时的时间为APP_SWITCH_DELAY_TIME,我们可以看一下该变量的定义:
// Amount of time after a call to stopAppSwitches() during which we will// prevent further untrusted switches from happening.static final long APP_SWITCH_DELAY_TIME = 5*1000;
然后我们可以看一下mHander的handleMessage方法的具体实现:
case DO_PENDING_ACTIVITY_LAUNCHES_MSG: {synchronized (ActivityManagerService.this) {mStackSupervisor.doPendingActivityLaunchesLocked(true);}} break;
可以发现这里直接调用了mStackSupervisor.doPendingActivityLaunchesLocked方法,好吧,继续看一下doPendingActivityLaunchesLocked方法的实现。
final void doPendingActivityLaunchesLocked(boolean doResume) {while (!mPendingActivityLaunches.isEmpty()) {PendingActivityLaunch pal = mPendingActivityLaunches.remove(0);startActivityUncheckedLocked(pal.r, pal.sourceRecord, null, null, pal.startFlags,doResume && mPendingActivityLaunches.isEmpty(), null, null);}}
可以发现这里就是调用了startActivity的操作了,这里就是开始启动Activity了,所以当我们按下HOME按键的时候,后台的startActivity都会延时5秒钟执行…
然后回到我们的launchHomeFromHotKey方法,看一下launchHomeFromHotKey方法的实现。
void sendCloseSystemWindows(String reason) {PhoneWindow.sendCloseSystemWindows(mContext, reason);}
可以发现这里调用了PhoneWindow的静态方法sendCloseSystemWindow,继续看一下该方法的实现逻辑。
public static void sendCloseSystemWindows(Context context, String reason) {if (ActivityManagerNative.isSystemReady()) {try {ActivityManagerNative.getDefault().closeSystemDialogs(reason);} catch (RemoteException e) {}}}
看到这里,很明显了又是调用了Binder的进程间通讯,最终ActivityManagerService的closeSystemDialogs方法会被执行,所以我们继续看一下ActivityManagerService的closeSystemDialogs方法的实现。
@Overridepublic void closeSystemDialogs(String reason) {enforceNotIsolatedCaller("closeSystemDialogs");final int pid = Binder.getCallingPid();final int uid = Binder.getCallingUid();final long origId = Binder.clearCallingIdentity();try {synchronized (this) {// Only allow this from foreground processes, so that background// applications can't abuse it to prevent system UI from being shown.if (uid >= Process.FIRST_APPLICATION_UID) {ProcessRecord proc;synchronized (mPidsSelfLocked) {proc = mPidsSelfLocked.get(pid);}if (proc.curRawAdj > ProcessList.PERCEPTIBLE_APP_ADJ) {Slog.w(TAG, "Ignoring closeSystemDialogs " + reason+ " from background process " + proc);return;}}closeSystemDialogsLocked(reason);}} finally {Binder.restoreCallingIdentity(origId);}}
可以发现其实在方法体中将关闭窗口的逻辑下发到了closeSystemDialogsLocked中,所以我们继续看一下closeSystemDialogsLocked方法的实现。
void closeSystemDialogsLocked(String reason) {Intent intent = new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY| Intent.FLAG_RECEIVER_FOREGROUND);if (reason != null) {intent.putExtra("reason", reason);}mWindowManager.closeSystemDialogs(reason);mStackSupervisor.closeSystemDialogsLocked();broadcastIntentLocked(null, null, intent, null, null, 0, null, null, null,AppOpsManager.OP_NONE, null, false, false,-1, Process.SYSTEM_UID, UserHandle.USER_ALL);}
可以发现在方法体中首先调用了mWindowManager.closeSystemDialogs方法,该方法就是关闭当前页面中存在的系统窗口,比如输入法,壁纸等:
@Overridepublic void closeSystemDialogs(String reason) {synchronized(mWindowMap) {final int numDisplays = mDisplayContents.size();for (int displayNdx = 0; displayNdx < numDisplays; ++displayNdx) {final WindowList windows = mDisplayContents.valueAt(displayNdx).getWindowList();final int numWindows = windows.size();for (int winNdx = 0; winNdx < numWindows; ++winNdx) {final WindowState w = windows.get(winNdx);if (w.mHasSurface) {try {w.mClient.closeSystemDialogs(reason);} catch (RemoteException e) {}}}}}}
讲过这样一层操作之后,我们就关闭了当前中存在的系统窗口。然后还是回到我们的launchHomeFromHotKey方法,我们发现在方法体的最后我们调用了startDockOrHome方法,这个方法就是实际的跳转HOME页面的方法了,我们可以具体看一下该方法的实现。
void startDockOrHome(boolean fromHomeKey, boolean awakenFromDreams) {if (awakenFromDreams) {awakenDreams();}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) {}}Intent intent;if (fromHomeKey) {intent = new Intent(mHomeIntent);intent.putExtra(WindowManagerPolicy.EXTRA_FROM_HOME_KEY, fromHomeKey);} else {intent = mHomeIntent;}startActivityAsUser(intent, UserHandle.CURRENT);}
可以发现我们在方法体中调用了createHomeDockIntent,这个方法的作用就是创建到达HOME页面的Intent对象,然后我们调用了startActivityAsUser方法,这样经过一系列的调用之后就调起了home页面的Activity,所以这时候系统就返回到了HOME页面。
总结:
home键的处理事件是在PhoneWindowManager类中处理,监听到操作后首先关闭系统window窗体,然后调用系统launcher桌面程序,这样就会到了手机桌面了。
android home键流程分析相关推荐
- Android SDCard UnMounted 流程分析(三)
前篇地址 Android SDCard UnMounted 流程分析(一) Android SDCard UnMounted 流程分析(二) 前一篇讲到SDCard unmout onEvent 发送 ...
- android camera2 API流程分析
Android camera2 API流程分析 Android5.0之后,新推出来了一个类,android.hardware.camera2,与原来的camera的类实现照相和拍视频的流程有所不同,原 ...
- Android UI绘制流程分析(三)measure
源码版本Android 6.0 请参阅:http://androidxref.com/6.0.1_r10 本文目的是分析从Activity启动到走完绘制流程并显示在界面上的过程,在源码展示阶段为了使跟 ...
- Android -- Wifi启动流程分析
Android -- Wifi启动流程分析 Android网络各个模式中,Wifi应该是目前最常用的一种网络方式了:下面就简单介绍下Android中Wifi的启动流程. 当我在Setting菜单里点击 ...
- 【SemiDrive源码分析】【X9芯片启动流程】27 - AP1 Android Preloader启动流程分析(加载atf、tos、bootloader镜像后进入BL31环境)
[SemiDrive源码分析][X9芯片启动流程]27 - AP1 Android Preloader启动流程分析(加载atf.tos.bootloader镜像后进入BL31环境) 一.Android ...
- android volume挂载流程,Android SDCard UnMounted 流程分析(一)
Android SDCard框架 Android SDCard框架,我们修改一般涉及到四大模块 Linux Kernel 用于检测热拔插,作为框架开发者来说,这者不用涉及 Vold 作为Kernel ...
- android app启动流程分析,Android应用开发之Android 7.0 Launcher3的启动和加载流程分析...
本文将带你了解Android应用开发Android 7.0 Launcher3的启动和加载流程分析,希望本文对大家学Android有所帮助. Android 7.0 Launcher3的启动和加载流程 ...
- Android流媒体处理流程分析
文章目录 1. WiFiDisplay简介 2.RTSP协议流程分析 3. 流媒体协议简介 4. RTP.RTCP协议简介 4.1 RTP协议 4.1 RTP载荷H264码流 4.2 RTP载荷PS码 ...
- Android 5.1 长按power键流程分析
安全模式简述 android平台,在长按power / menu键时会快速进入一个模式选择,部分定制的平台是直接进入安装模式,也可以定制成公司需要的一些特定功能模式,比如报警 ... power 也属 ...
- Android Binder Driver流程分析
Binder是Android中使用最为广泛的IPC框架,从实现的角度可以分为内核层和用户空间层,本文主要分析下Binder Driver在内核层的实现. Binder Driver初始化 1 2 3 ...
最新文章
- c语言基础习题下载,C语言基础题目
- webView加载不出网页的一种可能情况
- 世界上最大的黑客 Party,有关“DEFCON”的十个冷知识
- ROS学习(九):ROS URDF-link
- 鸿蒙正式推送时间,鸿蒙系统现已正式推送,更新名单也随之出炉,包括荣耀手机!...
- delphi批量存入多媒体字段 遇到内存溢出的坑
- jmetter持续时间_Jmeter常用线程组设置及场景运行时间计算
- Ubuntu使用技巧(三), 硬盘安装Linux(UEFI)
- mysql 创建账号权限_mysql创建用户,并指定用户的权限(grant命令)
- 【JZOJ4819】【NOIP2016提高A组模拟10.15】算循环
- 30 MM配置-采购-采购申请-采购申请审批策略-编辑类
- 买房后每月还贷是什么感觉?
- Win7/Win8/IIS7/IIS8配置ASP/ACCESS
- cudnn下载注意事项
- 女生学java软件开发怎么样?就业前景如何?
- 成为优秀交互设计师的六个必备技能
- RichTextBox 增加行间距
- 深入浅出JDBC核心技术
- STemWin的移植
- 解决IDEA里提示“Spring Configuration Check“ “Unmapped Spring configuration files found.“的问题
热门文章
- 0 、 ‘0‘ 、 “0“ 、 ’\0’ 区别
- 阿里聚石塔限制IP 过部署服务器详细流程
- WPS如何打开pdf目录
- 自动化邮件报告平台-邮件发送highchart图表
- 用python贴吧自动回帖_python基于selenium实现贴吧自动发帖
- sql 遇到多个重复列名报错:Ambiguous column reference ***
- PCI驱动的注册和初始化
- 未转变者服务器tp指令大全,Unturned未转变者3.21版本高级指令大全
- <<算法竞赛进阶指南>>:陪审团
- 计算机理解人的情感的前提是,情感识别