启动模式的一个典型应用:
手机淘宝中我的--右上角设置中,右上角有个首页,点击首页后就可以打开首页,但这时再按返回键却无法回到设置界面了。
这就可以通过启动模式很方便的控制。
先介绍一个查看安卓设备中任务栈以及栈中Activity情况的命令
一. 使用命令
adb shell dumpsys activity
为了可以在控制台直接使用这个命令,我们可以在系统环境变量中为path添加adb.exe文件的目录
例如我的电脑中sdk中的adb.exe位于C:\developTools\SDK\sdk\platform-tools目录中,
我可以这样配置adb的path
当然不配置adb也可以,若不配置,当要使用adb命令时就需要输入adb的完整路径了,比如我的电脑adb路径是C:\developTools\SDK\sdk\platform-tools\adb.exe,那么我可以这样
C:\developTools\SDK\sdk\platform-tools\adb shell dumpsys activity
运行上面命令后我们会看到如下输出,可以看到有两个栈,一个com.xxx.ccc和一个com.example.tt,com.xxx.ccc栈中有4个Activity,com.example.tt栈中有3个Activity,
当按安卓手机中返回键时,会按从上到下顺序依次退栈,
即按com.xxx.ccc栈中MainActivity3->com.xxx.ccc中MainActivity2->->com.xxx.ccc中MainActivity->com.xxx.ccc中MainActivity4->com.example.tt中MainActivity3->com.example.tt中MainActivity2->->com.example.tt中MainActivity->桌面
当切换任务栈时,会有一个Activity缩放动画效果,比如com.xxx.ccc中MainActivity4 按返回键到com.example.tt中MainActivity3时,或者com.example.tt中MainActivity3开启com.xxx.ccc中MainActivity4时都会有一个动画效果
二.启动模式
Activity才有启动模式,启动模式需要在清单文件中的Activity节点中配置,可以给每一个activity分别配置启动模式
例如
启动模式可以不配置,默认是standard模式。
安卓的Activity总共有4种启动模式,分别是standard,singTop,singTask,singInstance。
singTask和singInstance还能指定新启动的activity所在的栈,可以在清单中的activity节点中这样配置
<activity android:name="com.example.tt.MainActivity4" android:taskAffinity="com.xxx.ccc" android:launchMode="singleTask"></activity>
standard和singTop不支持指定任务栈,所以配置该属性无效。
4种启动模式的activity默认都运行在任务栈名与应用包名相同的任务栈中(即使任务栈名相同也有可能是两个任务栈,具体是不是同一个任务栈需要根据任务栈中某一值来判断)
(1)standard: 默认启动模式,若清单中不配置则是standard模式。每次启动standard模式的activity都会创建新的activity实例。一个任务栈中可以有多个该实例,谁启动的standard模式的activity,那么这个standard模式的activity就运行在启动它的activity所在的任务栈中。(但有个特例,singInstance模式的activity启动了standard模式的activity,则sta模式的activity不会运行在singInstance模式的activity所在的栈中)。
例如MainActivity,MainActivity2,MainActivity3,MainActivity4都是standard模式, 当MainActivity4启动MainActivity4时,可以看到每次都会创建新的activity
Running activities (most recent first): TaskRecord{53c5ec5 #255 A=com.example.tt U=0 sz=7} Run #6: ActivityRecord{5dde76b u0 com.example.tt/.MainActivity4 t255} Run #5: ActivityRecord{f13a97b u0 com.example.tt/.MainActivity4 t255} Run #4: ActivityRecord{c7a6fbd u0 com.example.tt/.MainActivity4 t255} Run #3: ActivityRecord{8c3994d u0 com.example.tt/.MainActivity4 t255} Run #2: ActivityRecord{b55c7f u0 com.example.tt/.MainActivity3 t255} Run #1: ActivityRecord{36ef18f u0 com.example.tt/.MainActivity2 t255} Run #0: ActivityRecord{d379153 u0 com.example.tt/.MainActivity t255}
当我们用getApplicationContext().startActivity(new Intent(getApplicationContext(), MainActivity4.class));启动activity时会抛出如下异常
这是由于context中没有任务栈导致的。
(2)singTop:栈顶复用模式。如果当前activity是singTop模式,当该activity再次启动自己时,并不会创建新的activity,而且也不会调用当前activity的onCreate和onStart方法,但会调用当前activity的onNewIntent方法。 若singTop模式的activityA已经存在栈中,但不是栈顶的话,若再次启动singTop模式的activity,则会创建新的activity,不会执行原singTop模式activity的onNewIntent方法。
例如MainActivity,MainActivity2,MainActivity3都是standard模式,MainActivity4是singTop模式,当MainActivity4启动MainActivity4时,可以看到并没有创建新的activity
Running activities (most recent first): TaskRecord{1553bef #254 A=com.example.tt U=0 sz=4} Run #3: ActivityRecord{3038f55 u0 com.example.tt/.MainActivity4 t254} Run #2: ActivityRecord{ab22e5 u0 com.example.tt/.MainActivity3 t254} Run #1: ActivityRecord{62e00f1 u0 com.example.tt/.MainActivity2 t254} Run #0: ActivityRecord{fc3c595 u0 com.example.tt/.MainActivity t254}
Running activities (most recent first): TaskRecord{561d45a #256 A=com.example.tt U=0 sz=8} Run #7: ActivityRecord{2082c6 u0 com.example.tt/.MainActivity4 t256} Run #6: ActivityRecord{ba21d8 u0 com.example.tt/.MainActivity3 t256} Run #5: ActivityRecord{f8c1d54 u0 com.example.tt/.MainActivity2 t256} Run #4: ActivityRecord{a49a842 u0 com.example.tt/.MainActivity t256} Run #3: ActivityRecord{e3a7692 u0 com.example.tt/.MainActivity4 t256} Run #2: ActivityRecord{cba592e u0 com.example.tt/.MainActivity3 t256} Run #1: ActivityRecord{312ba8a u0 com.example.tt/.MainActivity2 t256} Run #0: ActivityRecord{5bc6f1e u0 com.example.tt/.MainActivity t256}
例如 MainActivity,MainActivity2,MainActivity3都是standard模式,MainActivity4是singTask模式,
并指定MainActivity4运行在com.xxx.ccc栈中,
然后依次启动MainActivity,MainActivity2,MainActivity3,MainActivity4,可以看到创建了新的栈com.xxx.ccc和activity4
Running activities (most recent first): TaskRecord{fa0d4de #258 A=com.xxx.ccc U=0 sz=1} Run #3: ActivityRecord{62f75b8 u0 com.example.tt/.MainActivity4 t258} TaskRecord{a0a76bf #257 A=com.example.tt U=0 sz=3} Run #2: ActivityRecord{26c5a88 u0 com.example.tt/.MainActivity3 t257} Run #1: ActivityRecord{487e504 u0 com.example.tt/.MainActivity2 t257} Run #0: ActivityRecord{45ce62f u0 com.example.tt/.MainActivity t257}在上面的基础上再启动standa模式的MainActivity,MainActivity2,MainActivity3,会看到MainActivity,MainActivity2,MainActivity3都运行在了com.xxx.ccc任务栈中
Running activities (most recent first): TaskRecord{fa0d4de #258 A=com.xxx.ccc U=0 sz=4} Run #6: ActivityRecord{392eaf9 u0 com.example.tt/.MainActivity3 t258} Run #5: ActivityRecord{dc37945 u0 com.example.tt/.MainActivity2 t258} Run #4: ActivityRecord{c79bfdb u0 com.example.tt/.MainActivity t258} Run #3: ActivityRecord{62f75b8 u0 com.example.tt/.MainActivity4 t258} TaskRecord{a0a76bf #257 A=com.example.tt U=0 sz=3} Run #2: ActivityRecord{26c5a88 u0 com.example.tt/.MainActivity3 t257} Run #1: ActivityRecord{487e504 u0 com.example.tt/.MainActivity2 t257} Run #0: ActivityRecord{45ce62f u0 com.example.tt/.MainActivity t257}然后继续启动MainActivity4,则会看到com.xxx.ccc中是MainActivity,MainActivity2,MainActivity3都被移除栈了
Running activities (most recent first): TaskRecord{fa0d4de #258 A=com.xxx.ccc U=0 sz=1} Run #3: ActivityRecord{62f75b8 u0 com.example.tt/.MainActivity4 t258} TaskRecord{a0a76bf #257 A=com.example.tt U=0 sz=3} Run #2: ActivityRecord{26c5a88 u0 com.example.tt/.MainActivity3 t257} Run #1: ActivityRecord{487e504 u0 com.example.tt/.MainActivity2 t257} Run #0: ActivityRecord{45ce62f u0 com.example.tt/.MainActivity t257}
例如 MainActivity,MainActivity2,MainActivity3都是standard模式,MainActivity4是singInstance模式,并且采用默认任务栈时,则MainActivity3启动MainActivity4 时会创建新的任务栈,虽然新的任务栈的名字和原来任务栈的名字相同(都是包名)但却是一个新的任务栈。当MainActivity4继续启动MainActivity4时,则任务栈和activity不会有任何变化。
Running activities (most recent first): TaskRecord{60baca6 #260 A=com.example.tt U=0 sz=1} Run #3: ActivityRecord{ed1a804 u0 com.example.tt/.MainActivity4 t260} TaskRecord{18dc294 #259 A=com.example.tt U=0 sz=3} Run #2: ActivityRecord{41f8740 u0 com.example.tt/.MainActivity3 t259} Run #1: ActivityRecord{8dc273c u0 com.example.tt/.MainActivity2 t259} Run #0: ActivityRecord{77da77b u0 com.example.tt/.MainActivity t259}当继续通过MainActivity4启动MainActivity3时,会看到MainActivity3又回到了原任务栈中
Running activities (most recent first): TaskRecord{556e59d #261 A=com.example.tt U=0 sz=4} Run #4: ActivityRecord{13c8d80 u0 com.example.tt/.MainActivity3 t261} TaskRecord{1fab5e3 #262 A=com.example.tt U=0 sz=1} Run #3: ActivityRecord{a82bf76 u0 com.example.tt/.MainActivity4 t262} TaskRecord{556e59d #261 A=com.example.tt U=0 sz=4} Run #2: ActivityRecord{b997b52 u0 com.example.tt/.MainActivity3 t261} Run #1: ActivityRecord{28b39ee u0 com.example.tt/.MainActivity2 t261} Run #0: ActivityRecord{a513bc1 u0 com.example.tt/.MainActivity t261}
Running activities (most recent first): TaskRecord{1fab5e3 #262 A=com.example.tt U=0 sz=1} Run #4: ActivityRecord{a82bf76 u0 com.example.tt/.MainActivity4 t262} TaskRecord{556e59d #261 A=com.example.tt U=0 sz=4} Run #3: ActivityRecord{13c8d80 u0 com.example.tt/.MainActivity3 t261} Run #2: ActivityRecord{b997b52 u0 com.example.tt/.MainActivity3 t261} Run #1: ActivityRecord{28b39ee u0 com.example.tt/.MainActivity2 t261} Run #0: ActivityRecord{a513bc1 u0 com.example.tt/.MainActivity t261}
三,注意
即使指定了Activity所在的任务栈和启动模式为singTask或singInstance也不能直接用ApplicationContext启动activity,否则同样会抛出同样的异常。解决方法就是在intent中添加flag FLAG_ACTIVITY_NEW_TASK
例如:
Intent intent=new Intent(getApplicationContext(), MainActivity4.class); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); getApplicationContext().startActivity(intent);
FLAG_ACTIVITY_SINGLE_TOP的作用和在清单中指定singTop效果相同。
FLAG_ACTIVITY_CLEAR_TOP,在同一任务栈中,若要启动的实例已经存在,则把该实例以及上面的activity全部移除栈,并创建新的实例,然后加入栈中
例如,有4个Activity,全是standard模式,依次启动4个activity
Running activities (most recent first): TaskRecord{13451a2 #271 A=com.example.tt U=0 sz=4} Run #3: ActivityRecord{aae2f54 u0 com.example.tt/.MainActivity4 t271} Run #2: ActivityRecord{eae7f90 u0 com.example.tt/.MainActivity3 t271} Run #1: ActivityRecord{2c1608c u0 com.example.tt/.MainActivity2 t271} Run #0: ActivityRecord{435285c u0 com.example.tt/.MainActivity t271}
在MainActivity4中启动MainActivity2,可以看到栈中就剩两个了,而且MainActivity2已经不是原来那个activity了,而MainActivity还是原来那个
Running activities (most recent first): TaskRecord{13451a2 #271 A=com.example.tt U=0 sz=2} Run #1: ActivityRecord{7b48125 u0 com.example.tt/.MainActivity2 t271} Run #0: ActivityRecord{435285c u0 com.example.tt/.MainActivity t271}
FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS:如果不希望用户通过历史列表回到我们的Activity,只需设置该flag,或在清单中配置
android:excludeFromRecents="true"
清单中还可以给activity配置android:allowTaskReparenting="true",当appA启动了appB的activityB,并且activityB配置了android:allowTaskReparenting="true"后,当回到桌面,然后在点击桌面上appB的图标时,开启的不是appB主activity,而是刚才启动的activityB。