Android 面试黑洞——当我按下 Home 键再切回来,会发生什么?

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip204888 (备注Android)
img

正文

*   standard
*   singleTop
*   singleTask
*   singleInstance
  • Intent.FLAG_ACTIVITY_***
    • FLAG_ACTIVITY_NEW_TASK
    • FLAG_ACTIVITY_SINGLE_TOP
    • FLAG_ACTIVITY_CLEAR_TOP
    • FLAG_ACTIVITY_MULTIPLE_TASK
    • FLAG_ACTIVITY_NEW_DOCUMENT
    • FLAG_ACTIVITY_REORDER_TO_FRONT
    • FLAG_ACTIVITY_PREVIOUS_IS_TOP
    • FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
    • FLAG_ACTIVITY_RETAIN_IN_RECENTS
    • FLAG_ACTIVITY_TASK_ON_HOME
  • 的 android:taskAffinity
  • 的 android:allowTaskReparenting
  • 的 android:clearTaskOnLaunch
  • Activity 的回退栈(Task)
  • Android 的最近任务列表(Recents / Overview)切换
  • 启动器(桌面)的 App 图标点击
  • ……

你把这个大话题弄明白了,才可以指哪打哪,随心所欲。面试官有时候问一些比较刁钻的 launchMode 的问题,其实也不是为了刁难你,这都是对实际开发有用的,只是它比较难掌握而已。

所以今天,我就把 launchMode 以及和它相关的这一大套东西,给大家讲清楚。安全带系好了。

视频先行

要看视频的可以直接去 哔哩哔哩 或者 YouTube 观看。

强烈建议扫码看视频版本!

强烈建议扫码看视频版本!

强烈建议扫码看视频版本!

本期视频用了大量的 3D 动画来配合讲解,比如这样: 微信图片_20201015144036.gif 所以有条件的话强烈建议观看视频版本,因为本期的文字版可能会比较不适合阅读。

下面的文字是本期视频的脚本,为了方便阅读才修改成了文章的格式。所以如果你点开视频,下面的文字就不用看了。

Task 和回退栈

大家好,我是扔物线朱凯。

先问个问题:当我们在 Android 手机里点了最近任务的方块键,我们看到的这是一个个的……什么?

一个个…… Activity?一个个…… App?我们看到的是一个个……Task,任务。

当我们的 App 图标在桌面上被点击的时候,App 的默认 Activity——也就是那个配置了 MAIN + LAUNCHER 的 intent-filter 的 Activity——会被启动,并且这个 Activity 会被放进系统刚创建的一个 Task 里。我们通过最近任务键可以在多个 App 之间进行切换,但其实更精确地说,我们是在多个 Task 之间切换。

每个 Task 都有一个自己的回退栈,它按顺序记录了用户打开的每个 Activity,这样就可以在用户按返回键的时候,按照倒序来依次关闭这些 Activity。当回退栈里最后一个 Activity 被关闭,这个 Task 的生命也就结束了。

但它并不会在最近任务列表里消失。系统依然会保留这个 Task 的一个残影给用户,目的是让用户可以方便地「切回去」;只是这种时候的所谓「切回去」,其实是对 App 的重新启动,因为原先的那个 Task 已经不存在了。

所以,在最近任务里看见的 Task,未必是还活着的。

singleTask

Activity 是一个可以跨进程、跨应用的组件。当你在 A App 里打开 B App 的 Activity 的时候,这个 Activity 会直接被放进 A 的 Task 里,而对于 B 的 Task,是没有任何影响的。

为什么?为什么这么设计?

首先我们想一想:我们为什么要打开别的 App 的 Activity?因为它提供了一个通用的功能,对吧?比如通讯录 App 可能会提供一个添加联系人的 Activity 供其他 App 使用。那么这些通用的功能,它的逻辑是和谁相关的?比如我从短信 App 里点击一个电话号码,选择「新建联系人」,然后通讯录 App 提供的添加联系人 Activity 就会被打开,对吧?这个 Activity 它的逻辑是和哪个 App 相关的?和短信相关吗?相关的,因为它是从短信跳过来的嘛,它们是在一整个逻辑链条上的。换句话说,如果我现在按了返回键,我会回到刚才的短信界面。是吧?那它和通讯录相关吗?是不相关的。所谓不相关,就是在这个时候用户如果按下最近任务的方块键,他不应该看到通讯录的 Task;而如果他现在回到桌面,点击通讯录的图标,他看到的也不应该是这个添加联系人的页面,而应该是一个联系人列表,因为用户的这个操作大概率是要查看通讯录;相反,在这个时候他再切回短信 App,他应该回到刚才的添加联系人页面,继续编辑联系人信息。所以对于「添加联系人」这个页面来说,它是和打开它的那个 App 有相关性,而不是和提供它的 App,对吧?更确切地说,也不是和打开它的 App 相关,而是和打开它的 Task 相关。是这回事吧?而这个逻辑,实际上也是 Android 默认的规则。当你在不同的 Task 里打开相同的 Activity 的时候,这个 Activity 会被创建出不同的实例,分别放在每一个 Task 里,互不干扰。这是符合产品逻辑,也是符合用户心理的。

但是!这只是默认的规则。有的时候我们会需要不同的产品逻辑。比如我在短信里点击的不是电话号码,而是一个邮箱地址,那么我的邮箱 App 提供的编写邮件的 Activity 就会被打开,对吧?这个时候,这个编写邮件的 Activity,它的逻辑是和哪个 App 相关的?首先,依然是和短信 App 相关的,对吧?原因跟刚才一样,它是从短信打开的。那么它和邮箱 App 相关吗?也是相关的。因为按照用户使用邮件的习惯,如果现在按下最近任务键,用户会期望看到邮箱 App 的 Task 出现在短信 Task 的旁边,并且当它点击这个 Task,或者当它切回桌面点击邮箱 App 的图标,他都会期望回到写邮件的界面继续写。编写邮件和添加联系人这两件事并没有本质的不同,只是用户不同的心理预期决定了我们要有不同的产品逻辑。所以如果你们也做通讯录或者邮箱,而且产品逻辑和我说的不一样,没关系,这是产品经理负责的事,我在说的是如果你有怎样的产品逻辑,你应该怎么写。

那么如果我要做这种逻辑的邮箱,我应该怎么办呢?很简单,只要在 AndroidManifest.xml 里把这个编写邮件的 的 launchMode 设置为 singleTask 就行了。

singleTask 可以让 Activity 被别的 App 启动的时候不会进入启动它的 Task 里,而是会在属于它自己的 Task 里创建,放在自己的栈顶,然后把这整个 Task 一起拿过来压在启动它的 Task 上面。这种逻辑可以保证,不管是从哪个 App 启动,被标记为 singleTask 的 Activity 总是会被放在自己的 Task 里。如果你仔细留意也会发现,这种方式打开的 Activity 的入场动画是应用间切换的动画,而不是普通的 Activity 入场动画。这种不一致并不是 Android 不拘小节不修边幅,相反,这是在刻意地提醒用户:你在进行跨任务操作。这时候用户如果点返回键,界面会显示你的 App 里的上一个 Activity,而不是直接返回到之前的 App。直到用户反复按返回键,把这个 App 所有的 Activity 全都关闭了,上面的 Task 消失,下面的 Task 才会出来,也就是对于我们的例子来说,短信 App 才会露出来,而且这次,又变成了应用间切换的动画——确切地说,是 Task 间切换的动画。

也就是说,不止 Activity 在 Task 内部可以叠成栈,不同的 Task 之间也可以叠起来。不过有一点:Task 的叠加,只适用于前台 Task,前台叠加的多个 Task 在进入后台的第一时间就会被拆开。前台 Task 进入后台最常见的场景有两种:按 Home 键回到桌面,以及按最近任务键查看最近任务。需要注意的是:前台 Task 是在显示最近任务的时候就已经进入了后台,而不是在你切换到其他应用之后。所以如果用户从短信进入邮箱以后没有按直接返回键,而是先查看一下最近任务再马上按返回键切回去,这个时候虽然表面上看着没变,但实际上前台 Task 已经只剩下了一个。现在如果用户再连续按返回键关掉邮件 App 的 Task,他就不会回到短信了,而是直接回到桌面。

我觉得这个其实有点反用户直觉的。我只是切出去再切回来,怎么就变了?但是,Android 就是这么工作的。

allowTaskReparenting

除了 singleTask,对于新建邮件这种场景,还有一种解决方案是使用一个叫做 allowTaskReparenting 的属性。Activity 默认情况下只会归属于一个 Task,不会在多个 Task 之间跳来跳去,但你可以通过设置来改变这个逻辑。如果你不是用 singleTask 来设置编写邮件的 Activity,而是把它的 allowTaskReparenting 属性设置为 true,那么当用户从短信里打开这个 Activity 的时候,它虽然依然会进入短信 App 的 Task 里,但当稍后用户再从桌面点开邮件 App 的时候,原先那个放在短信 Task 里的 Activity 会被挪过来,放进邮件 App 的 Task 里,在回退栈的顶端被显示出来;而这时候你再切回短信,也会发现那个 Activity 已经不见了。这也就是所谓的「Task Reparenting」。你打开我的时候,我在你的 Task 里;稍后我又可以回到我原本所属的 Task 来。

这跟 singleTask 比起来,因为 Activity 刚被打开的时候并没有发生 Task 切换,所以也没有 Task 切换的夸张的入场动画,对于用户是无感知的;而且因为只有一个 Task,用户切到后台再切回来的时候也不会像 singleTask 那样被切断自己的回退路径。

好用吧?

总结

算法知识点繁多,企业考察的题目千变万化,面对越来越近的“金九银十”,我给大家准备好了一套比较完善的学习方法,希望能帮助大家在有限的时间里尽可能系统快速的恶补算法,通过高效的学习来提高大家面试中算法模块的通过率。

这一套学习资料既有文字档也有视频,里面不仅仅有关键知识点的整理,还有案例的算法相关部分的讲解,可以帮助大家更好更全面的进行学习,二者搭配起来学习效果会更好。

部分资料展示:




有了这套学习资料,坚持刷题一周,你就会发现自己的算法知识体系有明显的完善,离大厂Offer的距离更加近。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注Android)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

链图片转存中…(img-XxAkqZLg-1713226782188)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 22
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值