接触了android四大组件之一 Activity,这么长时间,都没有认真总结一下。今天就总结一下吧。
总得来说活动的启动模式有一下四种:standard, singleTop, singleTask, singleInstance。可以在清单文件中,通过给activity标签指定 android:launchMode 属性来选择启动模式。
一:standard 是活动模式默认的启动模式,如果不显示的指定的话,所有活动都会自动使用这种启动模式。对于使用standard模式的活动,系统不会再乎这个活动是否已经在返回栈中存在,每次启动都会去创建该活动的一个新的实例。例如启动activity时为 :A B C D,返回时依次为:D C B A。
二:singleTop 当活动的启动模式指定为singleTop,在启动活动时如果发现该活动已经在返回栈的 栈顶 时,就不会再创建该活动的实例了。可能有人会问了,如果该活动不在返回栈的栈顶时怎么处理呢?很抱歉,不在返回栈的栈顶时,系统会去创建该活动的一个新的实例。那有没有只要是在返回栈中的活动,就不会再去创建该活动的实例,的启动模式呢?当然是有啦,看看第三种启动模式。
三:singleTask 模式可以很好地解决重复创建栈顶活动的问题。当活动指定为 singleTask 模式后,每次启动该活动时系统首先会在返回栈中检查是否存在该活动的实例,如果发现已经存在则直接使用该实例,并把在这个活动之上的所有活动统统出栈,如果没有发现就会创建一个新的活动实例。注意:如果检查到返回栈中存在该活动的实例,会回调该方法:onNewIntent(Intent intent),而onCreate(Bundle savedInstanceState) 方法,将不再回调。
四:singleInstance 模式就比较强大一些,有意思一些。指定为singleInstance模式的活动会启动一个新的返回栈来管理活动。那么这样做有什么意义呢?想象以下场景,假设我们的程序中有一个活动是允许其他程序调用的,如果我们想实现其他程序和我们的程序可以共享这个活动的实例,应该如何实现呢?使用前面三种启动模式肯定是做不到的,因为每个应用程序都会有自己的返回栈,同一个活动在不同的返回栈入栈是必然是创建了新的实例。而使用singleInstance 模式就可以解决这个问题,在这种模式下会有一个单独的返回栈来管理这个活动,不管是那个应用程序来访问这个活动,都公用同一个返回栈,也就解决了共享活动实例的问题。
五:TaskAffinity 的相关知识点。(比较重要的一块知识点)
一. intent.setFlags()方法中的参数值含义:
1.FLAG_ACTIVITY_CLEAR_TOP:例如现在的栈情况为:A B C D 。D此时通过intent跳转到B,如果这个intent添加FLAG_ACTIVITY_CLEAR_TOP标记,则栈情况变为:A B。如果没有添加这个标记,则栈情况将会变成:A B C D B。也就是说,如果添加了FLAG_ACTIVITY_CLEAR_TOP标记,并且目标Activity在栈中已经存在,则将会把位于该目标activity之上的activity从栈中弹出销毁。这跟上面把B的Launch mode设置成singleTask类似。简而言之,跳转到的activity若已在栈中存在,则将其上的activity都销掉。注意:虽与singleTask类似,但不同点为:如果目标Activity在栈中已经存在,仍然会再此执行Activity的生命周期。当然,onNewIntent(Intent intent) 方法也不会回调。
2.FLAG_ACTIVITY_NEW_TASK:例如现在栈1的情况是:A B C。C通过intent跳转到D,并且这个intent添加了FLAG_ACTIVITY_NEW_TASK标记,如果D这个Activity在Manifest.xml中的声明中添加了Task affinity,系统首先会查找有没有和D的Task affinity相同的task栈存在,如果有存在,将D压入那个栈,如果不存在则会新建一个D的affinity的栈将其压入。如果D的Task affinity默认没有设置,则会把其压入栈1,变成:A B C D,这样就和不加FLAG_ACTIVITY_NEW_TASK标记效果是一样的了。注意如果试图从非activity的非正常途径启动一个activity(例见下文“intent.setFlags()方法中参数的用例”),比如从一个service中启动一个activity,则intent比如要添加FLAG_ACTIVITY_NEW_TASK标记(编者按:activity要存在于activity的栈中,而非activity的途径启动activity时必然不存在一个activity的栈,所以要新起一个栈装入启动的activity)。简而言之,跳转到的activity根据情况,可能压在一个新建的栈中。
3.FLAG_ACTIVITY_NO_HISTORY:例如现在栈情况为:A B C。C通过intent跳转到D,这个intent添加FLAG_ACTIVITY_NO_HISTORY标志,则此时界面显示D的内容,但是它并不会压入栈中。如果按返回键,返回到C,栈的情况还是:A B C。如果此时D中又跳转到E,栈的情况变为:A B C E,此时按返回键会回到C,因为D根本就没有被压入栈中。简而言之,跳转到的activity不压在栈中。
4.FLAG_ACTIVITY_SINGLE_TOP:和Activity的Launch mode的singleTop类似。如果某个intent添加了这个标志,并且这个intent的目标activity就是栈顶的activity,那么将不会新建一个实例压入栈中。简而言之,目标activity已在栈顶则跳转过去,不在栈顶则在栈顶新建activity。
二.intent.setFlags()方法中参数的用例:
很多人使用startActivity时候,会碰到如下的异常:
Caused by: android.util.AndroidRuntimeException: Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?
都知道,Context中有一个startActivity方法,Activity继承自Context,重载了startActivity方法。如果使用Activity的startActivity方法,不会有任何限制,而如果使用Context的startActivity方法的话,就需要开启一个新的task(编者按:参见一.2.的编者按),遇到上面那个异常的,都是因为使用了Context的startActivity方法。解决办法是:Java代码中加一个flag,即intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)。这样就可以在新的task里面启动这个Activity了。