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

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

singleTop:

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

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

实验一:

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

ActivityA.java:

public class ActivityA extends Activity implements OnClickListener {

    private Button button;

    @Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("cexo", "ActivityA onCreate()----taskId:" + getTaskId());
setContentView(R.layout.activity_a);
button = (Button) findViewById(R.id.button);
button.setOnClickListener(this);
} @Override
public void onClick(View view) {
Intent intent = new Intent(this, ActivityB.class);
startActivity(intent);
}
}

ActivityB.java:

public class ActivityB extends Activity implements OnClickListener {
private Button button; @Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("cexo", "ActivityB onCreate()----taskId:" + getTaskId());
setContentView(R.layout.activity_b);
button = (Button) findViewById(R.id.button);
button.setOnClickListener(this);
} @Override
public void onClick(View view) {
Intent intent = new Intent(this, ActivityC.class);
startActivity(intent);
}
}

ActivityC.java:

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

其布局为:

activity_a.xml:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" > <TextView
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="ActivityA" /> <Button
android:id="@+id/button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:text="go ActivityB" /> </RelativeLayout>

activity_b.xml:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" > <TextView
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="ActivityB" /> <Button
android:id="@+id/button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:text="go ActivityC" /> </RelativeLayout>

activity_c.xml:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" > <TextView
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="ActivityC" /> </RelativeLayout>

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

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.launchmodeltest"
android:versionCode="1"
android:versionName="1.0" > <uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="18" /> <application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name="com.example.launchmodeltest.ActivityA"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="com.example.launchmodeltest.ActivityB" />
<activity android:name="com.example.launchmodeltest.ActivityC" />
</application> </manifest>

运行如下:

输出日志如下:

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

添加一个跳转的按钮:

ActivityC.java:

public class ActivityC extends Activity implements OnClickListener {
private Button button; @Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("cexo", "ActivityC onCreate()----taskId:" + getTaskId());
setContentView(R.layout.activity_c);
button = (Button) findViewById(R.id.button);
button.setOnClickListener(this);
} @Override
public void onClick(View view) {
Intent intent = new Intent(this, ActivityC.class);
startActivity(intent);
}
}

activity_c.xml:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" > <TextView
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="ActivityC" /> <Button
android:id="@+id/button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:text="go ActivityC" /> </RelativeLayout>

运行如下:

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

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

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

实验二:

先看下博客原文描述:

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

ActivityC.java:

public class ActivityC extends Activity implements OnClickListener {
private Button button; @Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("cexo", "ActivityC onCreate()----taskId:" + getTaskId());
setContentView(R.layout.activity_c);
button = (Button) findViewById(R.id.button);
button.setOnClickListener(this);
} @Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
Log.d("cexo", "ActivityC onNewIntent()");
} @Override
public void onClick(View view) {
Intent intent = new Intent(this, ActivityB.class);
startActivity(intent);
}
}

见证奇迹的时刻到了:

运行日志如下:

所以,对于这个模式所带来的效果就是:如果要跳转的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:

public class MainActivity extends Activity implements OnClickListener {

    private Button button;

    @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("cexo", "MainActivity onCreate()----taskId:" + getTaskId());
setContentView(R.layout.activity_main);
button = (Button) findViewById(R.id.button);
button.setOnClickListener(this);
} @Override
public void onClick(View view) {
// 调用另外一个工程的ActivityB
startActivity(new Intent("com.example.launchmodeltest.ActivityB"));
} }

activity_main.xml:

<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" > <TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello_world" /> <Button
android:id="@+id/button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:text="go ActivityB" /> </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:

public class ActivityD extends Activity {

    @Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("cexo", "ActivityD onCreate()----taskId:" + getTaskId());
setContentView(R.layout.activity_d);
} @Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
Log.d("cexo", "ActivityD onNewIntent()");
} }

activity_d.xml:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" > <TextView
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="ActivityD" /> </RelativeLayout>

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

ActivityC.java:

public class ActivityC extends Activity implements OnClickListener {
private Button button; @Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("cexo", "ActivityC onCreate()----taskId:" + getTaskId());
setContentView(R.layout.activity_c);
button = (Button) findViewById(R.id.button);
button.setOnClickListener(this);
} @Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
Log.d("cexo", "ActivityC onNewIntent()");
} @Override
public void onClick(View view) {
Intent intent = new Intent(this, ActivityD.class);
startActivity(intent);
}
}

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

可以看到,跳转到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. iOS底层框架浅析

    1.简介 IOS是由苹果公司为iPhone.iPod touch和iPad等设备开发的操作系统. 2.知识点 iPhone OS(现在叫iOS)是iPhone, iPod touch 和 iPad 设 ...

  2. 服务发现框架选型,Consul还是Zookeeper还是etcd

    背景 本文并不介绍服务发现的基本原理.除了一致性算法之外,其他并没有太多高深的算法,网上的资料很容易让大家明白上面是服务发现. 想直接查看结论的同学,请直接跳到文末. 目前,市面上有非常多的服务发现工 ...

  3. Sap MM 定义物料号码范围

    Sap里面的物料编号可以设置内部给号或者外部给号,外部的意思就是通过手动输入,内部就是系统自动根据号码段分配. 物料号是根据物料类型定义范围的. 笔记 作者:明光烁亮 出处:http://www.cn ...

  4. k8s SLUB: Unable to allocate memory on node -1 错误

    Try to Fix Two Linux Kernel Bugs While Testing TiDB Operator in K8sWed, May 1, 2019 Wenbo Zhang Auth ...

  5. 最新 博盾习言java校招面经 (含整理过的面试题大全)

    从6月到10月,经过4个月努力和坚持,自己有幸拿到了网易雷火.京东.去哪儿. 博盾习言等10家互联网公司的校招Offer,因为某些自身原因最终选择了 博盾习言.6.7月主要是做系统复习.项目复盘.Le ...

  6. perl删除文件前几列

    perl oneline 快速删除文件的前两列代码如下 6 perl -lane 'print join("\t",@F[2..$#F])' test.txt 输出效果

  7. PHP,Excel导出换行

    // 有id,才算真的有发票数据 if ($v['b_invoice_id']) { $v['b_invoice_info'] = json_decode($v['b_invoice_json'],t ...

  8. 《Mysql - Mysql 是如何保证主备一致的?》

    一:Mysql 主备的基本原理? - 主备切换流程(M-S 架构) -  - 在状态 1 中,客户端的读写都直接访问节点 A,而节点 B 是 A 的备库,只是将 A 的更新都同步过来,到本地执行. - ...

  9. 修改主机名和修改主机映射和ssh免登陆

    1.修改主机名 vim /etc/sysconfig/network NETWORKING=yes HOSTNAME=cc3 2.修改主机映射 vi /etc/hosts 127.0.0.1 loca ...

  10. python学习-43 装饰器 -- 函数闭包2

    函数闭包为函数加上认证功能 1.登陆账号 user_dic ={'username':None,'login':False} def auth_func(func): def wrapper(*arg ...