Android中的Launch Mode详情


Posted in Java/Android onJune 05, 2022

一. 多任务和Task、启动模式

Android 手机在早期,下方通常会内置三个实体的触摸按键,分别是:桌面菜单返回。大概在Android 5.0 之后,Android开始流行系统内置虚拟按键,其中的菜单被替换成了多任务,一旦我们按下多任务按键,一个个的任务快照就以流的形式展现在屏幕之上。

随着Google对多任务更好地支持,越来越多的厂商将正面的实体按键,替换成了虚拟按键,也将菜单按键删除,替换成了多任务视图按钮,显然多任务在之后的Android版本中是非常重要的一个概念。

甚至由于虚拟按键的出现,一些特定型号的手机在下方可能会形成奇怪的五层下巴:

Android中的Launch Mode详情

我们将这一个个的任务叫做Task,多任务视图中,会显示各个Task顶层Activity的快照(之所以是快照,是因为Activity不一定是存活的,有可能它只是一张图片。)Task中,以回退栈的形式堆叠Activity。

Android中的Launch Mode详情

每个Task,对应一个名称,如果我们不去设置,那么就是我们Application的应用包名,默认情况下,start一个新的Activity就会被装入该Task当中,然后在这个Task中进行堆叠,新打开的Activity在上方,用户可以通过按下返回键回退到上一个Activity当中。

每个Activity,都有一个TaskAffinity属性,标志了它想去的Task是哪一个,如果你不填写,那么通常是默认的目标栈,但是要注意的是,TaskAffinity需要和LaunchMode搭配在一起使用

  • 如果不设置LaunchMode(即采用默认的Standard),那么你从ActivityA中启动一个设置了TaskAffinity的ActivityB,你会发现它不生效,它仍然在当前包名的ActivityA的对应的任务栈当中。
  • 如果你将TaskAffinity配置和LaunchMode = SingleTask一起使用,在你打开了ActivityB时,你按下多任务按钮,你会发现一个App出现了两个Task,即两个回退栈,这两个回退栈的分别对应ActivityA和ActivityB的对应的Task的回退栈:

Android中的Launch Mode详情

它们拥有相同的项目名称,因为只是名为router的app模块下的两个不同TaskAffinity的Activity,不过这个TaskAffinity并不显示在多任务视图当中。

二. 四种启动模式详解

所以,四种启动模式,对应的具体的启动情况如下:

1. Standard

在当前的Task的回退栈中,启动一个Activity实例,放在栈顶。

在这种模式下,使用TaskAffinity是无效的。即使填写了TaskAffinity,最终也会被创建在执行启动命令的Activity对应的Task栈的栈顶,而不是TaskAffinity对应的Task的栈顶。

2. SingleTask

在TaskAffinity指定的退回栈中尝试启动一个Activity实例:

  • 如果指定的回退栈中,含有该Activity相同类型的实例,那么就回调onNewIntent()方法,告知原先已经存在的实例X,然后回调onResume()方法,并将原先实例上方的实例全部移除出回退栈,这样一来,原先已经存在的实例X就会出现在栈顶。
  • 如果指定的回退栈中,不包含Activity相同类型的实例,那么就在栈顶创建,走正常的生命周期回调:onCreate()->onStart()->onResume()
  • 如果TaskAffinity对应的Task都不存在的情况下,会先去创建目标Task,再走创建Activity实例的流程,最后压入栈顶。
  • 如果没有指定TaskAffinity,那么就指定为当前调用启动操作的Activity的Task,将Activity压入该Task回退栈的栈顶。

注意:

创建在当前Task和其它Task的Activity跳转动画是不相同的。

SingleTask + TaskAffinity实际上也是一种全局的单例,因为它的创建结果最终都会将Activity创建在指定的TaskAffinity的Task下。即使不填写TaskAffinity,也会只在当前Activity对应的Task下创建或者复用唯一的一个实例。

3. SingleTop

在TaskAffinity指定的退回栈中尝试在启动一个Activity实例。和SingleTask类似,但是复用条件稍有不同。仅在目标Task的回退栈的顶部含有相同类型的Activity时,触发复用,回调onNewIntent和onResume。

4.SingleInstance

在TaskAffinity指定的退回栈中,创建一个Activity实例,但是,一个Task中仅允许一个Activity存在,如果两个Activity对应的TaskAffinity是相同的,例如从A中以SingleInstance启动B,B中以SingleInstance启动C,BC的TaskAffinity是相同的。

这种情况下,如果我们在C中,呼出多任务视图菜单,我们会发现,此时栈中只有C和A对应的Task,B对应的Task并不存在,你可能会认为B和C在同一个栈中,B在C之下。但是如果你以另外一种方式: 在C中,呼出多任务菜单,回到C后,然后点击返回,你认为应该回到B,但是你会发现,直接退到桌面了。 或者你在C中,直接按回车,你会发现从C->B和从B->A的跳转过场动画,都是Task间的转场动画,而不是Task内部的跳转动画。

这涉及到另外我们就要讲另外一个问题,Task间跳转时,Task间的堆叠问题(Task叠在另一个Task上面),而打开多任务列表或者按下Home键会导致堆叠被破坏。

B和C即使有同一个TaskAffinity命名,并且根据我们说的一个Task中仅允许一个Activity存在,C打开时,B应该被关闭,从多任务上来看,似乎也是这样的,因为只有C和A的Task在多任务视图中,但是我们确实又可以从C的Task返回到B的Task,在任务栈中我们又看不到B的身影,这可以下一个结论:多任务视图中的不可见的Task不一定不存在,如果发生SingleInstance + TaskAffinity冲突的情况,例如:

Activity A和Activity B都是SingleInstance的,并且又都是设置了TaskAffinity为 com.example.newTask。如果既要保证AB都在同一个Task中,又要保证该Task只能有一个>Activity,那么就会导致冲突。

经过测试,当冲突发生时,Android会为两个Activity都创建一个Task,但是同一时间,只有一个能>在多任务视图中被看见,但是如果顺序启动:A、B,是可以在B中回退到A中的。并且回退的动画是Task 间切换的动画。

那么Task中可见的Activity一定在运行吗?答案也是否定的,如果我们在ActivityA按下返回键,退回到桌面,我们此时打开多任务,我们会发现,ActivityA的Task的快照仍然保留在多任务视图之上,但是它此时已经“死了”,我们点击它,实际上是创建了一个新的ActivityA。所以,多任务视图中中的不可见的Task不一定不存在,多任务视图中可见的Activity也不一定就是Running状态的

三. Task间堆叠与Task Reparenting

1. Task间堆叠

考虑如下的两个任务栈间切换场景:

Android中的Launch Mode详情

 我们需要从ActivityC中启动Activity E,此时ActivityE的启动模式被设置成了:SingleTask,TaskAffinity是Task2。ActivityE被启动之后,在屏幕上显示出来了。

基于这个状态,接下来有几个问题:

  • 问题1:如果此时按多次返回键,会发生什么?
  • 问题2:如果此时先按Home回到桌面,再从多任务列表打开Task1,显示的是ActivityC还是ActivityE?
  • 问题3:如果此时先按Home回到桌面,再从多任务列表打开Task2,显示的是ActivityE,如果此时不不断地按返回键,会发什么?
  • 问题4:如果此时先打开多任务列表,再按返回,返回Task2,此时显示的是ActivityE。如果此时不不断地按返回键,会发什么?

对于问题1,答案是:Activity E/D/C/B/A同时出栈,A出栈之后回到桌面。 对于问题2,答案是:显示的是ActivityC 对于问题3、4,答案是:Activity E/D出栈,D出栈之后,回到桌面,而不是Activity C。

问题1的原因是因为,Task2的任务被打开之后,整个Task2成为了优先显示的Task,被堆在屏幕之上,一旦Task2的Activity退完了,Task1可以无缝地衔接上:

Android中的Launch Mode详情

 就好像Task2被堆在了Task1之上,这样的堆叠,保证了用户交互的连贯性。

这样的Task间的堆叠跳转特性适用于用户跳转某个页面之后,按返回键不想马上跳回原Activity的一种情况。

但是,这种堆叠仅限于Task跳转刚刚发生的情况,一旦用户进行了:

  • Home键返回桌面
  • 切出多任务视图

这类的操作,将顶端的Task变为后台Task之后,那么这种「堆叠」就会立刻失效,并且不再恢复,所以在问题3、4中,最后返回的是桌面,而不是Task1的ActivityC,这种堆叠被破坏了,Task1和Task2又重新回到平级的状态了。

但是在最新的API32版本中,切到Android 自带的多任务视图并不会导致Task2重新被移动到Task1平级的位置,这意味着,你从多任务切回来之后,在ActivityE按下返回,仍然会回退到到Task1的ActivityC之上。

如果有一些场景,你希望打开ActivityE之后不退到D,直接退到C那应该怎么做呢?其实不设置SingleTask就好了,默认的就是这种情况,比如外卖平台支付订单之后,付款软件的的付款结果页的一个实例就被留在了外卖软件的Task中。

2. Task Reparenting/Task重定父级

通常来说,一个Activity被装进一个Task的栈之后,就不会去移动了,但是我们可以借助Task Reparenting来做父级的重新指定。

例如上述的例子中,我们队ActivityE设置:allowTaskReparenting。我们从Task1中的ActivityC启动ActivityE时,ActivityE会启动在Task1中。

一旦我们切到桌面,再重新从桌面图标重新打开Task1时,我们会发现ActivityE从Task1中消失了,而从桌面打开Task2对应的App图标时,会发现,ActivityE重新回到了自己的Task2中,例如我们按照如下定义:

<activity
            android:name=".ActivityE"
            android:allowTaskReparenting="true"
            android:exported="false"
            android:taskAffinity="com.example.anotherTask" />

如果我们在默认的,和包名相同的com.rEd.router下创建该MainActivityE,那么此时的MainActivity会在当前的com.rEd.router下创建该MainActivityE实例,然后我退到桌面,再重新在桌面的App图标打开App,我会发现,启动的App的当前的Activity变成了ActivityC,而不是ActivityE:

Android中的Launch Mode详情

而且,多任务视图中的Task2中的快照是空白的,我们点开Task2,发现ActivityE回到了它taskAffinity指定的Task中:

Android中的Launch Mode详情

另外,如果同时设置了allowTaskReparenting=true和LaunchMode,那么LaunchMode会优先生效,Activity会直接创建在其他的Task中。

到此这篇关于Android中的Launch Mode详情的文章就介绍到这了,更多相关Android Launch Mode内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!


Tags in this post...

Java/Android 相关文章推荐
Spring Bean的实例化之属性注入源码剖析过程
Jun 13 Java/Android
解析Java中的static关键字
Jun 14 Java/Android
详解Spring Boot使用系统参数表提升系统的灵活性
Jun 30 Java/Android
Java并发编程必备之Future机制
Jun 30 Java/Android
JavaGUI模仿QQ聊天功能完整版
Jul 04 Java/Android
JavaWeb 入门篇(3)ServletContext 详解 具体应用
Jul 16 Java/Android
mapstruct的用法之qualifiedByName示例详解
Apr 06 Java/Android
详解Flutter和Dart取消Future的三种方法
Apr 07 Java/Android
Java 异步任务计算FutureTask
Apr 28 Java/Android
Java8 CompletableFuture 异步回调
Apr 28 Java/Android
详解Spring Security如何在权限中使用通配符
Jun 28 Java/Android
Spring Boot实现文件上传下载
Aug 14 Java/Android
Spring中的@Transactional的工作原理
Jun 05 #Java/Android
Qt数据库应用之实现图片转pdf
Ubuntu18.04下QT开发Android无法连接设备问题解决实现
Jun 01 #Java/Android
openGauss数据库JDBC环境连接配置的详细过程(Eclipse)
Jun 01 #Java/Android
Java中生成微信小程序太阳码的实现方案
Jun 01 #Java/Android
Java处理延时任务的常用几种解决方案
Java实现添加条码或二维码到Word文档
Jun 01 #Java/Android
You might like
把PHP安装为Apache DSO
2006/10/09 PHP
玩转虚拟域名◎+ .
2006/10/09 PHP
javascript操作cookie_获取与修改代码
2009/05/21 Javascript
Extjs学习笔记之九 数据模型(上)
2010/01/11 Javascript
js冒泡、捕获事件及阻止冒泡方法详细总结
2014/05/08 Javascript
jquery实现类似淘宝星星评分功能实例
2014/09/12 Javascript
重写document.write实现无阻塞加载js广告(补充)
2014/12/12 Javascript
Nodejs学习笔记之Stream模块
2015/01/13 NodeJs
被遗忘的javascript的slice() 方法
2015/04/20 Javascript
TypeError document.getElementById(...) is null错误原因
2015/05/18 Javascript
js+HTML5实现视频截图的方法
2015/06/16 Javascript
jQuery实现带有洗牌效果的动画分页实例
2015/08/31 Javascript
基于jquery css3实现点击动画弹出表单源码特效
2015/08/31 Javascript
如何让一个json文件显示在表格里【实现代码】
2016/05/09 Javascript
node.js学习之交互式解释器REPL详解
2016/12/08 Javascript
easyui combogrid实现本地模糊搜索过滤多列
2017/05/13 Javascript
javascript获取指定区间范围随机数的方法
2017/09/08 Javascript
解决JQuery全选/反选第二次失效的问题
2017/10/11 jQuery
基于jQuery Ajax实现下拉框无刷新联动
2017/12/06 jQuery
layui使用数据表格实现购物车功能
2019/07/26 Javascript
微信小程序顶部导航栏可滑动并选中放大
2019/12/05 Javascript
[01:02:20]Mineski vs TNC 2019国际邀请赛小组赛 BO2 第二场 8.15
2019/08/16 DOTA
python thrift搭建服务端和客户端测试程序
2018/01/17 Python
几行Python代码爬取3000+上市公司的信息
2019/01/24 Python
通过python实现弹窗广告拦截过程详解
2019/07/10 Python
jupyter lab的目录调整及设置默认浏览器为chrome的方法
2020/04/10 Python
Jupyter加载文件的实现方法
2020/04/14 Python
python实现sm2和sm4国密(国家商用密码)算法的示例
2020/09/26 Python
高清屏中使用Canvas绘图出现模糊的问题及解决方法
2019/06/03 HTML / CSS
法律专业学生的自我评价
2014/02/07 职场文书
学生个人总结范文
2015/02/15 职场文书
财务工作个人总结
2015/02/27 职场文书
给学校的建议书400字
2015/09/14 职场文书
Win11怎么启动任务管理器?Win11启动任务管理器的几种方法
2021/11/23 数码科技
叶县这家生产军用电台的兵工厂,人称“四机部”,走出一上将
2022/02/18 无线电
使用Postman测试需要授权的接口问题
2022/06/21 Java/Android