我们马上进入主题,首先来理解一个概念:task。翻译过来就是任务,其实有时不要翻译过来理解更好。因为有时翻译过来后,在我们生活的语境中很难找到对应概念的东西,或者找到的很不贴切。这也是为什么直接翻译过来的这些泊来品,在中文中理解起来很不顺的原因。但是我们自己创造的技术,用中文描述会比用英文更好。其实都是同一个道理。所以我对这些泊来品的处理就是不要翻译,就权当它是一个很纯粹的符号。
task就是一个activity的集合,即一个task中有很多个activity。这些activity会被排列在一个回退栈中,排列的顺序就是它们打开的顺序,如依次打开activityA,activityB,activityC,activityD,那么从栈顶到栈底依次是activityD,activityC,activityB,activityA。(栈的数据结构可以到网上找资料学习一下)
当我们按回退键或用手势划动返回,那么栈顶的activity就会首先被弹出栈并销毁。当回退栈中的所有activity都被弹出后,这个task就不存在了。
当我们点击某个应用的图标,那么这个应用的task就会来到前台。如果这个app没有task存在,即这个app之前还没有打开过,那么就会创建一个新的task,用于”装“app打开的activity,打开的第一个activity就会被放在回退栈最底,放在回退栈栈底的这个activity被叫作root activity。
声明了下面Intent filter的activity叫root launcher activity,就是app应用的入口,一打开它,就会创建一个task,这个activity就会被放到回退栈的栈底:
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
当我们按返回键或用手势返回时,回退栈会弹出栈中的activity,但在弹出root launcher activity这个栈底的activity时,不同的android系统版本的处理是不同:
- android 11 或更低的版本:直接弹出root launcher activity并销毁
- android 12 或更高的版本:系统会将root launcher activity和它的task放到后台去,不会结束它。这样下一次,我们就可以快速启动app,这就是所谓的热启动,意味着root launcher activity不再需要经历完整的启动过程(从onCreate到onResume等等),与之对应的概念就是冷启动。在我们编程时,可以使用onBackPressed()去处理activity的导航和结束,而不是直接使用finish()方法。
当我们点击了home键或启动了另一个task,那么当前task就会被进入后台,进入后台的task的所有activity的状态都是stopped,也就是不会任何变化了,对应的回退栈也不会有变化了,一切都静止了。进入后台task与正在前台的task的区别是前者失去焦点,后者获得了焦点。当我们回到home界面点击进入了后台的app的图标,进入后台的task会快速回到前台,它的回退栈的栈顶activity就会恢复,而之前还在前台的app的task则会进入后台。
在回退栈中的activity们的顺序是不会发生变化的,也就是它们不会被重新排列。
从android 7.0(API level 24)开始支持多窗口,android系统会单独管理每一个窗口的task。换句话说,每一个window都有一个task。
一般来说,我们不需要过多操心这些task的管理和回退栈的情况。像我们一般的startActivity的调用创建的activity都会加入到当前的task中,并入栈到回退栈的栈顶。这里需要注意,如果回退栈里已有一个activityA,再startActivity一个activityA,那么回退栈里就会有两个activityA的实例。那么当我们有下面的需求时,我们就需要知道task和回退栈的处理了:
- 当你想重新创建一个新的task,让将要打开的activity加入其中,而不是加入到当前的task。
- 当你想重新启动一个activity时,只想把它在回退栈已有的实例展现出来,而不是创建一个新的activity实例。
- 当你想退出app时,清掉task中的所有activity,除了root launcher activity外
我们可以通过在AndroidManifest.xml中的activity标签处配置,或通过设置置intent的flag来实现。
我们先来介绍一些常见的在AndroidManifest.xml中的配置:
- android:launchMode ?可选的值用stardard,singleTop,singleTask,singleInstance,singleInstancePerTask。这些值有时和intent的一些flag联合着用。我们大概讲一些它的的意思,更多资料需要到网上去查。standard这个模式是最常用的,系统会创建新的activity实例加入到task中,并将intent传递给它。你可以想象,用这个模式startActivity多少次,你的task和回退栈里就有多少activity的实例。我们再来说singleTop这个模式,也是可以创建activity的多个实例,但是当将要实例化的activity已是回退栈栈顶的activity时,就直接使用栈顶的,并把intent传给它的onNewIntent,不会再创建其他实例。但是假如将要创建的activity不在栈顶或都虽然在栈顶但不是同一个目标task,那么activity新实例就会被创建。如果singleTop结合Intent._FLAG_ACTIVITY_CLEAR_TOP一起使用,那么就会达到这样一种效果:比如回退栈的底到顶依次是A D E F G,现在D的模式是singleTop,启动它时的intent的flage指定了_Intent._FLAG_ACTIVITY_CLEAR_TOP,那么执行后的结果是A D。singleIntance就是task中只有一个实例。它和singleTask很像。singleTask是task只有一个模式标为此的activity的实例,但是它允许有其他activity实例加入它的所在的task中。而singleIntance自己独用一个task,task里只有它自己,当它startActivity其他activity时,其他的activity实例会被分配到其他的task,如root launcher activity所属的task。_singleInstancePerTask是综合了singleTask和singleInstance两种情况:(1)singleTask的实例在task中只有一个,并允许其他activity实例加入自己所在task;(2)singleInstance独享一个task且只一个activity实例。综合起来就是singleInstancePerTask每个实例都有自己的task,并且在task中只有一个自己这样的实例,且允许其他activity实例加入自己的task中。singleInstancePerTask的实例将一直是所在task的root,它要和Intent的flag的Intent._FLAG_ACTIVITY_MULTIPLE_TASK 和_Intent.FLAG_ACTIVITY_NEW_DOCUMENT一起使用。提醒:在理解FLAG_ACTIVITY_NEW_TASK时要结合affinity来理解。如用带这个flag去打开同一个app的activity它会加入到同一个task,除非你指定不同的taskAffinity,如果是外部打开我们app的activity,那么它就是在外部app的task中。
- android:clearTaskOnLaunch默认值是false,这个属性一般用在root launcher activity,即启动activity,设置为true的话,会有这样一个效果,比如说当你打开了app,接着又点了两三个app上的页面,接着就按home键退出到屏幕,下一次再打app,就会直接回到app的首页,如果没有设置这一个,那么打开app时,还是停留在上一次的打开的页面处。
- android:alwaysRetainTaskState=“true”,这个值也只对task的root activity有效。打开应用它会回到最后一次状态。如web浏览器,每次打开它,都可以回退上次用户离开时的页面,这个是不是很熟悉。
- android:finishOnTaskLaunch="true"会在task回到前台或启动时结束activity实例。比如说你希望你的app每次打开或点了home键再打开时都是首页,那么只需要在那些需要关闭的activity加上这个属性即可。
|