Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Navigation in Android (七) ——关于安卓“返回”按键的干预处理 #201

Open
soapgu opened this issue May 24, 2023 · 0 comments
Labels
安卓 安卓

Comments

@soapgu
Copy link
Owner

soapgu commented May 24, 2023

  • 关于“返回”的前世今生

其实最早接触到返回到概念是浏览器的
图片
这小小的“后退”箭头按钮就是。
和他卧龙凤雏凑一堆的是“前进”
其实可以理解为对浏览记录的快捷跳转
图片
如图所示
对于安卓的世界来说,返回堆栈就有Task 和 Activity的“宏观级别”
图片
以及Framgment的“微观级别”
图片
但是从用户体验角度上来讲,后退是“一致”的
另外现在安卓的发展也在往单Activity,多Framgment方向发展。
这个和Web前端的单页面方向很接近,殊途同归吧

  • 关于干预“后退”按键的需求

虽然堆栈后退是常规操作。但是仍然有需要干预的可能和需要

  1. 操作体验的需要
    比如第一个启动页面,如果按退出,整个app就“关”掉了。很多时候只是用户手滑(shou jian)了。
    很多app,会提供一个提示,比如再按一次退出才真正退出。

  2. 业务方面考虑强需求
    如果有的页面从业务角度讲就是必须不能后退的,必须完成后才能返回,比如登陆页,我首页又不允许匿名登陆的情况下。

  3. 确实需要自定义后退
    比如当前的页面是一个内嵌了一个web浏览器,我点击后退。
    “真实”的需求是我web后退一个页面,但是我把web页面直接给干掉了估计用户会很愤怒

  • 关于返回键干预的具体方案

  1. 经典方案——使用ComponentActivity的onBackPressed
    这应该是安卓的“经典”回调了
  • 优点
    简单粗暴,直接在Activity里面Override就行了,几乎没有其他啥操作

  • 缺点
    局限在Activity的逻辑中,如果需要Framgment参与就必须写额外的接口和通信,对于飞速发展的Framgment来说显然落后了。

  • 例子

@Override
    public void onBackPressed() {
        Logger.i("---onBackPressed---");
        super.onBackPressed();
    }

注意:这里super.onBackPressed();一定要调用,除非你想把后退功能给“裁剪”掉

2023-05-23 22:24:21.889 21439-21439 meeting-pad             pid-21439                            I  ┌────────────────────────────────────────────────────────────────────────────────────────────────────────────────
2023-05-23 22:24:21.890 21439-21439 meeting-pad             pid-21439                            I  │ Thread: main
2023-05-23 22:24:21.890 21439-21439 meeting-pad             pid-21439                            I  ├┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
2023-05-23 22:24:21.890 21439-21439 meeting-pad             pid-21439                            I  │ Activity.onKeyUp  (Activity.java:3784)
2023-05-23 22:24:21.890 21439-21439 meeting-pad             pid-21439                            I  │    MainActivity.onBackPressed  (MainActivity.java:70)
2023-05-23 22:24:21.890 21439-21439 meeting-pad             pid-21439                            I  ├┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄┄
2023-05-23 22:24:21.890 21439-21439 meeting-pad             pid-21439                            I  │ ---onBackPressed---
2023-05-23 22:24:21.890 21439-21439 meeting-pad             pid-21439                            I  └────────────────────────────────────────────────────────────────────────────────────────────────────────────────

一切正常

再看看api文档

图片

官方的态度也是不建议使用了

  1. 推荐方案—— OnBackPressedDispatcher模式自定义后退
  • 优缺点先不表直接上方案

可以通过getOnBackPressedDispatcher的api来获取OnBackPressedDispatcher,
如果我在Fragment这个层级怎么办,可以使用requireActivity().getOnBackPressedDispatcher()

使用addCallback()方法来添加回调

推荐需要owner参数,可以有效控制“观察者”的有效性以及内存泄露问题,和LiveData的方式是一模一样的

OnBackPressedCallback的接口很简单,实现handleOnBackPressed方法即可,另外记得设置enabled,一般使用true。

  • 有趣的Callback职责链模式
    简而言之,就是你可以注册多个OnBackPressedCallback到OnBackPressedDispatcher。
    但是这个Dispatcher只会执行一个,而且是“后发先至”的Stack模型。
    职责链有点点太“高级”了,换一个类比,和军队指挥系统有点像
    连长牺牲了,副连长上,副连长牺牲了,排长上~
    全牺牲,我就是连长了

  • 代码测试

this.getOnBackPressedDispatcher().addCallback(this, new OnBackPressedCallback(true) {
            @Override
            public void handleOnBackPressed() {
                Logger.i("handleOnBackPressed11111");
            }
        });

this.getOnBackPressedDispatcher().addCallback(this, new OnBackPressedCallback(true) {
            @Override
            public void handleOnBackPressed() {
                Logger.i("handleOnBackPressed22222");
            }
        });

注册两个自定义Callback

图片 很明显,后注册的“handleOnBackPressed22222”的日志打印了出来。 另外原来后退的功能“废”掉了

回过头一想也是正确的,你把后退的职责抢过来了,那么什么都是自定义了,退堆这种事情也是自己来。你自己不做就没人做了!

  • 系统默认实现的Callback

既然我们增加了OnBackPressedCallback,原来默认的回调就被替换掉了。
让我们看看如果不加那么系统是怎么帮我们实现的吧

图片

这里是更新状态,可以看到只有作为主导航控件+堆栈里面有值才会生效果,如果FragmentManager里面堆栈空了就不起效果了,交给外面的Activity来处理了,很合理

图片 图片

实现部分,可以看到退栈的逻辑
这里抛砖引玉下,感兴趣的可以直接看源码

  • 优缺点
    缺点:几乎没有
    优点:接口灵活,还有层级优先级,装卸方便

  • 总结
    两套方案。官方文档说明了尽量不要混用,产生不必要的bug。

  • 总结及相关链接

其实这次算是二刷自定义后退
第一次其实我没真正看懂~~

@soapgu soapgu added the 安卓 安卓 label May 24, 2023
@soapgu soapgu changed the title 关于安卓“返回”按键的干预处理 Navigation in Android (六) ——关于安卓“返回”按键的干预处理 May 27, 2023
soapgu added a commit that referenced this issue May 27, 2023
@soapgu soapgu changed the title Navigation in Android (六) ——关于安卓“返回”按键的干预处理 Navigation in Android (七) ——关于安卓“返回”按键的干预处理 Aug 10, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
安卓 安卓
Projects
None yet
Development

No branches or pull requests

1 participant