对于Activity中的四个lauchMode【standard(默认)、singleTop、singleTask、singleInstance】的介绍网上已经有大把的文章了,但是在实际应用开发时,对于这几个的区别一直搞混,在有些实际场景中需要通过设置不同模式来解决的比较模糊,所以有必要记录一下自己对它们的理解,做下备忘,当然是结合网上的资料,而不重复造轮子了,当拿来主义,另外工作中碰到与这里相关的场景也会不断添加,达到融会贯通,话不多说,进入正题。

对于这些模块的介绍,可以参考:http://blog.csdn.net/yyingwei/article/details/8295969,对于第一种默认的模式这里就不记录了,比较容易理解,下面主要是记录一下剩下三种模式:

singleTop:

对于它的直观解释,可以从人家博文中看下,下面先贴出来原话:

从其文字描述来看,该模式还是比较好理解的,下面用实验来证明上面的理论,为了方便,这里用三个界面来阐述既可:ActvityA代表A、ActvityB代表B、ActvityC代表C,其实验步骤如下:

实验一:

用代码进行实验,代码结构如下:

ActivityA.java:

  1. public class ActivityA extends Activity implements OnClickListener {
  2.  
  3. private Button button;
  4.  
  5. @Override
  6. public void onCreate(Bundle savedInstanceState) {
  7. super.onCreate(savedInstanceState);
  8. Log.d("cexo", "ActivityA onCreate()----taskId:" + getTaskId());
  9. setContentView(R.layout.activity_a);
  10. button = (Button) findViewById(R.id.button);
  11. button.setOnClickListener(this);
  12. }
  13.  
  14. @Override
  15. public void onClick(View view) {
  16. Intent intent = new Intent(this, ActivityB.class);
  17. startActivity(intent);
  18. }
  19. }

ActivityB.java:

  1. public class ActivityB extends Activity implements OnClickListener {
  2. private Button button;
  3.  
  4. @Override
  5. public void onCreate(Bundle savedInstanceState) {
  6. super.onCreate(savedInstanceState);
  7. Log.d("cexo", "ActivityB onCreate()----taskId:" + getTaskId());
  8. setContentView(R.layout.activity_b);
  9. button = (Button) findViewById(R.id.button);
  10. button.setOnClickListener(this);
  11. }
  12.  
  13. @Override
  14. public void onClick(View view) {
  15. Intent intent = new Intent(this, ActivityC.class);
  16. startActivity(intent);
  17. }
  18. }

ActivityC.java:

  1. public class ActivityC extends Activity {
  2. @Override
  3. public void onCreate(Bundle savedInstanceState) {
  4. super.onCreate(savedInstanceState);
  5. Log.d("cexo", "ActivityC onCreate()----taskId:" + getTaskId());
  6. setContentView(R.layout.activity_c);
  7. }
  8. }

其布局为:

activity_a.xml:

  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2. android:layout_width="fill_parent"
  3. android:layout_height="fill_parent" >
  4.  
  5. <TextView
  6. android:id="@+id/textView"
  7. android:layout_width="match_parent"
  8. android:layout_height="wrap_content"
  9. android:text="ActivityA" />
  10.  
  11. <Button
  12. android:id="@+id/button"
  13. android:layout_width="match_parent"
  14. android:layout_height="wrap_content"
  15. android:layout_alignParentBottom="true"
  16. android:text="go ActivityB" />
  17.  
  18. </RelativeLayout>

activity_b.xml:

  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2. android:layout_width="fill_parent"
  3. android:layout_height="fill_parent" >
  4.  
  5. <TextView
  6. android:id="@+id/textView"
  7. android:layout_width="match_parent"
  8. android:layout_height="wrap_content"
  9. android:text="ActivityB" />
  10.  
  11. <Button
  12. android:id="@+id/button"
  13. android:layout_width="match_parent"
  14. android:layout_height="wrap_content"
  15. android:layout_alignParentBottom="true"
  16. android:text="go ActivityC" />
  17.  
  18. </RelativeLayout>

activity_c.xml:

  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2. android:layout_width="fill_parent"
  3. android:layout_height="fill_parent" >
  4.  
  5. <TextView
  6. android:id="@+id/textView"
  7. android:layout_width="match_parent"
  8. android:layout_height="wrap_content"
  9. android:text="ActivityC" />
  10.  
  11. </RelativeLayout>

其AndroidManifest.xml配置如下,都是采用默认配置:

  1. <?xml version="1.0" encoding="utf-8"?>
  2. <manifest xmlns:android="http://schemas.android.com/apk/res/android"
  3. package="com.example.launchmodeltest"
  4. android:versionCode="1"
  5. android:versionName="1.0" >
  6.  
  7. <uses-sdk
  8. android:minSdkVersion="8"
  9. android:targetSdkVersion="18" />
  10.  
  11. <application
  12. android:allowBackup="true"
  13. android:icon="@drawable/ic_launcher"
  14. android:label="@string/app_name"
  15. android:theme="@style/AppTheme" >
  16. <activity
  17. android:name="com.example.launchmodeltest.ActivityA"
  18. android:label="@string/app_name" >
  19. <intent-filter>
  20. <action android:name="android.intent.action.MAIN" />
  21.  
  22. <category android:name="android.intent.category.LAUNCHER" />
  23. </intent-filter>
  24. </activity>
  25. <activity android:name="com.example.launchmodeltest.ActivityB" />
  26. <activity android:name="com.example.launchmodeltest.ActivityC" />
  27. </application>
  28.  
  29. </manifest>

运行如下:

输出日志如下:

此时进行第二步,从ActivityC中再次跳转到ActivityC,在AcitivtyC的lauchMode为默认情况下,很容易理解,会再重新创建一个ActivityC的实例,但是如果将其设置为singleTop呢?上面结果也已经说明了,下面来验证下:

添加一个跳转的按钮:

ActivityC.java:

  1. public class ActivityC extends Activity implements OnClickListener {
  2. private Button button;
  3.  
  4. @Override
  5. public void onCreate(Bundle savedInstanceState) {
  6. super.onCreate(savedInstanceState);
  7. Log.d("cexo", "ActivityC onCreate()----taskId:" + getTaskId());
  8. setContentView(R.layout.activity_c);
  9. button = (Button) findViewById(R.id.button);
  10. button.setOnClickListener(this);
  11. }
  12.  
  13. @Override
  14. public void onClick(View view) {
  15. Intent intent = new Intent(this, ActivityC.class);
  16. startActivity(intent);
  17. }
  18. }

activity_c.xml:

  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2. android:layout_width="fill_parent"
  3. android:layout_height="fill_parent" >
  4.  
  5. <TextView
  6. android:id="@+id/textView"
  7. android:layout_width="match_parent"
  8. android:layout_height="wrap_content"
  9. android:text="ActivityC" />
  10.  
  11. <Button
  12. android:id="@+id/button"
  13. android:layout_width="match_parent"
  14. android:layout_height="wrap_content"
  15. android:layout_alignParentBottom="true"
  16. android:text="go ActivityC" />
  17.  
  18. </RelativeLayout>

运行如下:

从中可以发现,我按了多次跳转ActivityC,都没有创建它的新实例,因为此时它已经在栈顶了,这时按back键返回三次既可退出,这就是singleTop的作用:如果要打开的Activity配了此种启动模式,如果它已经处于栈顶,则不会重新创建实例了。

但是,我们知道对于Activity,有一个onNewIntent()方法,在实际工作中,当设置singleTask模式时,如果要跳转的Activity已经打开过了,再跳转时就会走这个方法,再回到我们现在的这个例子,虽说点击了跳转不要再创建一个新的ActivityC,那它的onNewIntent()方法会走么?下面来论证下:

可见,最终会走onNewIntent()方法,下面来处理第二种情况。

实验二:

先看下博客原文描述:

咱们来实验下,先将ActivityB的lauchMode设置为singleTop:

ActivityC.java:

  1. public class ActivityC extends Activity implements OnClickListener {
  2. private Button button;
  3.  
  4. @Override
  5. public void onCreate(Bundle savedInstanceState) {
  6. super.onCreate(savedInstanceState);
  7. Log.d("cexo", "ActivityC onCreate()----taskId:" + getTaskId());
  8. setContentView(R.layout.activity_c);
  9. button = (Button) findViewById(R.id.button);
  10. button.setOnClickListener(this);
  11. }
  12.  
  13. @Override
  14. protected void onNewIntent(Intent intent) {
  15. super.onNewIntent(intent);
  16. Log.d("cexo", "ActivityC onNewIntent()");
  17. }
  18.  
  19. @Override
  20. public void onClick(View view) {
  21. Intent intent = new Intent(this, ActivityB.class);
  22. startActivity(intent);
  23. }
  24. }

见证奇迹的时刻到了:

运行日志如下:

所以,对于这个模式所带来的效果就是:如果要跳转的Activity在栈顶,再次打开则不会重新创建实例,只会走该Activity的onNewIntent()方法;如果不在栈顶,则会重新创建一个实例,注意:之前的实例还是保留。

singleTask:

这里就不费口舌了,直接上别人的原话:

下面就来用实验来证实下,首先将ActivityB中launchMode改为singleTask,并且将其它Activity的launchMode还原,如下:

这时先按这样的顺序启动:ActivityA---->ActivityB----->ActivityC,然后再从ActivityC跳转到ActivityB,查看下栈的情况:

从结果可以看出,当从ActivityC跳转到ActivityB时,这时按两下back键则就退回了桌面,那就说明ActivityB为栈顶了,也就是ActivityC被销毁了,关于这个销毁状态就不打印,可以从运行结果中看出来。

当从ActivityC跳转到ActivityB时,这时也会走ActivityB的onNewIntent()方法,这里就不演示了。

另外,作者对该lauchMode进行了进一步的阐述,总结得挺好的,平常对于SingleTask都想不到这种细节,针对它我自己再来理解下:

1.singleTask 并不一定处于栈底

2.singleTask 并不一定会启动新的task

对于这两点的解释,先引用原作者的:

结合自己的实验来理解,从ActivityA启动ActivityB,其Task还是同一个,也就是说明"singleTask 并不一定会启动新的task",从输出日志可以看出来:

另外ActivityB很显然没有在栈底,所以也说明了"singleTask 并不一定处于栈底"

3.singleTask 并一定会是栈底的根元素

对于这个情况,原作者的解释如下:

关于这个,可以做一个实验来证明下,实验流程用图来说明下:

首先将ActivityB的声明为显示intent,以便另外工程TestApk来调用,如下:

然后按这个顺利启动:ActivityA-------->ActivityB--------->ActivityC:

接着新建一个工程,名叫"TestApk":

MainActivity.java:

  1. public class MainActivity extends Activity implements OnClickListener {
  2.  
  3. private Button button;
  4.  
  5. @Override
  6. protected void onCreate(Bundle savedInstanceState) {
  7. super.onCreate(savedInstanceState);
  8. Log.d("cexo", "MainActivity onCreate()----taskId:" + getTaskId());
  9. setContentView(R.layout.activity_main);
  10. button = (Button) findViewById(R.id.button);
  11. button.setOnClickListener(this);
  12. }
  13.  
  14. @Override
  15. public void onClick(View view) {
  16. // 调用另外一个工程的ActivityB
  17. startActivity(new Intent("com.example.launchmodeltest.ActivityB"));
  18. }
  19.  
  20. }

activity_main.xml:

  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2. xmlns:tools="http://schemas.android.com/tools"
  3. android:layout_width="match_parent"
  4. android:layout_height="match_parent" >
  5.  
  6. <TextView
  7. android:layout_width="wrap_content"
  8. android:layout_height="wrap_content"
  9. android:text="@string/hello_world" />
  10.  
  11. <Button
  12. android:id="@+id/button"
  13. android:layout_width="match_parent"
  14. android:layout_height="wrap_content"
  15. android:layout_alignParentBottom="true"
  16. android:text="go ActivityB" />
  17.  
  18. </RelativeLayout>

下面分两个场景来试验下:

1,LaunchModelTest工程没有启动,然后去调用ActivityB,看这时ActivityB是否会启动新的Task呢?

从中可以发现,实时是新启了一个Task,而且按back退出时,按一下ActivityB就从LaunchModelTest退出来了,这说明ActivityB就位于栈底了,论证了作者所说明的。

2、LaunchModelTest工程先启动,并且按照ActivityA---->ActivityB---->ActivityC的顺序启动了,然后TestApk工程去调用ActivityB,看这时ActivityB是否会启动新的Task呢?

从中可以看出,这时走了ActivityB的onNewIntent方法了,因为栈中已经有ActivityB实例了,则直接用,而且ActivityB还在原来的Task中,并未新启动,下面来看下退栈的顺序:

也就是先退当前栈,由于ActivityB界面最后显示,所以先退它所在的栈,所以按了两个back才退出LaunchModelTest,然后再退其它栈,也就是TestApk所在的栈,记住一点:先退当前栈,然后再退其它栈

关于这个模式,最后再来记下作者总结的,很关键:

singleInstance:

先上别人的解释,然后再一一去用实验验证:

上面已经将文字划分了多个段,对应不同的情况,下面一一来验证:

①:Task栈1的情况为:A B C。C通过Intent跳转到D,而D的Launch mode为singleInstance,则将会新建一个Task栈2。此时Task栈1的情况还是为:A B C。Task栈2的情况为:D。此时屏幕界面显示D的内容:

首先先将所有的Activity的LaunchMode都还原则默认的:

新建一个ActivityD,并将其LauchMode设置为singleInstance,如下:

ActivityD.java:

  1. public class ActivityD extends Activity {
  2.  
  3. @Override
  4. public void onCreate(Bundle savedInstanceState) {
  5. super.onCreate(savedInstanceState);
  6. Log.d("cexo", "ActivityD onCreate()----taskId:" + getTaskId());
  7. setContentView(R.layout.activity_d);
  8. }
  9.  
  10. @Override
  11. protected void onNewIntent(Intent intent) {
  12. super.onNewIntent(intent);
  13. Log.d("cexo", "ActivityD onNewIntent()");
  14. }
  15.  
  16. }

activity_d.xml:

  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  2. android:layout_width="fill_parent"
  3. android:layout_height="fill_parent" >
  4.  
  5. <TextView
  6. android:id="@+id/textView"
  7. android:layout_width="match_parent"
  8. android:layout_height="wrap_content"
  9. android:text="ActivityD" />
  10.  
  11. </RelativeLayout>

并修改ActivityC.java,点击按钮跳转到ActivityD.java:

ActivityC.java:

  1. public class ActivityC extends Activity implements OnClickListener {
  2. private Button button;
  3.  
  4. @Override
  5. public void onCreate(Bundle savedInstanceState) {
  6. super.onCreate(savedInstanceState);
  7. Log.d("cexo", "ActivityC onCreate()----taskId:" + getTaskId());
  8. setContentView(R.layout.activity_c);
  9. button = (Button) findViewById(R.id.button);
  10. button.setOnClickListener(this);
  11. }
  12.  
  13. @Override
  14. protected void onNewIntent(Intent intent) {
  15. super.onNewIntent(intent);
  16. Log.d("cexo", "ActivityC onNewIntent()");
  17. }
  18.  
  19. @Override
  20. public void onClick(View view) {
  21. Intent intent = new Intent(this, ActivityD.class);
  22. startActivity(intent);
  23. }
  24. }

按照上面的流程,这时看下最终效果:

可以看到,跳转到ActivityD时,新建了一个栈,正好如文字描述一样。

②:如果这时D又通过Intent跳转到D,则Task栈2中也不会新建一个D的实例,所以两个栈的情况也不会变化

添加一个Button用来跳转到ActivityD自身,修改如下:

这时再看下结果:

可以看到,当打开了ActivityD之后,再跳到它时,是不会再创建实例的,栈中会保证唯一,跟singleTask模式差不多【关于singleTask和singleInstance的区别,之后会讨论】,但是onNewIntent()方法会触发,也跟描述的一致。

③:如果D跳转到C,则栈1的情况变成了:A B C C,因为C的Launch mode为standard,此时如果再按返回键,则栈1变成:A B C。也就是说现在界面还显示C的内容,不是D

将ActivityD中的跳转改为跳到ActivityC,如下:

这时看下结果:

从退栈的过程来看,先退当前栈,然后再退其它栈,跟singleTask也基本类似,另外从ActivityD跳转到ActivityC时,新创建了一个,所以返回了两次才退到了ActivityB。

另外,从作者关于这个mode的额外描述中,提到了一个特珠情况:"现在有一个问题就是这时这种情况下如果用户点击了Home键,则再也回不到D的即时界面了。",下面也来实验下:

看到,这时按home键之后,一直按back键则退到ActivityA之后,整个程序就退出了,确实ActivityD就永远回不去了,关于解决方案作者也说了,比较容易理解,这里就不多赘述了,但是让我有一个新的发现,这也许就是singleInstance与singleTask之间最大的区别点吧,基于上面这个过程【重要!】,咱们再来走一遍流程,仔细看着,会有新大路发现:

发现新大路了没?就是消失不见的ActivityD所在的栈并未消失,当再次调它时则会切回来,也就是说这个栈是与APP无关的,不同的APP最终都可以共用这个栈,如果这时再返回,再走一遍流程应该就会创建ActivityD了,因为按back时,已经将ActivityD主动退出栈了,是否是这样了,下面继续:

按back键:

再走一遍:

果真如此,这就是关于lauchmode的不同点,之后会会对最难以区分的SingleTask和SingleInstance进行辨析。

Activity之launchMode理解的更多相关文章

  1. Activity的launchMode和任务栈小结

    对Activity的launchMode的理解一直没有好好总结下,这两天系统总结下launchMode的使用方法: Activity的launchMode属性决定了Activity和应用程序当前任务栈 ...

  2. android开发艺术探索学习 之 结合Activity的生命周期了解Activity的LaunchMode

    转载请标明出处: http://blog.csdn.net/lxk_1993/article/details/50749728 本文出自:[lxk_1993的博客]: 首先还是先介绍下Activity ...

  3. Activity的LaunchMode情景思考

    此链接:http://blog.csdn.net/xiaodongrush/article/details/28597855 1. 有哪几种类型?分别有什么用? http://developer.an ...

  4. Activity的LaunchMode应用场景思考

    本文链接:http://blog.csdn.net/xiaodongrush/article/details/28597855   1. 有哪几种类型?分别有什么用? http://developer ...

  5. Activity生命周期理解

    在Android应用程序运行时,Activity的活动状态由Android以Activity栈的形式管理,当前活动的Activity位于栈顶.随着应用程序之间的切换.关闭等,每个Activity都有可 ...

  6. Activity快速入门理解

    ​ 在Java领域,JBPM和Activity是两个主流的工作流系统,而Activity的出现无疑将会取代JBPM(Activity的开发者就是从Jbpm开发者出来的 1. 1个插件 在Eclipse ...

  7. Activity: launchMode 和 Intent.FLAG_ACTIVITY_CLEAR_TOP

    Activity 的 launchMode: 1. standard: 标准模式 这种启动模式为标准模式,也是默认模式.每当我们启动一个Activity,系统就会相应的创建一个实例,不管这个实例是否已 ...

  8. 1.1 Activity

    1.概念 Application:由多个相关的松散的与用户进行交互Activity组成,通常被打包成apk后缀文件中: Activity:就是被用来进行与用户交互和用来与android内部特性交互的组 ...

  9. Activity 的启动模式

    好久没用过那几种activity的启动模式了,如今看到singletop竟然傻了眼,完全忘记了这几种启动模式的区别!隧将两年前的总结翻出来,通读一遍那晦涩难懂的记录,又理解了一遍,在以前记录的基础上, ...

随机推荐

  1. TortoiseSVN安装和使用(转)

    http://blog.csdn.net/Zhihua_W/article/details/64904692?locationNum=2&fps=1 https://www.cnblogs.c ...

  2. axios.js 在测试机ios7.1的iphone4中不能发送http请求解决方案

    原因:axios使用promise语法,浏览器不支持该语法 解决思路:使浏览器支持promise语法 具体代码: 安装es6-promise,npm i es6-promise -D. 在引入axio ...

  3. Asp.Net Core中完成拒绝访问功能

    很多时候如果用户没有某个菜单的操作权限的话在页面是不应该显示出来的. @if (SignInManager.IsSignedIn(User) && User.IsInRole(&quo ...

  4. ucore 源码剖析

    lab1 源码剖析 从实模式到保护模式 初始化ds,es和ss等段寄存器为0 使能A20门,其中seta20.1写数据到0x64端口,表示要写数据给8042芯片的Output Port;seta20. ...

  5. python安装OpenCV – 4.1.0

    (python3) [jiangshan@localhost ~]$ pip install opencv_python==4.1.0Collecting opencv_python==4.1.0 E ...

  6. [转帖]Nginx Image Module图片缩略图 水印处理模块

    Nginx Image Module图片缩略图 水印处理模块 https://www.cnblogs.com/jicki/p/5546972.html Nginx Image Module图片缩略图 ...

  7. Tomcat报错:No result type specified for result named 'success'

    今天学Struts, tomcat报出了异常信息 Exception starting filter [struts2] Unable to load configuration.还有 No resu ...

  8. Git服务器搭建--ssh/http

    测试环境 Windows 7 Ultimate, 64-bit 6.1.7601, Service Pack 1(实体机,虚拟机VMware的宿主机) VMware® Workstation 7.1. ...

  9. Linux02 cd命令以及绝对路径和相对路径

    一.cd 这是一个非常基本,也是大家常用的命令,用于切换当前目录,他的参数就是要切换的目录的路径,可以是绝对路径,也可以是相对路径. cd /home/keshengtao/ 绝对路径 cd ./pa ...

  10. redis数据库——python使用和django中使用

    为什么要学redis 1.redis是内存 no-sql 数据库,相比mysql等硬盘数据库效率高 2.在内存值配置数据库使用,而不直接使用内存,redis存储的数据是可以管理的 3.memcache ...