Android 13 返回导航大变更:返回键彻底废弃 + 可预见型返回手势

Posted TechMerger

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 13 返回导航大变更:返回键彻底废弃 + 可预见型返回手势相关的知识,希望对你有一定的参考价值。

android 10 首次引入了全局返回手势,但直到返回触发才能看到目标上层画面。13 针对该特性进行了优化,即返回触发之前可以预览上层画面。同时彻底废弃了返回键相关的 API,这将对现有的 App 逻辑产生巨大的影响!

前言

Android 13 针对包括手机、大屏、折叠屏等 Android 设备推出了可预见型返回手势(Predictive Back Gesture)特性。该特性将便于用户在返回完成之前可以先预览到目标画面或结果,这样的话可以允许他们决定是否要继续返回或者放弃并停留在当前画面。

另外引入关于 KEYCODE_BACK KeyEvent 相关的一系列变更。

为节省篇幅和统一认识,后续的相关描述将按照如下规则简称:

  • 本次引入的可预见型返回手势 + KEYCODE_BACK 系列变更:统称为新返回导航
  • KEYCODE_BACK KeyEvent:简称为 KEYCODE_BACK
  • 传统导航模式和 Swipe-Up 导航模式下的返回按钮:简称为Back KeyButton
  • 全局返回手势:简称为Back Gesture
Back KeyButtonBack Gesture

后续将按照如下几个方面去阐述:

  1. 新返回导航的具体影响
  2. 如何确定是否受影响
  3. 适配方案的选择
  4. 适配方案的详述
  5. SDK API 适配方案的深入探讨
  6. 新返回导航支持与否的深入比较和原理分析
  7. 注意和残留事项

1. 新返回导航的具体影响

简单来说会产生如下影响:

  • 返回手势的可预见型 UI 的增强:展示返回触发前上层画面
  • 原有 API 废弃:
    • KEYCODE_BACK:详述见小章节
    • Activity/Dialog:onBackPressed()
  • 引入全新的 SDK 返回相关 API:
    • Manifest 中 enableOnBackInvokedCallback 属性
    • Activity/Dialog/Window:getOnBackInvokedDispatcher()
    • OnBackInvokedDispatcher
    • OnBackInvokedCallback

备注:无关TargetSDKVersion ,运行在 13 上只要支持新返回导航均会受收到如上的影响。

KEYCODE_BACK 非推荐

准确含义是 13 上一旦开启新返回导航支持,无论是 Back Gesture 的触发还是 Back KeyButton 的点击,App 均无法监听到 KEYCODE_BACK 事件。即相关的如下 API 将无法被回调:

  • Activity
    • dispatchKeyEvent()
    • onKeyDown()
    • onKeyUp()
    • onBackPressed()
  • Dialog:API 同上

2. 如何确定是否受影响

除了上述提到的具体变更以外,所有 KEYCODE_BACK 的相关逻辑都得测试一下是否存在问题,比如容易忽略的 View、Dialog$Builder。

简单来说,检查下现有代码是否用到了如下 API:

  1. Activity/Dialog#onBackPressed()
  2. Activity:dispatchKeyEvent()、onKeyDown()、onKeyUp(),监听 KEYCODE_BACK
  3. Activity:使用 AndroidX 的 OnBackPressedDispatcher、OnBackPressedCallback API
  4. Dialog:dispatchKeyEvent()、onKeyDown()、onKeyUp()、setOnKeyListener(),监听 KEYCODE_BACK
  5. AlertDialog$Builder:setOnKeyListener(),监听 KEYCODE_BACK
  6. View:dispatchKeyEvent()、onKeyDown()、onKeyUp()、setOnKeyListener(),监听 KEYCODE_BACK

3. 适配方案的选择

大多数 App 都会选择自定义返回导航,可选的方式包括 SDK 的原生 API 和 AndroidX 的 Callback API。依据这些情况的不同、App 适配的意愿不同,适配的方案也不一样。

没有自定义返回导航的场景

加入新返回导航的支持即可,具体见《4.1 加入新返回导航的支持》章节。

自定义返回导航的场景

需要按照现有 API 是否接入了 AndroidX 的 OnBackPressedDispatcher 进行分情况适配。

是否使用了AndroidX如何处理返回导航推荐的适配策略备注
YesAndroidX APIs升级已有的 AndroidX 返回 API3rd Party App 的大多数 case
Unsupported SDK APIs迁移非推荐 SDK 返回 API 到 AndroidX API少部分 App
NoUnsupported SDK APIs,但我愿意迁移迁移非推荐 SDK 返回 API 到新 SDK 返回 API系统 App 的大多数 case
Unsupported SDK APIs,但我不愿意迁移延迟加入新返回导航的支持,直到它成为必须特性少部分 App

4. 适配方案的详述

4.1 加入新返回导航的支持

Manifest 中针对新返回导航特性引入的属性 enableOnBackInvokedCallback 默认是 false,即默认不支持该特性,支持的话需要声明为 true。

<application
    ...
    android:enableOnBackInvokedCallback="true"
    ... >
...
</application>

实测发现:即便声明成了 false,但如果代码中残存了 13 的新 API(比如 OnBackInvokedCallback)的使用,仍会导致新返回导航发生作用。

也就是说,不支持的话,就不要使用任何新的返回相关 API。

4.2 关闭新返回导航的支持

正如上面所述,按照如下即可关闭对新返回导航的支持:

  1. enableOnBackInvokedCallback 声明为 false(不声明亦可)
  2. 不要使用 OnBackInvokedCallback 等返回相关 API

4.3 升级已有的 AndroidX 返回 API

对于已使用 AndroidX 返回 API 的 App 只需开启新返回导航的支持,其他的适配工作交由 AndroidX 框架来完成。

Supporting the predictive back gesture requires updating your app, using the OnBackPressedCallback AppCompat 1.6.0-alpha03 (AndroidX) or higher API.

笔者按照官方说明将 AppCompat 包升级到了 1.6.0-alpha03

dependencies 
    implementation 'androidx.appcompat:appcompat:1.6.0-alpha03'

使用其提供的 OnBackPressedCallback API 监听 Activity 的 Back 操作如下:

class BackKeyTestActivityAppCompat : AppCompatActivity() 
    override fun onCreate(savedInstanceState: Bundle?) 
        ...
        onBackPressedDispatcher.addCallback(object : OnBackPressedCallback(true) 
            override fun handleOnBackPressed() 
                Log.d("BackGesture", "Activity#handleOnBackPressed()")
            
        )
    

    override fun dispatchKeyEvent(event: KeyEvent): Boolean 
        Log.d("BackGesture", "Activity#dispatchKeyEvent() event:$event")
        return super.dispatchKeyEvent(event)
    

    override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean 
        Log.d("BackGesture", "Activity#onKeyDown() event:$event")
        return super.onKeyDown(keyCode, event)
    

    override fun onKeyUp(keyCode: Int, event: KeyEvent): Boolean 
        Log.d("BackGesture", "Activity#onKeyUp() event:$event")
        return super.onKeyUp(keyCode, event)
    

    override fun onBackPressed() 
        Log.d("BackGesture", "onBackPressed()")
        super.onBackPressed()
    

可是实测发现:

  • 即便在 13 上开启了新返回导航,无论是 Back Gesture 还是 Back KeyButton,Callback 和 KeyEvent 回调均未执行,Activity 将直接结束
  • 但同样的代码运行在 12 上的话,Back Gesture 和 Back KeyButton 下 Callback 和 KeyEvent 均能被回调

12-Back Gesture 的执行日志:

05-31 10:35:28.732 11267 11267 D BackGesture: Activity#dispatchKeyEvent() event:KeyEvent  action=ACTION_DOWN, keyCode=KEYCODE_BACK, ... 
05-31 10:35:28.733 11267 11267 D BackGesture: Activity#onKeyDown() event:KeyEvent  action=ACTION_DOWN, keyCode=KEYCODE_BACK, ... 
05-31 10:35:28.733 11267 11267 D BackGesture: Activity#dispatchKeyEvent() event:KeyEvent  action=ACTION_UP, keyCode=KEYCODE_BACK, ... 
05-31 10:35:28.733 11267 11267 D BackGesture: Activity#onKeyUp() event:KeyEvent  action=ACTION_UP, keyCode=KEYCODE_BACK, ... 
05-31 10:35:28.733 11267 11267 D BackGesture: onBackPressed()
05-31 10:35:28.734 11267 11267 D BackGesture: Activity#handleOnBackPressed()

12-Back KeyButton 的执行日志:

05-31 10:37:21.724 11267 11267 D BackGesture: Activity#dispatchKeyEvent() event:KeyEvent  action=ACTION_DOWN, keyCode=KEYCODE_BACK... 
05-31 10:37:21.724 11267 11267 D BackGesture: Activity#onKeyDown() event:KeyEvent  action=ACTION_DOWN, keyCode=KEYCODE_BACK... 
05-31 10:37:21.846 11267 11267 D BackGesture: Activity#dispatchKeyEvent() event:KeyEvent  action=ACTION_UP, keyCode=KEYCODE_BACK... 
05-31 10:37:21.846 11267 11267 D BackGesture: Activity#onKeyUp() event:KeyEvent  action=ACTION_UP, keyCode=KEYCODE_BACK... 
05-31 10:37:21.846 11267 11267 D BackGesture: onBackPressed()
05-31 10:37:21.846 11267 11267 D BackGesture: Activity#handleOnBackPressed()

调试了一下,发现 AppCompat 框架里使用 13 的新 SDK API 前的版本判断有问题:

public class ComponentActivity 
    protected void onCreate(@Nullable Bundle savedInstanceState) 
        ...
        if (Build.VERSION.SDK_INT >= 33) 
            mOnBackPressedDispatcher.setOnBackInvokedDispatcher(getOnBackInvokedDispatcher());
        
        ...
    


public final class OnBackPressedDispatcher 
    Cancellable addCancellableCallback(@NonNull OnBackPressedCallback onBackPressedCallback) 
        ...
        if (Build.VERSION.SDK_INT >= 33) 
            updateBackInvokedCallbackState();
            onBackPressedCallback.setIsEnabledConsumer(mEnabledConsumer);
        
        return cancellable;
    

Beta 版的 SDK_INT 常量仍然是 12L 的 32,到正式发布才会改为 33,所以版本判断应当使用 BuildCompat 的如下 API:

// BuildCompat.java
    public static boolean isAtLeastT() 
        return VERSION.SDK_INT >= 33
                || (VERSION.SDK_INT >= 32
                && isAtLeastPreReleaseCodename("Tiramisu", VERSION.CODENAME));
    

官方文档提示说的是使用 1.6.0-alpha03 及以上,那么 03 应该是首次引入上述适配的版本,可能还没做好。查了下 AppCompat 包是否出现最新版本,果然有个 1.6.0-alpha04

Version 1.6.0-alpha04

​ May 18, 2022

更新了后确实好了,即 13 上开启支持的话,无论是 Back Gesture 还是 Back KeyButton,能像预期的那样都只会输出 androidX 版本的 Callback,Back 相关 KeyEvent 回调将不再执行

05-31 10:55:10.773  5041  5041 D BackGesture: Activity#handleOnBackPressed()

但仍有一点未达预期:

  • 按理说 13 上关闭支持的话,无论是 Back Gesture 还是 Back KeyButton,运行结果应该和 12 保持一致,即收到 Back 相关 KeyEvent 回调以及 OnBackPressedCallback
  • 可实测发现:只有 Back KeyButton 点击是上述结果,Back Gesture 的话只收到了 Callback、没有 KeyEvent 回调,这里有点奇怪

4.4 迁移非推荐 SDK 返回 API 到 AndroidX API

适配步骤:

  1. 迁移已有的系统返回处理逻辑到 AndroidX 的 OnBackPressedDispatcher API,他需要指定 OnBackPressedCallback 实现,详细的可参考如何提供自定义返回导航

    对于 Activity:

    class MyActivity : AppCompatActivity() 
        override fun onCreate(savedInstanceState: Bundle?) 
            super.onCreate(savedInstanceState)
            val callback = onBackPressedDispatcher.addCallback(this) 
                // Handle the back button event
            
        
        ...
    
    

    对于 Fragment:

    public class FormEntryFragment extends Fragment 
        @Override
        public void onAttach(@NonNull Context context) 
            super.onAttach(context);
            OnBackPressedCallback callback = new OnBackPressedCallback(
                true // default to enabled
            ) 
                @Override
                public void handleOnBackPressed() 
                    showAreYouSureDialog();
                
            ;
            requireActivity().getOnBackPressedDispatcher().addCallback(
                this, // LifecycleOwner
                callback);
        
    
    
  2. 禁用原有的系统返回手势回调,比如 onBackPressed()、KEYCODE_BACK

    解释:getOnBackPressedDispatcher 早在 13 之前就已经支持,既然换了就没必要保留 SDK API 逻辑。

  3. 最后记得加入新返回导航的支持。

4.5 迁移非推荐 SDK 返回 API 到新 SDK 返回 API

适配步骤:

  1. 运行在 13 及之后的版本上使用全新的 SDK API 即 OnBackInvokedCallback,12及之前的版本仍可使用旧的返回 API

  2. 在 Activity、Dialog、Window 等 Window 级别的组件里需要监听返回手势的逻辑处注册实现了 onBackInvoked 方法的 OnBackInvokedCallback。这将阻止当前的 Activity 被结束,这样的话当用户触发了系统返回操作的话你的 Callback 将有机会执行你预期的返回动作

  3. 为了确保正确支持系统“后退导航”的未来增强功能,你的 App 必须注销 OnBackInvokedCallback。否则,用户在使用系统后退导航时可能会看到不良行为,例如,在视图之间“卡住”并强制他们退出应用。

    To ensure that future enhancements to the system Back navigation are properly supported, your app MUST unregister the OnBackInvokedCallback. Otherwise, users may see undesirable behavior when using a system Back navigation—for example, “getting stuck” between views and forcing them to force quit your app.

    @Override
    void onCreate() 
      if (BuildCompat.isAtLeastT()) 
        getOnBackInvokedDispatcher().registerOnBackInvokedCallback(
            OnBackInvokedDispatcher.PRIORITY_DEFAULT,
            () -> 
                // ...
            
        );
      
    
    

    比如 WebView 需要拦截返回手势以回退网页,当已经返回到主画面的时候应当注销该 Callback 让系统来处理 finish。

  4. 同样的,加入新返回导航的支持。

备注:onBackPressed() 逻辑保留也没有关系,并不会发生冲突,而且为了兼容 13 之前的系统功能本就应该保留。

registerOnBackInvokedCallback() 说明

registerOnBackInvokedCallback() 调用的时候需要提供如下两个参数:

  • priority按照注册的逆序进行,但如果是高优先级的先回调。可选范围:int 型,亦可选如下预设常量:

    • PRIORITY_DEFAULT:值为 0,普通回调
    • PRIORITY_OVERLAY:值为 1000000,优先回调

    但不可以是负值、否则会发生 IllegalArgumentException 异常

    java.lang.IllegalArgumentException: Application registered OnBackInvokedCallback cannot have negative priority. Priority: -1

  • callback:OnBackInvokedCallback 实例,会在 Back Gesture 触发、Back KeyButton 按压的时候被回调

    实际结果:只有最后一个 register 的 Callback 得到调用,但如果列表里存在 PRIORITY_OVERLAY 等更高优先级的 Callback 的话则优先。与如下描述不符:

    When back is triggered, callbacks on the in-focus window are invoked in reverse order in which they are added within the same priority. Between different priorities, callbacks with higher priority are invoked first.

5. SDK API 适配方案的深入探讨

5.1 案例

和 KEYCODE_BACK 相关的有很多 API 可以处理、场景也很繁杂,简单举例如下:

  1. 覆写 Activity#onKeyDown() 处理 KEYCODE_BACK 的 DOWN:
class Activity 
    override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean 
        if ( ... ) return false

        when (keyCode) 
            KeyEvent.KEYCODE_BACK ->  methodA() 
            KeyEvent.KEYCODE_MENU ->  ... 
            else -> 
        

        return if ( ... ) 
            true
         else super.onKeyDown(keyCode, event)
    

  1. 覆写 Activity#onKeyUp() 处理 KEYCODE_BACK 的 UP

  2. 覆写 Activity#dispatchKeyEvent() 将 KeyEvent 传递到 Fragment 处理

  3. 覆写 Activity#onBackPressed() 处理返回回调

  4. 调用 Dialog#setOnKeyListener() 处理 KEYCODE_BACK

  5. 调用 AlertDialog.Builder#setOnKeyListener() 处理 KEYCODE_BACK

  6. 覆写 Dialog#dispatchKeyEvent() 处理 KEYCODE_BACK

  7. 覆写 EditText#onKeyPreIme() 处理 KEYCODE_BACK

  8. 甚至还有覆写 View 的 dispatchKeyEvent() 等函数处理 KEYCODE_BACK

5.2 适配

适配的目的在于确保如下:

  • 12 及以前的设备上 Back Gesture、Back KeyButton 以及其他 Key 抵达的时候,onKeyUp() 等回调能正常收到
  • 13 上开启新返回导航支持的话:Back Gesture 和 Back KeyButton 能在对应的 Callback 里回调,并和之前的 Back 动作保持一致。同时,其他 Key 仍能在 onKeyUp() 等原有函数里监听到

以上述的案例 1 的代码为例,如下是如何改造以保证能在 12 和 13 上运行一样的 Key 相关动作:

class Activity 
    private var onBackInvokedCallback: OnBackInvokedCallback? = null

    override fun onCreate(savedInstanceState: Bundle?) 
        ...

        if (BuildCompat.isAtLeastT()) 
            onBackInvokedCallback = OnBackInvokedCallback 
                onBackEvent()
            .also 
                onBackInvokedDispatcher.registerOnBackInvokedCallback(
                    OnBackInvokedDispatcher.PRIORITY_DEFAULT,
                    it
                )
            
        
    

    override fun onDestroy() 
        super.onDestroy()

        if (BuildCompat.isAtLeastT()) 
            onBackInvokedCallback?.let 
                onBackInvokedDispatcher.unregisterOnBackInvokedCallback(it)
            
        
    

    private fun onBackEvent() 
        // if ( ... ) return false
        if ( ... ) return

        // when (keyCode) 
        //     KeyEvent.KEYCODE_BACK ->  methodA() 
        //     KeyEvent.KEYCODE_MENU ->  ... 
        //     else -> 
        // 
        methodA()

        // return if ( ... ) 
        //     true
        //  else super.onKeyDown(keyCode, event)
    

    // 为兼容旧版仍需完全保留
    override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean 
        ...
    

如上适配的关键点在于:除了在 Manifest 中将 enableOnBackInvokedCallback 属性打开和注册 OnBackInvokedCallback() 以外,重点在于如何实现 onBackInvoked() 来达到旧版的同等返回逻辑:

  • 删除掉 Back KeyButton 以外的逻辑,因为 Callback 只针对 Back 事件,没有可能收到其他 KEY 事件
  • 删除掉 KEYCODE_BACK 的检查,因为 Callback 只针对 Back 事件、没有必要检查
  • 按照原有的 dispatchtKeyEvent()、onKeyDown()、onKeyUp() 的逻辑决定 return true、false 以及 super 的改写办法
  • 兼容旧版本保留所有的 KeyEvent 的处理逻辑

此外,需要留意如下一些细节:

  • 新的 Callback 如何区分 dispatchKeyEvent()、onKeyDown()、onKeyUp() 的时机?

    无法区分,开启新返回导航之后只有一个 OnBackInvokedCallback 回调时机,其在 Back Gesture TriggerBack KeyButton Up 时触发。

    原本时序:dispatchKeyEvent(DOWN) -> onKeyDown() -> dispatchKeyEvent(UP) -> onKeyUp()

  • 新的 Callback 如何针对 KEYCODE_BACK 的 DOWN 和 UP 作区分?

    无法区分,开启新返回导航之后只有最终的 Callback,没有 DOWN 和 UP 之分。

  • 新的 Callback 针对 dispatchKeyEvent() 等处理的 return true、false、super 如何区分?

    • false:本意是不处理,对应于现在的 Callback 可以是什么也不做或直接 return
    • true:本意是处理,对应于现在的 Callback 可以是处理外加 return
    • super:本意是交由父类处理,对应于现在的 Callback 可以是 return 或者直接删除,这取决于原来的 super 调用位置,也可以考虑在某条件满足的时候提前注销 Callback 这种思路
  • Back 以外,比如 Menu KeyEvent 的监听是否受影响?

    不受影响。之前的 Menu Key 等监听在 13 上仍可以监听到、正常运行,可以保留。

  • 如何兼容 13 以前的版本呢?

    新老处理共存,判断运行版本:13 上开启的话执行新逻辑,13 以前继续沿用旧逻辑。

5.3 集成到 Base 中统一处理

Activity、Fragment 以及 Dialog 众多的情况下,可在 Base 类里加入统一的注册和销毁 Callback 的复用代码。

为了不干预不需要处理的子类,默认不进行注册。需要的子类覆写 isNeedInterceptBackEvent() 返回 true 并实现自己的 Callback 逻辑即可。

如下的 BaseActivity 事例代码:

open class BaseActivity: AppCompatActivity() 
    private var onBackInvokedCallback: OnBackInvokedCallback? = null

    /**
     * Inner class for handle back callback totally.
     */
    internal class OnBackInvokedCallbackInner constructor(baseActivity: BaseActivity) :
        OnBackInvokedCallback 
        private val activity: WeakReference<BaseActivity>

        override fun onBackInvoked() 
            activity.get()?.apply 
                onBackEvent()
            
        

        init 
            activity = WeakReference(baseActivity)
        
    

    /**
     * Override this method and return true if child wanna handle back event.
     */
    open fun isNeedInterceptBackEvent(): Boolean = false

    /**
     * Default back operation is invoking onBackPressed().
     * Child activity could override and implement its own operation.
     */
    open fun onBackEvent() 
        onBackPressed()
    

    override fun onCreate(savedInstanceState: Bundle?) 
        ...
        if (isNeedInterceptBackEvent() && BuildCompat.isAtLeastT()) 
            onBackInvokedCallback = OnBackInvokedCallbackInner(this).also 
                onBackInvokedDispatcher.registerOnBackInvokedCallback(
                    OnBackInvokedDispatcher.PRIORITY_DEFAULT,
                    it
                )
            
        
    

    override fun onDestroy() 
        ...
        if (BuildCompat.isAtLeastT()) 
            onBackInvokedCallback?.let 
                onBackInvokedDispatcher.unregisterOnBackInvokedCallback(it)
            
        
    

需要的子类进行覆写。

class BackKeyHandleActivity : BaseActivity() 
    ...
    override fun isNeedInterceptBackEvent(): Boolean = true

    override fun onBackEvent()  ... 

    // 兼容 13 之前的逻辑
    override fun onBackPressed()  ... 

6. 新返回导航支持与否的深入比较和原理分析

针对采用新 SDK 返回 API 方案分别在 13 上开启和关闭新返回导航的支持,观察 KeyEvent 相关的 Log 输出,并尝试分析一些原理方面的差异。

6.1 开启支持

Back Gesture

开启新的返回手势支持的话,只能收到 OnBackInvokedCallback 回调,确实无法像以前一样灵活、精细地处理 KEYCODE_BACK 了。

如下的系统日志可以瞥见 Callback 处理的一些细节。

05-26 10:26:27.929   787   787 D NoBackGesture: Start gesture: MotionEvent  action=ACTION_DOWN ... 
05-26 10:26:27.929   787   787 D NoBackGesture: Prediction [1653531987929,47,633,-1,0.000000,1]
05-26 10:26:27.930   787   787 D NoBackGesture: reset mTriggerBack=false
05-26 10:26:27.931   787   852 D ShellBackPreview: initAnimation mMotionStarted=false
05-26 10:26:27.932   787   787 D NoBackGesture: Gesture [1653531987932,alw=TRUE,TRUE,TRUE,FALSE,disp=Point(1080, 2340),wl=82,il=0,wr=82,ir=0,excl=SkRegion()]
05-26 10:26:27.933   599  2725 D CoreBackPreview: Focused window found using getFocusedWindowToken
05-26 10:26:27.933   599  2725 D CoreBackPreview: startBackNavigation currentTask=Task1d3c440 #502 type= ..., callbackInfo=OnBackInvokedCallbackInfo ... 
05-26 10:26:27.934   787   852 D ShellBackPreview: Received backNavigationInfo:BackNavigationInfo...
05-26 10:26:27.963   787   787 D OnBackInvokedDispatcher: ViewRootImpl.registerBackCallbackOnWindow. Dispatcher:android.window.WindowOnBackInvokedDispatcher@be64a11 Package:com.android.systemui IWindow:android.view.ViewRootImpl$W@5c4e776 Session:android.view.IWindowSession$Stub$Proxy@3998bd7
05-26 10:26:27.968   787   787 V OnBackInvokedDispatcher: Proxy setActual android.window.WindowOnBackInvokedDispatcher@be64a11. Current null
05-26 10:26:27.968   787   787 V OnBackInvokedDispatcher: Proxy transferring 0 callbacks to android.window.WindowOnBackInvokedDispatcher@be64a11
05-26 10:26:28.271  3978  3978 D BackGesture: onBackInvoked()

通过 adb shell dumpsys input 命令确实也没有看到 InputFlinger 发送 KEYCODE_BACK 的记录。

    MotionEvent(deviceId=8, eventTime=2965229468000, source=TOUCHSCREEN | STYLUS, displayId=0, action=DOWN ...)
    MotionEvent(deviceId=8, eventTime=2965457324000, source=TOUCHSCREEN | STYLUS, displayId=0, action=MOVE ...)
    ...
    MotionEvent(deviceId=8, eventTime=2965524225000, source=TOUCHSCREEN | STYLUS, displayId=Android 多返回栈技术详解

手机桌面显示返回键怎么设置

关于TaskStackBuilder

防止应用在导航栏上按下 Home 时返回 BootActivity

iOS -- 全局导航栏返回键

关于iOS导航控制器隐藏和显示会出现返回键失效,导航栏标题动画异常