Activity启动模式
Activity的启动模式是android系统管理activity的重要设置项和方式。对应AndroidManifest中activity节点下的launchMode属性。
四大启动模式
standard:标准模式,默认
standard是默认的启动模式,即标准模式,每启动一个Activity,都会创建一个新的实例
singleTop:Task栈顶复用模式
singleTop:当要启动的目标Activity已经位于栈顶时,不会创建新的实例,会复用栈顶的Activity,并且其onNewIntent()方法会被调用,如果目标Activity不在栈顶,则跟standard一样创建新的实例。
singleTask:Task栈内复用模式
这种模式启动的Activity只会存在相应的Activity的taskAffinit任务栈中,同一时刻系统中只会存在一个实例,已存在的实例被再次启动时,会重新唤起该实例,并清理当前Task任务栈该实例之上的所有Activity,同时回调onNewIntent()方法。
singleInstance:全局单例模式
这种模式启动的Activity独自占用一个Task任务栈,同一时刻系统中只会存在一个实例,已存在的实例被再次启动时,只会唤起原实例,并回调onNewIntent()方法。
需要说明的是:上面的场景仅仅适用于Activity启动Activity,并且采用的都是默认Intent,没有额外添加任何Flag,否则表现就可能跟上面的完全不一致,尤其要注意的是FLAG_ACTIVITY_NEW_TASK的使用,后面从源码中看,依靠FLAG_ACTIVITY_NEW_TASK其实可以分为两派。
有关启动模式的那些事
面试的时候,面试官经常同你随便侃侃Activity的启动模式,但Activity启动牵扯的知识点其实很多,并非能单单用四个启动模式就能概括的,默认的启动模式的表现会随着Intent Flag的设置而改变,因此侃Activity启动模式大多走流程装逼,最多结合项目遇到的问题,随便刁难一下面试者,并不太容易把控,也许最后,面试官跟面试者的答案都是错了,比如在Service中必须通过设置FLAG_ACTIVITY_NEW_TASK才能启动Activity,这个时候启动Activit会有什么样的表现呢?就这一个问题,答案就要分好几个场景:
Activity的taskAffinity属性的Task栈是否存在
如果存在,要看Activity是否存已经存在于该Task
如果已经存在于该taskAffinity的Task,要看其是不是其rootActivity
如果是其rootActivity,还要看启动该Activity的Intent是否跟当前intent相等
不同场景,所表现的行为都会有所不同,再比如singleInstance属性,如果设置了,大家都知道只有一个实例,将来再启动会复用,但是如果使用Intent.FLAG_ACTIVITY_CLEAR_TASK来启动,仍然会重建,并非完全遵守singleInstance的说明,还有不同Flag在叠加使用时候也会有不同的表现,单一而论Activity启动模式其实是很难的。
Intent.FLAG_ACTIVITY_NEW_TASK分析
从源码来看,Intent.FLAG_ACTIVITY_NEW_TASK是启动模式中最关键的一个Flag,依据该Flag启动模式可以分成两类,设置了该属性的与未设置该属性的,对于非Activity启动的Activity(比如Service或者通知中启动的Activity)需要显示的设置Intent.FLAG_ACTIVITY_NEW_TASK,而singleTask及singleInstance在AMS中被预处理后,隐形的设置了Intent.FLAG_ACTIVITY_NEW_TASK,而启动模式是standard及singletTop的Activity不会被设置Intent.FLAG_ACTIVITY_NEW_TASK,除非通过显示的intent setFlag进行设置。
FLAG_ACTIVITY_NEW_TASK这个属性更多的关注点是在Task,可以认为没有设置FLAG_ACTIVITY_NEW_TASK的情况下,taskAffinity可以不考虑,大多数情况下,需要将Activity引入到自己taskAffinity的Task中,Intent.FLAG_ACTIVITY_NEW_TASK的初衷是在Activity目标taskAffinity的Task中启动,非Activity启动Activity都必须添加Intent.FLAG_ACTIVITY_NEW_TASK才行,以Service启动的Activity为例:
1 | Intent intent = new Intent(BackGroundService.this, A.class); |
这种情况很有意思,如果目标Activity实例或者Task不存在,则一定会新建Activity,并将目标Task移动到前台,但是如果Activity存在,却并不一定复用,也不一定可见。这里假定A是standard的Activity,如果已经有一个A实例,并且所在的堆栈的taskAffinity跟A的taskAffinity一致,这个时候要看这个task的根Activity是不是A,如果是A,还要看A的intent是不是跟当前的启动的intent相等,如果都满足,只要将task可见即可。
否则,就需要新建A,并根据A的task栈的存在情况而选择直接入栈还是新建栈。但是,如果Intent想要的启动的Activity的目标堆栈存在,那就将整个堆栈往前迁移,如果位于顶部的Task栈正好是目标Activity的Task栈,那就不做任何处理,连onNewIntent都不会回调,怎么判断目标的Activity的Task栈同找到的栈一致呢?如果找不到目标Task自然会启动Task,如果目标task栈根Activit的intent同新将要启动的Activit相同,就不启动新Activity,否则启动Activity。
Intent.FLAG_ACTIVITY_CLEAR_TASK:必须配合FLAG_ACTIVITY_NEW_TASK使用
If set in an Intent passed to Context.startActivity(), this flag will cause any existing task that would be associated with the activity to be cleared before the activity is started. That is, the activity becomes the new root of an otherwise empty task, and any old activities are finished. This can only be used in conjunction with FLAG_ACTIVITY_NEW_TASK.
这个属性必须同FLAG_ACTIVITY_NEW_TASK配合使用,如果设置了FLAG_ACTIVITY_NEW_TASK|Intent.FLAG_ACTIVITY_CLEAR_TASK,如果目标task已经存在,将清空已存在的目标Task,否则,新建一个Task栈,之后,新建一个Activity作为根Activity。Intent.FLAG_ACTIVITY_CLEAR_TASK的优先级最高,基本可以无视所有的配置,包括启动模式及Intent Flag,哪怕是singleInstance也会被finish,并重建。
Intent.FLAG_ACTIVITY_CLEAR_TOP
如果没有使用FLAG_ACTIVITY_NEW_TASK,目标是当前Task栈,根据不同的组合会产生不同的效果,如果单独使用Intent.FLAG_ACTIVITY_CLEAR_TOP,并且没有设置特殊的launchmode,那么,Google官方的示例是:如果ABCD Task中的D采用Intent.FLAG_ACTIVITY_CLEAR_TOP唤起B,这个时候首先会将CD出栈,但是至于B是否会重建,要视情况而定,如果没有设置FLAG_ACTIVITY_SINGLE_TOP,则会将B finish掉,之后创建新的入栈。如果同一个栈中原来有
如果没有则新建,不会去另一个栈中寻找。
如果同时设置了FLAG_ACTIVITY_SINGLE_TOP,在当前栈已有的情况下就不会重建,而是直接回调B的onNewIntent(),
官方解释如下:
For example, consider a task consisting of the activities: A, B, C, D. If D calls startActivity() with an Intent that resolves to the component of activity B, then C and D will be finished and B receive the given Intent, resulting in the stack now being: A, B。
The currently running instance of activity B in the above example will either receive the new intent you are starting here in its onNewIntent() method, or be itself finished and restarted with the new intent.
If it has declared its launch mode to be “multiple” (the default) and you have not set FLAG_ACTIVITY_SINGLE_TOP in the same intent, then it will be finished and re-created; for all other launch modes or if FLAG_ACTIVITY_SINGLE_TOP is set then this Intent will be delivered to the current instance’s onNewIntent().
如果同时使用了FLAG_ACTIVITY_NEW_TASK ,这个时候,目标是Activity自己所属的Task栈,如果在自己的Task中能找到一个Activity实例,则将其上面的及自身清理掉,之后重建。
如果同时在加上FLAG_ACTIVITY_SINGLE_TOP,会更特殊一些,如果topActivity不是目标Activity,就会去目标Task中去找,并唤起
Intent.FLAG_ACTIVITY_CLEAR_TOP| FLAG_ACTIVITY_NEW_TASK|singleTop.jpg
如果topActivity是目标Activity,就直接回调topActivity的onNewIntent,无论topActivity是不是在目标Task中
Intent.FLAG_ACTIVITY_SINGLE_TOP
Intent.FLAG_ACTIVITY_SINGLE_TOP多用来做辅助作用,跟launchmode中的singleTop作用一样,在Task栈顶有的话,就不新建,栈顶没有的话,就新建,这里的Task可能是目标栈,也可能是当前Task栈,配合FLAG_ACTIVITY_NEW_TASK及FLAG_ACTIVITY_CLEAR_TOP都会有很有意思的效果。
为什么非Activity启动Activity要强制规定使用参数FLAG_ACTIVITY_NEW_TASK
从源码上说,ContextImpl在前期做了检查,如果没添加Intent.FLAG_ACTIVITY_NEW_TASK就抛出异常,
1 |
|
为什么要这么呢?其实直观很好理解,如果不是在Activity中启动的,那就可以看做不是用户主动的行为,也就说这个界面可能出现在任何APP之上,如果不用Intent.FLAG_ACTIVITY_NEW_TASK将其限制在自己的Task中,那用户可能会认为该Activity是当前可见APP的页面,这是不合理的。举个例子:我们在听音乐,这个时候如果邮件Service突然要打开一个Activity,如果不用Intent.FLAG_ACTIVITY_NEW_TASK做限制,那用户可能认为这个Activity是属于音乐APP的,因为用户点击返回的时候,可能会回到音乐,而不是邮件(如果邮件之前就有界面)。