Android 11 截图流程梳理

本文详细梳理了Android 11中截图的流程,从PhoneWindowManager的按键分发开始,探讨了Power键按下事件的处理,进而进入ScreenshotRunnable,直到任务交由TakeScreenshotService处理。在TakeScreenshotService中,调用GlobalScreenshot的takeScreenshotFullscreen方法完成截图。整个过程详尽阐述,有助于理解Android系统的截图机制。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Android 原生截屏方式为,power键和音量下键的组合键,那么想要分析截屏流程就从按键的处理流程开始往下进行分析

1. PhoneWindowManager -- Android按键分发

public int interceptKeyBeforeQueueing(KeyEvent event, int policyFlags) {
        case KeyEvent.KEYCODE_VOLUME_DOWN:
        case KeyEvent.KEYCODE_VOLUME_UP:
        case KeyEvent.KEYCODE_VOLUME_MUTE: {
            if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
                //音量键按下
                if (down) {
                    // Any activity on the vol down button stops the ringer toggle shortcut
                    cancelPendingRingerToggleChordAction();
    
                    if (interactive && !mScreenshotChordVolumeDownKeyTriggered
                            && (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
                        //该值在后面的判断中会需要
                        mScreenshotChordVolumeDownKeyTriggered = true;
                        mScreenshotChordVolumeDownKeyTime = event.getDownTime();
                        mScreenshotChordVolumeDownKeyConsumed = false;
                        cancelPendingPowerKeyAction();
                        interceptScreenshotChord();
                        interceptAccessibilityShortcutChord();
                    }
                } else {
                    mScreenshotChordVolumeDownKeyTriggered = false;
                    cancelPendingScreenshotChordAction();
                    cancelPendingAccessibilityShortcutAction();
                }
            }
            break;
        }
    
        case KeyEvent.KEYCODE_POWER: {
            EventLogTags.writeInterceptPower(
                    KeyEvent.actionToString(event.getAction()),
                    mPowerKeyHandled ? 1 : 0, mPowerKeyPressCounter);
            // Any activity on the power button stops the accessibility shortcut
            cancelPendingAccessibilityShortcutAction();
            result &= ~ACTION_PASS_TO_USER;
            isWakeKey = false; // wake-up will be handled separately
            if (down) {
                //power键按下事件处理
                interceptPowerKeyDown(event, interactive);
            } else {
                interceptPowerKeyUp(event, interactive, canceled);
            }
            break;
        }
}

在PhoneWindowManager中处理power键的事件及音量下键的事件, 截图时Power键为按下状态,所以去看power键的按下事件处理

private void interceptPowerKeyDown(KeyEvent event, boolean interactive) {
        ......
        //锁屏电源键状态并且检测屏幕截图
        if (interactive && !mScreenshotChordPowerKeyTriggered
                    && (event.getFlags() & KeyEvent.FLAG_FALLBACK) == 0) {
                //power键按下的标志
                mScreenshotChordPowerKeyTriggered = true;
                //获取 Power 键的触发时间
                mScreenshotChordPowerKeyTime = event.getDownTime();
                //处理屏幕截图事件
                interceptScreenshotChord();
                interceptRingerToggleChord();
        }
        ......
    }
    
private void interceptScreenshotChord() {
        /*
        mScreenshotChordEnabled 其值为mContext.getResources().getBoolean(com.android.internal.R.bool.config_enableScreenshotChord);
        mScreenshotChordVolumeDownKeyTriggered 音量下键按下时值为true
        mScreenshotChordPowerKeyTriggered  电源键按下时值为true
        mA11yShortcutChordVolumeUpKeyTriggered 音量上键抬起时为false , 按下时为true
        */
        if (mScreenshotChordEnabled
                && mScreenshotChordVolumeDownKeyTriggered && mScreenshotChordPowerKeyTriggered
                && !mA11yShortcutChordVolumeUpKeyTriggered) {
            //获取当前时间
            final long now = SystemClock.uptimeMillis();
            //当前时间小于 音量下键按下时间 + 150ms
            //当前时间小于 power键按下时间 + 150ms 
            if (now <= mScreenshotChordVolumeDownKeyTime + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS
                    && now <= mScreenshotChordPowerKeyTime
                            + SCREENSHOT_CHORD_DEBOUNCE_DELAY_MILLIS) {
                mScreenshotChordVolumeDownKeyConsumed = true;
                cancelPendingPowerKeyAction();
                //使用全屏截图--type
                mScreenshotRunnable.setScreenshotType(TAKE_SCREENSHOT_FULLSCREEN);
                mScreenshotRunnable.setScreenshotSource(SCREENSHOT_KEY_CHORD);
                //执行mScreenshotRunnable
                mHandler.postDelayed(mScreenshotRunnable, getScreenshotChordLongPressDelay());
            }
        }
}

继续查看ScreenshotRunnable, 此时会一步步向下调用,最终到SystemUI

private class ScreenshotRunnable implements Runnable {
        private int&
<think>好的,我现在需要帮用户梳理Android的Input流程文档。用户可能是个开发者,遇到了输入事件处理的问题,或者想深入理解Android系统如何处理触摸和按键事件。我应该从整体流程开始,逐步分解各个部分,确保逻辑清晰。 首先,我得回忆一下Android系统的Input处理流程。大致分为硬件层、内核层、系统服务层和应用层。硬件检测到触摸或按键事件后,通过设备驱动传到Linux内核,生成原始事件。接着,系统服务如InputReader和InputDispatcher处理这些事件,InputReader读取事件并加工,InputDispatcher将事件分发给合适的窗口。 然后,要考虑窗口管理,比如WMS如何确定哪个窗口获得焦点,处理窗口层级和焦点切换。之后是应用层的处理,ViewRootImpl和View体系如何接收事件,经过onTouchEvent等方法传递,直到被消费。 可能还需要提到常见问题,比如事件拦截、多指触摸、ANR等,帮助用户实际开发中避免问题。用户可能需要实际案例或者流程图,但当前文本回复无法展示图表,所以要用文字描述清楚流程。 需要检查是否有遗漏的关键点,比如InputManagerService的作用,事件的分发顺序,以及Choreographer和VSync信号的关系,确保事件处理的同步。另外,不同Android版本是否有差异?可能用户使用的版本较新,但一般介绍通用流程,除非特别说明。 用户可能还想知道如何调试Input事件,比如使用getevent、sendevent命令,或者查看日志。这部分可以放在问题排查里。还要考虑是否需要涉及无障碍服务或输入法处理,但基础流程可能不需要,除非用户特别提到。 最后,确保结构清晰,分点讲解,从底层到上层,每部分的职责明确,让用户能一步步理解整个流程。避免使用过于专业的术语,必要时解释缩写,比如WMS、IMS等。检查是否有错误,比如InputDispatcher的分发逻辑是否正确,是否先找到目标窗口,再发送事件,应用处理超时导致ANR等。</think>以下是对 Android Input 流程梳理文档,帮助你理解从物理输入(触摸、按键等)到应用层事件处理的完整流程: --- ### **1. 输入事件流程概览** Android 的输入流程分为 **硬件层 → 内核层 → 系统服务层 → 应用层**,核心模块包括: - **InputReader**:读取原始输入事件。 - **InputDispatcher**:分发事件到目标窗口。 - **WindowManagerService (WMS)**:管理窗口层级与焦点。 - **ViewRootImpl & View 体系**:处理事件传递与消费。 --- ### **2. 详细流程分解** #### **2.1 硬件层 → 内核层** - **硬件输入**:用户触摸屏幕或按下物理按键,硬件生成中断信号。 - **内核驱动**:设备驱动(如 `evdev`)将原始事件写入 `/dev/input/eventX` 节点,事件格式为 `struct input_event`。 #### **2.2 系统服务层 (Native)** 1. **InputReader 线程**: - 通过 `EventHub` 监听 `/dev/input` 下的输入设备节点。 - 读取原始事件(`RawEvent`),并转换为 `KeyEvent`/`MotionEvent` 等结构化数据。 - 处理输入设备状态(如屏幕方向、多点触控校准)。 2. **InputDispatcher 线程**: - 接收来自 `InputReader` 的事件。 - 通过 `WindowManagerService` 确定目标窗口(基于焦点窗口、触摸区域、窗口层级等)。 - 将事件发送到目标应用进程的 `InputChannel`(基于 `SocketPair` 通信)。 #### **2.3 应用层 (Java)** 1. **InputChannel 与 ViewRootImpl**: - 每个窗口(Activity/Dialog)通过 `ViewRootImpl` 注册 `InputChannel`。 - `InputEventReceiver`(如 `ViewRootImpl.ViewPostImeInputReceiver`)接收事件。 2. **事件分发流程**: - **ViewRootImpl** → **DecorView** → **Activity** → **Window** → **View 层级**。 - 核心方法: - `View.dispatchTouchEvent()`:事件进入 View 树。 - `ViewGroup.onInterceptTouchEvent()`:拦截事件。 - `View.onTouchEvent()`:最终消费事件。 --- ### **3. 关键模块详解** #### **3.1 InputDispatcher 分发逻辑** - **焦点窗口**:由 `WMS` 维护,决定按键事件的目标(如 `EditText` 获取焦点时接收输入)。 - **触摸事件目标**:根据触摸坐标和窗口 `Surface` 的位置计算目标窗口。 - **ANR 机制**:若应用 5 秒内未处理完事件,触发 `ANR`(`InputDispatcher` 监测超时)。 #### **3.2 应用层事件传递** - **ACTION_DOWN 起点**:决定后续事件序列的接收者。 - **事件拦截**:`ViewGroup` 可通过 `onInterceptTouchEvent()` 拦截事件。 - **事件消费**:若某个 `View` 的 `onTouchEvent()` 返回 `true`,则消费该事件。 --- ### **4. 常见问题与调试** #### **4.1 常见问题** - **事件丢失/冲突**:多个 View 未正确处理 `onTouchEvent` 返回值。 - **焦点异常**:动态添加/移除 View 时未正确请求焦点。 - **ANR**:主线程阻塞导致事件未及时处理。 #### **4.2 调试工具** - **`getevent/sendevent`**:查看/模拟底层输入事件。 - **`adb shell dumpsys input`**:查看 InputDispatcher 状态、焦点窗口。 - **日志过滤**:搜索 `InputDispatcher` 或 `ViewRootImpl` 相关日志。 --- ### **5. 流程图(文字描述)** ``` 硬件输入 → 内核生成事件 → InputReader 解析 → InputDispatcher 分发 → WMS 计算目标窗口 → 通过 Socket 发送到应用 → ViewRootImpl 接收 → Activity 传递 → View 层级处理 ``` --- 希望这份文档能帮助你理解 Android 输入事件的核心流程。如果需要更深入的源码分析或具体场景案例,可以进一步补充问题!
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值