上一篇文章中我们介绍了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键流程分析相关推荐

  1. Android SDCard UnMounted 流程分析(三)

    前篇地址 Android SDCard UnMounted 流程分析(一) Android SDCard UnMounted 流程分析(二) 前一篇讲到SDCard unmout onEvent 发送 ...

  2. android camera2 API流程分析

    Android camera2 API流程分析 Android5.0之后,新推出来了一个类,android.hardware.camera2,与原来的camera的类实现照相和拍视频的流程有所不同,原 ...

  3. Android UI绘制流程分析(三)measure

    源码版本Android 6.0 请参阅:http://androidxref.com/6.0.1_r10 本文目的是分析从Activity启动到走完绘制流程并显示在界面上的过程,在源码展示阶段为了使跟 ...

  4. Android -- Wifi启动流程分析

    Android -- Wifi启动流程分析 Android网络各个模式中,Wifi应该是目前最常用的一种网络方式了:下面就简单介绍下Android中Wifi的启动流程. 当我在Setting菜单里点击 ...

  5. 【SemiDrive源码分析】【X9芯片启动流程】27 - AP1 Android Preloader启动流程分析(加载atf、tos、bootloader镜像后进入BL31环境)

    [SemiDrive源码分析][X9芯片启动流程]27 - AP1 Android Preloader启动流程分析(加载atf.tos.bootloader镜像后进入BL31环境) 一.Android ...

  6. android volume挂载流程,Android SDCard UnMounted 流程分析(一)

    Android SDCard框架 Android SDCard框架,我们修改一般涉及到四大模块 Linux Kernel 用于检测热拔插,作为框架开发者来说,这者不用涉及 Vold 作为Kernel ...

  7. android app启动流程分析,Android应用开发之Android 7.0 Launcher3的启动和加载流程分析...

    本文将带你了解Android应用开发Android 7.0 Launcher3的启动和加载流程分析,希望本文对大家学Android有所帮助. Android 7.0 Launcher3的启动和加载流程 ...

  8. Android流媒体处理流程分析

    文章目录 1. WiFiDisplay简介 2.RTSP协议流程分析 3. 流媒体协议简介 4. RTP.RTCP协议简介 4.1 RTP协议 4.1 RTP载荷H264码流 4.2 RTP载荷PS码 ...

  9. Android 5.1 长按power键流程分析

    安全模式简述 android平台,在长按power / menu键时会快速进入一个模式选择,部分定制的平台是直接进入安装模式,也可以定制成公司需要的一些特定功能模式,比如报警 ... power 也属 ...

  10. Android Binder Driver流程分析

    Binder是Android中使用最为广泛的IPC框架,从实现的角度可以分为内核层和用户空间层,本文主要分析下Binder Driver在内核层的实现. Binder Driver初始化 1 2 3 ...

最新文章

  1. c语言基础习题下载,C语言基础题目
  2. webView加载不出网页的一种可能情况
  3. 世界上最大的黑客 Party,有关“DEFCON”的十个冷知识
  4. ROS学习(九):ROS URDF-link
  5. 鸿蒙正式推送时间,鸿蒙系统现已正式推送,更新名单也随之出炉,包括荣耀手机!...
  6. delphi批量存入多媒体字段 遇到内存溢出的坑
  7. jmetter持续时间_Jmeter常用线程组设置及场景运行时间计算
  8. Ubuntu使用技巧(三), 硬盘安装Linux(UEFI)
  9. mysql 创建账号权限_mysql创建用户,并指定用户的权限(grant命令)
  10. 【JZOJ4819】【NOIP2016提高A组模拟10.15】算循环
  11. 30 MM配置-采购-采购申请-采购申请审批策略-编辑类
  12. 买房后每月还贷是什么感觉?
  13. Win7/Win8/IIS7/IIS8配置ASP/ACCESS
  14. cudnn下载注意事项
  15. 女生学java软件开发怎么样?就业前景如何?
  16. 成为优秀交互设计师的六个必备技能
  17. RichTextBox 增加行间距
  18. 深入浅出JDBC核心技术
  19. STemWin的移植
  20. 解决IDEA里提示“Spring Configuration Check“ “Unmapped Spring configuration files found.“的问题

热门文章

  1. 0 、 ‘0‘ 、 “0“ 、 ’\0’ 区别
  2. 阿里聚石塔限制IP 过部署服务器详细流程
  3. WPS如何打开pdf目录
  4. 自动化邮件报告平台-邮件发送highchart图表
  5. 用python贴吧自动回帖_python基于selenium实现贴吧自动发帖
  6. sql 遇到多个重复列名报错:Ambiguous column reference ***
  7. PCI驱动的注册和初始化
  8. 未转变者服务器tp指令大全,Unturned未转变者3.21版本高级指令大全
  9. <<算法竞赛进阶指南>>:陪审团
  10. 计算机理解人的情感的前提是,情感识别