在AndroidManifest.xml中配置activity时,android:launchMode属性会指定启动activity的模式,有四种:
standard
singleTop
singleTask
singleInstance
这四种模式一般配合Intent属性变量FLAG_ACTIVITY_XXX使用,比如FLAG_ACTIVITY_NEW_TASK,本文暂时撇开FLAG_ACTIVITY_XXX,只讨论这四种模式的启动结果,先考虑只在同一个应用下的情况。
standard模式
系统默认情况下就是standard模式,假如A中设置为默认模式,A中有一个按钮,单击按钮时只启动自己,看看启动后的结果。AndroidManifest.xml、A和源码及布局分别为:
AndroidManifest.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.administrator.myapplication">
<application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name="com.example.administrator.myapplication.AActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> </intent-filter> <intent-filter> <action android:name="com.feeyan.www.a_activity"></action> <category android:name="android.intent.category.DEFAULT"></category> </intent-filter> </activity> </application>
</manifest> |
AActivity.java
1 2 3 4 5 6 7 8 9 10 11 12 | public class AActivity extends AppCompatActivity {
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.a_layout); }
public void startToB(View view) { startActivity(new Intent("com.feeyan.www.a_activity")); } } |
a_layout.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="com.example.administrator.myapplication.AActivity">
<TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello World!" />
<Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="to B" android:id="@+id/button" android:layout_centerVertical="true" android:layout_centerHorizontal="true" android:textSize="20sp" android:onClick="startToB" /> </RelativeLayout> |
注:本文案例在小米4上测试
假设现在没有运行程序,先通过命令dumpsys activity activities在串口中查看所有activity栈信息:
看红色区域,activity栈中只有一个代号为0的栈,栈中也只有一个id号为#1的task和id号为#0的ActivityRecord,ActivityRecord中保存的包名是com.miui.home,启动类名为.launcher.Launcher,也就是说,目前Activity栈中只有Launcher应用,没有别的应用。
现在启动案例,启动后的栈信息:
案例启动后,系统新建了一个栈stack #1,其中有新启动的Activity实例AActivity,保存在id号为#24的task中
然后,点击按钮再次启动AActivity,启动后的栈信息为:
在同样的stack #1、同样的task #24中多了一个AActicity实例,两个实例对应的ActivityRecord地址不一样,一个是42e23ae0,一个是42da3a08。
默认标准模式,每启动一次,就创建一个新实例,并放到栈顶,并且该实例放在同样的任务task中、同样的activity栈中。不会再新创建栈、task。
singleTop模式
如果把A设为默认Standard标准模式,把B设置singleTop模式,A启动B后看看什么情况,相关源码较简单,不再添加,本文后又源码下载地址。
在stack #1中,新创建了一个task #25,包含B实例和A实例,新启动的实例位于栈顶。如果在B中点击按钮再次启动B,发现栈信息不会改变,而且无论点击多少次按钮,stack #1栈中还是B和A,不会重新创建实例。
假设在A中启动B,B启动C,C设为标准模式,启动后的栈信息:
栈顶是C,其次是B和A,A在栈最底部;如果此时在C中点击按钮再启动B呢?启动后的栈信息:
发现栈顶又创建了一个B实例,不会复用栈中已有的实例,栈中总共有2个B实例!
singleTop模式跟标准Standard模式差不多,只不过多了一种情况,分为两种情况来看:
1. 如果栈中已经有了待启动activity实例并且位于栈顶,那么再次启动该activity时,系统会直接复用该实例,不会再创建新的实例;如果没有,就新创建实例,并放到栈顶;
2. 如果栈中已经有了待启动activity实例,但不在栈顶,那么再次启动该activity时,系统会再次创建新的实例并将该实例放到栈顶,这种情况和standard模式一样。
singleTask模式
如果A和C设置standard模式,B设为singleTask模式,A启动B,B启动C,按照这个顺序启动后,栈中自底向上为:A—–B—–C,如果此时在C中点击按钮再次启动B呢?启动后栈信息为:
此时,Task #30中只有A和B,B位于栈顶,没有C了,这个和singleTop有了明显的差别,在singleTop中C不会消失,而此时C却消失了。
1. 如果栈中已经有了待启动activity实例并且位于栈顶,那么再次启动该activity时,系统会直接复用该实例,不会再创建新的实例;如果没有,就新创建实例,并放到栈顶;
2. 如果栈中已经有了待启动activity实例但不在栈顶,那么再次启动该activity时,系统会复用已有实例,并且把位于该实例之上的所有其他activity实例移出栈,同时将该实例放在栈顶。
singleInstance模式
如果设A和C为standard模式,B为singleInstance模式,A启动B后栈信息为:
发现和上面三种情况都不一样的地方,在栈中新创建了一个task,并把B放入其中,上面三种情况都是在同样的task中。如果再B中启动C,启动后的栈:
A和C都是标准模式,都在同一个task中,而B在另外一个task中,因为启动了C,C在栈顶。如果再C中点击按钮再次启动B,结果会是什么?
B所在的task跑到了栈顶,A和C所在的task在栈底。而且B始终只有一个实例。
1. 如果栈中没有待启动activity实例,启动该activity时,系统会新创建一个task,再创建一个待启动activity实例,把该实例放到新task中,并且该task会在栈顶;
2. 如果栈中已经有了待启动activity实例,不管在栈的什么位置,系统都会复用已存在实例,并且把该实例放在栈顶,而且该task中只有一个该实例,不会再有第二个实例,也不会有其他activity实例;
3. 如果一个应用中有多个activity都设置成singleInstance模式,那么每个启动后的activity实例都保存在一个task中,不会在同一个task中,task和实例是一对一关系。
同一应用中测试小结
a. standard、singleTop存在多种实例的可能(“可能”二字表明,singleTop情况下,如果栈顶已有实例,再次启动时只会复用,如果不在栈顶,就会新创建实例);而singleTask和singleInstance只有一个实例,再次启动时不会创建新实例;
b. singleTask和singleInstance模式,再次调用时都会先调用onNewIntent方法,再调用onResume方法;对于singleTop,如果栈顶已有实例,也是先调用onNewIntent方法,再调用onResume方法,如果不在栈顶或者还没有实例,就会先调用onCreate方法;
c. singleInstance模式不同于其他三种,首次启动时会新开启一个task,该task只包含一个实例;再次启动时只会复用该task,不再新创建。
同一应用中只有1个app,源码地址:https://yunpan.cn/crybfHCWhcbwW 访问密码:e7cb
不同应用之间测试小结
上面分析了在同一应用中的情况,再看看不同应用之间的情形。通过测试得知:standard、singleTop没有什么改变,还是在在同样的栈、task中;singleInstance模式下也没有改变,还是会创建新的task并保存唯一实例;但singleTask却不一样,首次启动时,系统会在当前栈中创建一个新的task,再次启动时,复用已有task;而在同一应用中,再次启动时,不会再创建新task的,直接复用已有task。
不同应用之间测试有2个app,源码地址:https://yunpan.cn/crKLt2CuZ7drc 访问密码 e4b8
最终总结
不管是同一应用中还是不同应用之间,standard、singleTop、singleInstance各自没有区别,只有singleTask不一样,同一应用中不创建新的task,不同应用中有可能会创建新的task。(注:这里用到了“可能”二字,没有用“一定”,是因为这与taskAffinity属性有关,如果设置了此属性,就会在该属性对应的task中启动实例,否则,会创建新的task)。