launchMode 属性的四种启动模式
接上一篇文章https://blog.csdn.net/nishigesb123/article/details/88911044
引子
之前提到了launchMode属性的四种模式,下面回顾一下。
通过AndroidManifest.xml中activity标签的属性launchMode中可以设置Activity的加载模式,一共有以下四种模式:
- standard:标准模式,以这种模式加载必定会构造一个新的Activity实例放到目标task中的activity栈顶,不管当前 task的栈顶是什么情况。
- singleTop: 与standard模式类似,区别在于加载activity会多个判断步骤。判断需要加载的新activity与当前 task栈顶的activity是不是同一个,相同的话就不再构造新的activity,并调用这个activity的newlnstance ()方法,不相同就还是会构造新的activity放到栈顶。
- singleTask:该种模式下,会创建一个新的task来加iactivity,并且这个task中只允许存在一个Activity的一个实例(以后可以加载其他activity的实例)
- SingleInstance:这种模式下,会创建一个新的task并且这个task中只能存在一个需要加载的这个Activity实例,即除了这个activity之外,不允许其他activity。
直接看概念对于对“栈”不是很理解的朋友,可能一时半会难以理解,下面通过一系列案例深刻的去领会一下四种模式的区别和内涵。
准备工作
我们需要准备两个Activity,添加两个按钮,并为每个按钮准备点击事件,参考代码如下
Activity1
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/TextViewA"
android:text="This is TextViewA"
android:gravity="center"
app:layout_constraintTop_toTopOf="parent"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/AtoA"
android:text="AtoA"
android:gravity="center"
android:onClick="AtoA"
app:layout_constraintTop_toBottomOf="@id/TextViewA"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/AtoB"
android:text="AtoB"
android:onClick="AtoB"
android:gravity="center"
app:layout_constraintTop_toBottomOf="@id/AtoA"/>
</android.support.constraint.ConstraintLayout>
Activity2
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".Main2Activity">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/TextViewB"
android:text="This is TextViewB"
android:gravity="center"
app:layout_constraintTop_toTopOf="parent"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/BtoA"
android:text="BtoA"
android:onClick="BtoA"
android:gravity="center"
app:layout_constraintTop_toBottomOf="@id/TextViewB"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/BtoB"
android:text="BtoB"
android:onClick="BtoB"
android:gravity="center"
app:layout_constraintTop_toBottomOf="@id/BtoA"/>
</android.support.constraint.ConstraintLayout>
接下来我们要完成定义的四个点击事件
代码非常简单,就是通过显式Intent,Activity跳转到另一个Activity
AtoB即从ActivityA(MainActivity)跳转到ActivityB(Main2Activity),其余同理。
public void AtoA(View view){
Intent intent = new Intent(this,MainActivity.class);
startActivity(intent);
}
public void AtoB(View view){
Intent intent = new Intent(this,Main2Activity.class);
startActivity(intent);
}
public void BtoA(View view){
Intent intent = new Intent(this,MainActivity.class);
startActivity(intent);
}
public void BtoB(View view){
Intent intent = new Intent(this,Main2Activity.class);
startActivity(intent);
}
下面正式进入测试,首先是
standard
首先,我们把配置清单文件的两个activity的launchMode换成standard(实际上默认就是这个,不加也没事)
下面是测试效果,可以看到,当点击ATOA的时候,虽然是当前Activity跳转到当前Activity,但是实际上却是通过新建Activity来实现的(可以发现点击的时候屏幕有出现加载Activity的过场动画——屏幕抖了一下),即在standard模式启动无论如何创建新的Activity实例。都会所以我们点击了三次AToA,需要点击四次返回才能回到主页。
然后我们再进行一组测试,交替点击ATOB和ATOA,再观察上面的提示文字(提示文字提示了当前处于的Activity),不难发现,返回的时候,ActivityA和B也是交替出现的,再次论证了我们的结论。
singleTop
接下来我们把配置清单文件的两个activity的launchMode换成singleTop,图就不截了
我们无论如何点击ATOA,界面都没有发生变化,点击返回立马回到home界面。这就和前面的默认模式不一样了——当加载的是当前Activity,singleTop并不会创建新的Activity。
我们先点击ATOB(有反应跳转到了ActivityB),然后在ActivityB点击了几次BTOB(没有反应)
到这里都没有什么问题,和之前的结论一样。
接下来再点击BTOA,回到了ActivityA,最后我们返回,发现顺序是A->B->A
即虽然回到了ActivityA,但是实际上和一开始的那个ActivityA并不是同一个,即创建了新的Activity。
这可能有点难以理解,但是和我们之前的结论并不矛盾,当加载的是当前Activity,singleTop并不会创建新的Activity。
注意体会“当前”两个字,所以最终我们的结论是:当加载的是当前Activity,singleTop并不会创建新的Activity,反之,则会创建新的Activity。
singleTask
接下来我们把配置清单文件的两个activity的launchMode换成singleTask
下面看图,图中测试分三部分。
第一部分:在activityA狂按AtoA,没有反应,点返回后直接退出。
第二部分:在activityA点AtoB切换到activityB,再在activityB狂按BtoB,没有反应,点返***到A,再返回退出。
如此来看似乎和singleTop没有区别,然而,
第三部分:在activityA点AtoB切换到activityB,再在activityB点BtoA,切换回activityA,最后点了一下返回直接退出了。
实际上高版本的动画效果不明显,可以试试自定义切换动画,或者在低版本里测试。
实际上,可以发现,AtoB之后,BtoA的过程,不同于singleTop又创建了新的Activity,singleTask选择直接返回到之前的A。
即对于栈中已有的活动,singleTask会进行复用。
更具体的理解“复用”,我们再来一组测试。
AtoB->BtoA->AtoB
点击返回返回到了A,再点击返回退出。
然而我们分析一下:
第一次操作AtoB,此时栈情况由下至上A-B
第二次操作BtoA,由于A已经存在,所以复用A,由于B是在A之后才进入的,所以B会被清理掉,此时栈中将会只有A
第三次操作AtoB,和第一次操作完全一样此时栈情况由下至上A-B
所以需要点击两次返回才能退出。
singleInstance
接下来我们把配置清单文件的两个activity的launchMode换成singleInstance
和之前的测试一样依旧分三部分
第一部分:在activityA狂按AtoA,没有反应,点返回后直接退出。
第二部分:在activityA点AtoB切换到activityB,再在activityB狂按BtoB,没有反应,点返***到A,再返回退出。
到这里结果也完全一样。
第三部分:在activityA点AtoB切换到activityB,再在activityB点BtoA,切换回activityA,
再看两组测试。
第一组直接见过面的,就是AtoB,BtoA,AtoB三次操作,结果是点了两下返回就回到了主页。
第二组,我们交替切换AB,切换多少次数不清了,然而结果依旧是点了两下返回主页。
其实仔细观察,在这种模式下,Activity切换的动画效果非常“特别”,是不是很像点击了任务切换按钮切换了任务时的样子。
没错,回忆下对SingleInstance的介绍,实际上,在该模式下,并非新建Activity,而是新建Task,在新的Task里新建Activity。
即如果加载的不是当前Activity,Activity总是会在新的task中运行
然后我们就可以试着去得出结论了。
首先,对于加载的是当前Activity,显然SingleInstance也不会去创建新的Activity。
对于加载的不是当前Activity,新建Task,在新的Task里新建Activity。
补充
可以通过Flags来设置Activity的启动模式,参考代码如下:
public void Flags(View view){
Intent intent = new Intent(this,Main2Activity.class);
//设置Activity的启动模式,下为在新的任务栈中启动Activity,相当于singleTask
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
}
实际上效果和 launchMode 差不太多,像“FLAG_ACTIVITY_SINGLE_TOP”不用说也知道和singleTop类似把。
时间关系,就不一一演示了。
值得一提的是,当launchMode和setFlags同时存在时,后者优先级更高一些。
setFlags可选的参数比较多,可以参考这篇文章https://blog.csdn.net/berber78/article/details/7278408
或者参考API:https://developer.android.google.cn/reference/android/content/Intent.html#setFlags(int)