深入分析 Android 系统返回手势的实现原理(2)

简介: 深入分析 Android 系统返回手势的实现原理(2)

4. 创建返回手势视图

InputMonitor 创建完毕之后,EdgeBackGestureHandler 将立即创建手势视图即 NavigationBarEdgePanel 实例。并通过 setEdgeBackPlugin() 将其缓存,同时准备好承载该视图的 Window 参数一并传递过去。

// EdgeBackGestureHandler.java
    private void setEdgeBackPlugin(NavigationEdgeBackPlugin edgeBackPlugin) {
        if (mEdgeBackPlugin != null) {
            mEdgeBackPlugin.onDestroy();
        }
        // 缓存 NavigationEdgeBackPlugin 实现
        mEdgeBackPlugin = edgeBackPlugin;
        // 向 NavigationEdgeBackPlugin 注册 Back 手势的触发回调
        mEdgeBackPlugin.setBackCallback(mBackCallback);
        // 准备好手势视图的 Window 参数
        mEdgeBackPlugin.setLayoutParams(createLayoutParams());
        updateDisplaySize();
    }
    // 配置返回手势 Window 的参数
    // 包括 flag、type、title 等属性
    private WindowManager.LayoutParams createLayoutParams() {
        Resources resources = mContext.getResources();
        WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(
                resources.getDimensionPixelSize(R.dimen.navigation_edge_panel_width),
                resources.getDimensionPixelSize(R.dimen.navigation_edge_panel_height),
                WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
                WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                        | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
                        | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
                PixelFormat.TRANSLUCENT);
        ...
        layoutParams.setTitle(TAG + mContext.getDisplayId());
        layoutParams.setFitInsetsTypes(0 /* types */);
        layoutParams.setTrustedOverlay();
        return layoutParams;
    }

NavigationBarEdgePanel 构造函数将准备视图相关的描画、动画等相关初始化工作。


比如:


持有 WindowManager 为后续添加试图到 Window 上做准备

持有发出振动用的 mVibratorHelper,以进行后续的 click 振动

配置描画用的 Paint 属性

初始化返回箭头的颜色、淡入、角度动画

设置读取手势阈值 mSwipeThreshold

// NavigationBarEdgePanel.java
    public NavigationBarEdgePanel(Context context) {
        super(context);
        mWindowManager = context.getSystemService(WindowManager.class);
        mVibratorHelper = Dependency.get(VibratorHelper.class);
        ...
        mPaint.setStrokeWidth(mArrowThickness);
        mPaint.setStrokeCap(Paint.Cap.ROUND);
        ...
        mArrowColorAnimator = ValueAnimator.ofFloat(0.0f, 1.0f);
        mArrowColorAnimator.setDuration(COLOR_ANIMATION_DURATION_MS);
        mArrowColorAnimator.addUpdateListener(animation -> {
            int newColor = ColorUtils.blendARGB(
                    mArrowStartColor, mArrowColor, animation.getAnimatedFraction());
            setCurrentArrowColor(newColor);
        });
        mArrowDisappearAnimation = ValueAnimator.ofFloat(0.0f, 1.0f);
        mArrowDisappearAnimation.setDuration(DISAPPEAR_ARROW_ANIMATION_DURATION_MS);
        mArrowDisappearAnimation.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
        mArrowDisappearAnimation.addUpdateListener(animation -> {
            mDisappearAmount = (float) animation.getAnimatedValue();
            invalidate();
        });
        mAngleAnimation =
                new SpringAnimation(this, CURRENT_ANGLE);
        mAngleAppearForce = new SpringForce()
                .setStiffness(500)
                .setDampingRatio(0.5f);
        ...
        mSwipeThreshold = context.getResources()
                .getDimension(R.dimen.navigation_edge_action_drag_threshold);
        setVisibility(GONE);
        ...
    }

其后 NavigationBarEdgePanel 复写的 setLayoutParams() 会被 EdgeBackGestureHandler 调用。拿到 Handler 为其准备的 Window 参数后将本视图添加到一个专用 Window。


注意:此时 View 还是不可见的,后续事件产生的时候会进行展示和刷新。

// NavigationBarEdgePanel.java
    public void setLayoutParams(WindowManager.LayoutParams layoutParams) {
        mLayoutParams = layoutParams;
        mWindowManager.addView(this, mLayoutParams);
    }

5. 预处理 Touch 事件

当 InputDispatcher 收到 InputReader 传递过来的事件,在分发前会从 mGestureMonitorsByDisplay Map 中收集对应 Display 的 Monitor 实例,并将其中的 Server 端 InputChannel 一并放入到 Input Target 中。

// InputDispatcher.cpp
InputEventInjectionResult InputDispatcher::findTouchedWindowTargetsLocked( ... ) {
    ...
    if (newGesture || (isSplit && maskedAction == AMOTION_EVENT_ACTION_POINTER_DOWN)) {
        ...
        // 取出 InputMonitor
        std::vector<TouchedMonitor> newGestureMonitors = isDown
                ? findTouchedGestureMonitorsLocked(displayId, tempTouchState.portalWindows)
                : std::vector<TouchedMonitor>{};
        ...
        newGestureMonitors = selectResponsiveMonitorsLocked(newGestureMonitors);
        ...
        if (newTouchedWindowHandle != nullptr) {
            ...
            tempTouchState.addOrUpdateWindow(newTouchedWindowHandle, targetFlags, pointerIds);
        }
        // 添加 Monitors 到 TouchedState
        tempTouchState.addGestureMonitors(newGestureMonitors);
    } 
    ...
    // 将 TouchedState 中 Touched Window 添加到 InputTargets 中
    for (const TouchedWindow& touchedWindow : tempTouchState.windows) {
        addWindowTargetLocked(touchedWindow.windowHandle, touchedWindow.targetFlags,
                              touchedWindow.pointerIds, inputTargets);
    }
    // 将 TouchedState 中 Monitors 添加到 InputTargets 中
    for (const TouchedMonitor& touchedMonitor : tempTouchState.gestureMonitors) {
        addMonitoringTargetLocked(touchedMonitor.monitor, touchedMonitor.xOffset,
                                  touchedMonitor.yOffset, inputTargets);
    }
    ...
    return injectionResult;
}
// 从 mGestureMonitorsByDisplay map 中
// 按照 Display Id 取出 Vector 返回出去
std::vector<TouchedMonitor> InputDispatcher::findTouchedGestureMonitorsLocked( ... ) const {
    std::vector<TouchedMonitor> touchedMonitors;
    std::vector<Monitor> monitors = getValueByKey(mGestureMonitorsByDisplay, displayId);
    addGestureMonitors(monitors, touchedMonitors);
    for (const sp<InputWindowHandle>& portalWindow : portalWindows) {
        const InputWindowInfo* windowInfo = portalWindow->getInfo();
        ...
    }
    return touchedMonitors;
}
// 提取 Monitor 中的 Server InputChannerl
// 放入到 InputTarget Vector
void InputDispatcher::addMonitoringTargetLocked( ... ) {
    InputTarget target;
    target.inputChannel = monitor.inputChannel;
    target.flags = InputTarget::FLAG_DISPATCH_AS_IS;
    ui::Transform t;
    t.set(xOffset, yOffset);
    target.setDefaultPointerTransform(t);
    inputTargets.push_back(target);
}

之后 dispatchEventLocked 将遍历 InputTarget Vector 实例,逐一使用其 InputChannel 实例通过 Socket 向 App 进程和 SystemUI 进程发送事件。

// InputDispatcher.cpp
void InputDispatcher::dispatchEventLocked( ... ) {
    ...
    for (const InputTarget& inputTarget : inputTargets) {
        sp<Connection> connection =
                getConnectionLocked(inputTarget.inputChannel->getConnectionToken());
        if (connection != nullptr) {
            prepareDispatchCycleLocked(currentTime, connection, eventEntry, inputTarget);
        }
    }
}

监听 Socket FD 写入的消费端 Looper 将触发 LooperCallback,进而从 Client 端 Socket 读取事件,最后通过 InputEventReceiver 回调。

// android_view_InputEventReceiver.cpp
int NativeInputEventReceiver::handleEvent(int receiveFd, int events, void* data) {
    ...
    // 通过 Client Socket 读取事件
    if (events & ALOOPER_EVENT_INPUT) {
        JNIEnv* env = AndroidRuntime::getJNIEnv();
        status_t status = consumeEvents(env, false /*consumeBatches*/, -1, nullptr);
        mMessageQueue->raiseAndClearException(env, "handleReceiveCallback");
        return status == OK || status == NO_MEMORY ? KEEP_CALLBACK : REMOVE_CALLBACK;
    }
    ...
    return KEEP_CALLBACK;
}
status_t NativeInputEventReceiver::consumeEvents(JNIEnv* env,
        bool consumeBatches, nsecs_t frameTime, bool* outConsumedBatch) {
    ...
    for (;;) {
        // 通过 Client InputChannel 发出读取事件请求
        status_t status = mInputConsumer.consume(&mInputEventFactory,
                consumeBatches, frameTime, &seq, &inputEvent);
        ...
        if (!skipCallbacks) {
            ...
            switch (inputEvent->getType()) {
            ...
            case AINPUT_EVENT_TYPE_MOTION: {
                MotionEvent* motionEvent = static_cast<MotionEvent*>(inputEvent);
                if ((motionEvent->getAction() & AMOTION_EVENT_ACTION_MOVE) && outConsumedBatch) {
                    *outConsumedBatch = true;
                }
                inputEventObj = android_view_MotionEvent_obtainAsCopy(env, motionEvent);
                break;
            }
            ...
            // 调用 InputEventReceiver Java 端
            // dispatchInputEvent()
            if (inputEventObj) {
                env->CallVoidMethod(receiverObj.get(),
                        gInputEventReceiverClassInfo.dispatchInputEvent, seq, inputEventObj);
                ...
            }...
        }
    }
}

InputEventReceiver 的 dispatchInputEvent() 会回调 onInputEvent()。

// InputEventReceiver.java
    private void dispatchInputEvent(int seq, InputEvent event) {
        mSeqMap.put(event.getSequenceNumber(), seq);
        onInputEvent(event);
    }

onInputEvent() 作为 SystemUI 监视到系统 Input 事件回调的入口,将展开整个返回手势的判断、视图和动画的刷新以及返回事件的触发。

首先将检查一下是否是 Touch 的 MotionEvent 类型,之后交给onMotionEvent() 预处理。

// EdgeBackGestureHandler.java
    private void onInputEvent(InputEvent ev) {
        if (!(ev instanceof MotionEvent)) return;
        MotionEvent event = (MotionEvent) ev;
        ...
        onMotionEvent(event);
    }

onMotionEvent() 将先进行共通的事件拦截和停用区域检查,通过后交给返回手势视图即 EdgeBackPlugin 进一步处理。

// EdgeBackGestureHandler.java
    private void onMotionEvent(MotionEvent ev) {
        int action = ev.getActionMasked();
        if (action == MotionEvent.ACTION_DOWN) {
            mInputEventReceiver.setBatchingEnabled(false);
            mIsOnLeftEdge = ev.getX() <= mEdgeWidthLeft + mLeftInset;
            mMLResults = 0;
            mLogGesture = false;
            mInRejectedExclusion = false;
            boolean isWithinInsets = isWithinInsets((int) ev.getX(), (int) ev.getY());
            // 根据返回手势是否有效、
            // 点击区域是否是停用区域等条件
            // 得到当前是否允许视图处理该手势
            mAllowGesture = !mDisabledForQuickstep && mIsBackGestureAllowed && isWithinInsets
                    && !mGestureBlockingActivityRunning
                    && !QuickStepContract.isBackGestureDisabled(mSysUiFlags)
                    && isWithinTouchRegion((int) ev.getX(), (int) ev.getY());
            if (mAllowGesture) {
                // 更新当前是屏幕左侧还是右侧
                mEdgeBackPlugin.setIsLeftPanel(mIsOnLeftEdge);
                // 发射事件给视图
                mEdgeBackPlugin.onMotionEvent(ev);
            }
        } else if (mAllowGesture || mLogGesture) {
            if (!mThresholdCrossed) {
                mEndPoint.x = (int) ev.getX();
                mEndPoint.y = (int) ev.getY();
                // 多个手指按下的话取消事件处理
                if (action == MotionEvent.ACTION_POINTER_DOWN) {
                    if (mAllowGesture) {
                        logGesture(SysUiStatsLog.BACK_GESTURE__TYPE__INCOMPLETE_MULTI_TOUCH);
                        cancelGesture(ev);
                    }
                    mLogGesture = false;
                    return;
                } else if (action == MotionEvent.ACTION_MOVE) {
                    // 手指移动超过长按阈值的话
                    // 也要取消事件处理
                    if ((ev.getEventTime() - ev.getDownTime()) > mLongPressTimeout) {
                        if (mAllowGesture) {
                            logGesture(SysUiStatsLog.BACK_GESTURE__TYPE__INCOMPLETE_LONG_PRESS);
                            cancelGesture(ev);
                        }
                        mLogGesture = false;
                        return;
                    }
                    ...
                }
            }
            // 通过上述检查的话
            // 将 MOVE、UP 交给视图
            if (mAllowGesture) {
                // forward touch
                mEdgeBackPlugin.onMotionEvent(ev);
            }
        }
        mProtoTracer.scheduleFrameUpdate();
    }

6. 展示返回手势和触发返回

NavigationBarEdgePanel 继续进行后面的工作:手势的判断、视图的刷新、动画的展示。


onMotionEvent() 的逻辑:


DOWN 的时候先让视图变为可见 VISIBLE

MOVE 的处理通过 handleMoveEvent() 判断距离,决定是否要更新赋予 mTriggerBack

UP 的时候将检查该变量决定是否触发返回动作即 triggerBack()

// NavigationBarEdgePanel.java
    public void onMotionEvent(MotionEvent event) {
        if (mVelocityTracker == null) {
            mVelocityTracker = VelocityTracker.obtain();
        }
        mVelocityTracker.addMovement(event);
        switch (event.getActionMasked()) {
            case MotionEvent.ACTION_DOWN:
                mDragSlopPassed = false;
                resetOnDown();
                mStartX = event.getX();
                mStartY = event.getY();
                setVisibility(VISIBLE);
                updatePosition(event.getY());
                mRegionSamplingHelper.start(mSamplingRect);
                mWindowManager.updateViewLayout(this, mLayoutParams);
                break;
            case MotionEvent.ACTION_MOVE:
                handleMoveEvent(event);
                break;
            case MotionEvent.ACTION_UP:
                if (mTriggerBack) {
                    triggerBack();
                } else {
                    cancelBack();
                }
                ...
        }
    }

handleMoveEvent() 则是重要的环节:判断 x 轴的 offset 数值是否达到了阈值 mSwipeThreshold,进而调用 setTriggerBack() 更新 mTriggerBack 变量、同时实时展示角度动画。

// NavigationBarEdgePanel.java
    private void handleMoveEvent(MotionEvent event) {
        float x = event.getX();
        float y = event.getY();
        float touchTranslation = MathUtils.abs(x - mStartX);
        float yOffset = y - mStartY;
        float delta = touchTranslation - mPreviousTouchTranslation;
        ...
        mPreviousTouchTranslation = touchTranslation;
        // 已经超过阈值的话
        // 设置达到触发返回事件条件
        if (!mDragSlopPassed && touchTranslation > mSwipeThreshold) {
            mDragSlopPassed = true;
            mVibratorHelper.vibrate(VibrationEffect.EFFECT_TICK);
            mVibrationTime = SystemClock.uptimeMillis();
            mDisappearAmount = 0.0f;
            setAlpha(1f);
            setTriggerBack(true /* triggerBack */, true /* animated */);
        }
        ...
        boolean triggerBack = mTriggerBack;
        if (Math.abs(mTotalTouchDelta) > mMinDeltaForSwitch) {
            triggerBack = mTotalTouchDelta > 0;
        }
        // 计算方向和偏移值
        mVelocityTracker.computeCurrentVelocity(1000);
        float xVelocity = mVelocityTracker.getXVelocity();
        float yVelocity = mVelocityTracker.getYVelocity();
        float velocity = MathUtils.mag(xVelocity, yVelocity);
        mAngleOffset = Math.min(velocity / 1000 * ARROW_ANGLE_ADDED_PER_1000_SPEED,
                ARROW_MAX_ANGLE_SPEED_OFFSET_DEGREES) * Math.signum(xVelocity);
        if (mIsLeftPanel && mArrowsPointLeft || !mIsLeftPanel && !mArrowsPointLeft) {
            mAngleOffset *= -1;
        }
        // 如果纵向偏移值达到了横向偏移两倍
        // 取消返回事件触发
        if (Math.abs(yOffset) > Math.abs(x - mStartX) * 2) {
            triggerBack = false;
        }
        setTriggerBack(triggerBack, true /* animated */);
        if (!mTriggerBack) {
            touchTranslation = 0;
        } else if (mIsLeftPanel && mArrowsPointLeft
                || (!mIsLeftPanel && !mArrowsPointLeft)) {
            // 更新角度
            touchTranslation -= getStaticArrowWidth();
        }
        setDesiredTranslation(touchTranslation, true /* animated */);
        // 更新角度动画
        updateAngle(true /* animated */);
        ...
    }
    private void setTriggerBack(boolean triggerBack, boolean animated) {
        if (mTriggerBack != triggerBack) {
            mTriggerBack = triggerBack;
            mAngleAnimation.cancel();
            updateAngle(animated);
            mTranslationAnimation.cancel();
        }
    }

7. InputManager 注入返回事件

NavigationBarEdgePanel 收到 UP 时,发现已经设置触发返回事件标志的话将通过 triggerBack() 发出注入返回事件的请求。


该函数首先会取出返回手势视图创建时带入的 BackCallback 实例并将触发 Back 手势的回调发射出去。其后就振动的触发、动画的结束、可见性改回 GONE 等收尾工作。

// NavigationBarEdgePanel.java
    private void triggerBack() {
        mBackCallback.triggerBack();
        // 产生 click 振动
        if (isSlow
                || SystemClock.uptimeMillis() - mVibrationTime >= GESTURE_DURATION_FOR_CLICK_MS) {
            mVibratorHelper.vibrate(VibrationEffect.EFFECT_CLICK);
        }
        ...
        // 隐藏动画的执行
        Runnable translationEnd = () -> {
            mAngleOffset = Math.max(0, mAngleOffset + 8);
            updateAngle(true /* animated */);
            mTranslationAnimation.setSpring(mTriggerBackSpring);
            setDesiredTranslation(mDesiredTranslation - dp(32), true /* animated */);
            // 最终将视图隐藏
            animate().alpha(0f).setDuration(DISAPPEAR_FADE_ANIMATION_DURATION_MS)
                    .withEndAction(() -> setVisibility(GONE));
            mArrowDisappearAnimation.start();
            scheduleFailsafe();
        };
        ...
    }

回调的函数也叫 triggerBack()。其工作是准备 Code 为 KEYCODE_BACKKeyEvent 并通过 InputManager#injectInputEvent() 注入到 InputDispatcher,等待 Input 系统的进一步处理和发出。

// EdgeBackGestureHandler.java
    private final NavigationEdgeBackPlugin.BackCallback mBackCallback =
            new NavigationEdgeBackPlugin.BackCallback() {
                @Override
                public void triggerBack() {
                    mFalsingManager.isFalseTouch(BACK_GESTURE);
                    // 发送返回按键的按下和抬起事件
                    boolean sendDown = sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK);
                    boolean sendUp = sendEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK);
                    ...
                }
                ...
            };
    private boolean sendEvent(int action, int code) {
        long when = SystemClock.uptimeMillis();
        // 构建 KeyEvent Java 实例
        final KeyEvent ev = new KeyEvent(when, when, action, code, 0 /* repeat */,
                0 /* metaState */, KeyCharacterMap.VIRTUAL_KEYBOARD, 0 /* scancode */,
                KeyEvent.FLAG_FROM_SYSTEM | KeyEvent.FLAG_VIRTUAL_HARD_KEY,
                InputDevice.SOURCE_KEYBOARD);
        // 赋值 DisplayId 并注入
        ev.setDisplayId(mContext.getDisplay().getDisplayId());
        return InputManager.getInstance()
                .injectInputEvent(ev, InputManager.INJECT_INPUT_EVENT_MODE_ASYNC);
    }

8. Dispatcher 分发返回事件

InputManager 将通过 IMS 和 JNI 最终向 InputDispatcher 注入 KeyEvent。


先将注入的 InputEvent 转换为 KeyEvent

加工成 KeyEvent 先交给 PhoneWindowManager 拦截处理

之后封装成 KeyEntry 并 push 到 queue 中

queue 中 Entry 取出并通过 enqueueInboundEventLocked() 放入存放待分发事件的 mInboundQueue 中

唤醒 InputDispatcher 的 Looper 进入 Thread 的 dispatchOnce() 准备分发

// InputDispatcher.cpp
InputEventInjectionResult InputDispatcher::injectInputEvent(const InputEvent* event...) {
    ...
    std::queue<std::unique_ptr<EventEntry>> injectedEntries;
    switch (event->getType()) {
        // 判断注入事件类型
        case AINPUT_EVENT_TYPE_KEY: {
            const KeyEvent& incomingKey = static_cast<const KeyEvent&>(*event);
            int32_t action = incomingKey.getAction();
            ...
            KeyEvent keyEvent;
            keyEvent.initialize(...);
            // 按键事件的话需要 PhoneWindowManager 预处理
            if (!(policyFlags & POLICY_FLAG_FILTERED)) {
                android::base::Timer t;
                mPolicy->interceptKeyBeforeQueueing(&keyEvent, /*byref*/ policyFlags);
            }
            ...
            // 之后再放入到待注入队列中
            std::unique_ptr<KeyEntry> injectedEntry =
                    std::make_unique<KeyEntry>(...);
            injectedEntries.push(std::move(injectedEntry));
            break;
        }
        ...
    }
    ...
    // 取出注入事件放入待分发队列
    while (!injectedEntries.empty()) {
        needWake |= enqueueInboundEventLocked(std::move(injectedEntries.front()));
        injectedEntries.pop();
    }
    mLock.unlock();
    // 唤醒 Looper 去分发
    if (needWake) {
        mLooper->wake();
    }
    // 返回注入结果
    InputEventInjectionResult injectionResult;
    { // acquire lock
        std::unique_lock _l(mLock);
        if (syncMode == InputEventInjectionSync::NONE) {
            injectionResult = InputEventInjectionResult::SUCCEEDED;
        ...
        }
        injectionState->release();
    } // release lock
    return injectionResult;
}

篇幅原因,省略了 InputDispatcher 分发按键事件的后续(事实上和前面讲述分发 Input 事件的流程大同小异)。

最终通过 InputChannel 抵达当前 Window 中 DecorViewdispatchKeyEvent()

9. App 收到返回事件

篇幅原因,省略 DecorView 树分发 KeyEvent 按键事件的流程。


一般来说 View 树不会拦截返回按键,最终将抵达 Activity#onBackPressed()

它进而决定是 Fragment 处理返回还是 Activity 进行 finish(当然 App 可以覆写它以改写行为)

// Activity.java
    public void onBackPressed() {
        ...
        FragmentManager fragmentManager = mFragments.getFragmentManager();
        // Fragment 先处理
        if (!fragmentManager.isStateSaved() && fragmentManager.popBackStackImmediate()) {
            return;
        }
        // Activity 自己处理
        if (!isTaskRoot()) {
            finishAfterTransition();
            return;
        }
        ...
    }

结语

1832b220aa754cd18c504acc7686a560.png

回顾一下 Back Gesture 响应的整体流程:


NavigationBarView 添加到 Window 上去的时候创建管理类 EdgeBackGestureHandler

EdgeBackGestureHandler 向 WMS 注册返回手势停用的监听者 SystemGestureExclusionListener

通过 InputManager 向 InputDispatcher 注入 InputMonitor 以监听系统 Input 事件

EdgeBackGestureHandler 创建手势视图 NavigationBarEdgePanel 的 Window 参数并将该视图添加到 Window

InputDispacher 将 Input 事件分发给见识者即 EdgeBackGestureHandler 定义的 InputEventReceiver 回调,并判断停用区域进行预处理

手势视图 NavigationBarEdgePanel 收到事件后进一步进行动画、角度和阈值的判断,决定是否触发返回事件

EdgeBackGestureHandler 收到触发请求通过 InputManager 注入 BACK 按键的返回事件

InputDispatcher 将 BACK 按键分发到背面 App 的 DecorView

经过 View 树的按键分发,Activity 的 onBackPressed 进行 Fragment 或 Activity 的返回处理

简单来讲,SystemUI 利用 InputMonitor 监视系统 Touch 事件、监听和获取 WMS 中保存的手势停用区域 Region、依据 Touch 事件展示动画和触发返回、通过 InputManager 注入返回按键事件、最终抵达背面 App。


整体篇幅挺大,还省略了诸多细节。感兴趣的朋友可以跟踪下整个流程,如果发现错误或笔者错过的重要细节,欢迎留言。

参考文章

相关文章
|
29天前
|
人工智能 搜索推荐 物联网
Android系统版本演进与未来展望####
本文深入探讨了Android操作系统从诞生至今的发展历程,详细阐述了其关键版本迭代带来的创新特性、用户体验提升及对全球移动生态系统的影响。通过对Android历史版本的回顾与分析,本文旨在揭示其成功背后的驱动力,并展望未来Android可能的发展趋势与面临的挑战,为读者呈现一个既全面又具深度的技术视角。 ####
|
27天前
|
IDE Java 开发工具
移动应用与系统:探索Android开发之旅
在这篇文章中,我们将深入探讨Android开发的各个方面,从基础知识到高级技术。我们将通过代码示例和案例分析,帮助读者更好地理解和掌握Android开发。无论你是初学者还是有经验的开发者,这篇文章都将为你提供有价值的信息和技巧。让我们一起开启Android开发的旅程吧!
|
15天前
|
监控 Java Android开发
深入探索Android系统的内存管理机制
本文旨在全面解析Android系统的内存管理机制,包括其工作原理、常见问题及其解决方案。通过对Android内存模型的深入分析,本文将帮助开发者更好地理解内存分配、回收以及优化策略,从而提高应用性能和用户体验。
|
16天前
|
存储 安全 Android开发
探索Android系统的最新安全特性
在数字时代,智能手机已成为我们生活中不可或缺的一部分。随着技术的不断进步,手机操作系统的安全性也越来越受到重视。本文将深入探讨Android系统最新的安全特性,包括其设计理念、实施方式以及对用户的影响。通过分析这些安全措施如何保护用户免受恶意软件和网络攻击的威胁,我们希望为读者提供对Android安全性的全面了解。
|
1月前
|
监控 Java Android开发
深入探讨Android系统的内存管理机制
本文将深入分析Android系统的内存管理机制,包括其内存分配、回收策略以及常见的内存泄漏问题。通过对这些方面的详细讨论,读者可以更好地理解Android系统如何高效地管理内存资源,从而提高应用程序的性能和稳定性。
66 16
|
22天前
|
安全 Android开发 iOS开发
深入探讨Android与iOS系统的差异及未来发展趋势
本文旨在深入分析Android和iOS两大移动操作系统的核心技术差异、用户体验以及各自的市场表现,进一步探讨它们在未来技术革新中可能的发展方向。通过对比两者的开放性、安全性、生态系统等方面,本文揭示了两大系统在移动设备市场中的竞争态势和潜在变革。
|
1月前
|
算法 JavaScript Android开发
|
1月前
|
安全 搜索推荐 Android开发
揭秘安卓与iOS系统的差异:技术深度对比
【10月更文挑战第27天】 本文深入探讨了安卓(Android)与iOS两大移动操作系统的技术特点和用户体验差异。通过对比两者的系统架构、应用生态、用户界面、安全性等方面,揭示了为何这两种系统能够在市场中各占一席之地,并为用户提供不同的选择。文章旨在为读者提供一个全面的视角,理解两种系统的优势与局限,从而更好地根据自己的需求做出选择。
96 2
|
1月前
|
安全 搜索推荐 程序员
深入探索Android系统的碎片化问题及其解决方案
在移动操作系统的世界中,Android以其开放性和灵活性赢得了广泛的市场份额。然而,这种开放性也带来了一个众所周知的问题——系统碎片化。本文旨在探讨Android系统碎片化的现状、成因以及可能的解决方案,为开发者和用户提供一种全新的视角来理解这一现象。通过分析不同版本的Android系统分布、硬件多样性以及更新机制的影响,我们提出了一系列针对性的策略,旨在减少碎片化带来的影响,提升用户体验。
|
1月前
|
安全 Android开发 iOS开发
深入探索iOS与Android系统的差异性及优化策略
在当今数字化时代,移动操作系统的竞争尤为激烈,其中iOS和Android作为市场上的两大巨头,各自拥有庞大的用户基础和独特的技术特点。本文旨在通过对比分析iOS与Android的核心差异,探讨各自的优势与局限,并提出针对性的优化策略,以期为用户提供更优质的使用体验和为开发者提供有价值的参考。