Activity之launchMode理解
对于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理解的更多相关文章
- Activity的launchMode和任务栈小结
对Activity的launchMode的理解一直没有好好总结下,这两天系统总结下launchMode的使用方法: Activity的launchMode属性决定了Activity和应用程序当前任务栈 ...
- android开发艺术探索学习 之 结合Activity的生命周期了解Activity的LaunchMode
转载请标明出处: http://blog.csdn.net/lxk_1993/article/details/50749728 本文出自:[lxk_1993的博客]: 首先还是先介绍下Activity ...
- Activity的LaunchMode情景思考
此链接:http://blog.csdn.net/xiaodongrush/article/details/28597855 1. 有哪几种类型?分别有什么用? http://developer.an ...
- Activity的LaunchMode应用场景思考
本文链接:http://blog.csdn.net/xiaodongrush/article/details/28597855 1. 有哪几种类型?分别有什么用? http://developer ...
- Activity生命周期理解
在Android应用程序运行时,Activity的活动状态由Android以Activity栈的形式管理,当前活动的Activity位于栈顶.随着应用程序之间的切换.关闭等,每个Activity都有可 ...
- Activity快速入门理解
在Java领域,JBPM和Activity是两个主流的工作流系统,而Activity的出现无疑将会取代JBPM(Activity的开发者就是从Jbpm开发者出来的 1. 1个插件 在Eclipse ...
- Activity: launchMode 和 Intent.FLAG_ACTIVITY_CLEAR_TOP
Activity 的 launchMode: 1. standard: 标准模式 这种启动模式为标准模式,也是默认模式.每当我们启动一个Activity,系统就会相应的创建一个实例,不管这个实例是否已 ...
- 1.1 Activity
1.概念 Application:由多个相关的松散的与用户进行交互Activity组成,通常被打包成apk后缀文件中: Activity:就是被用来进行与用户交互和用来与android内部特性交互的组 ...
- Activity 的启动模式
好久没用过那几种activity的启动模式了,如今看到singletop竟然傻了眼,完全忘记了这几种启动模式的区别!隧将两年前的总结翻出来,通读一遍那晦涩难懂的记录,又理解了一遍,在以前记录的基础上, ...
随机推荐
- Python扫描器-端口扫描
结合渗透测试最常见就是单个域名扫指纹,自动子域名查找.获取所有子域名的IP,自动C段IP查找相同子域名,利用有效IP扫端口. 常见端口库扫描 service_list = { 21:"FTP ...
- NGUI无法显示
早上起来发现 ,NGUI无法显示,后来发现是场景Camera的 depth =0 : 要设置depth=-1. 原来相机之间也有渲染层级
- Andrew Ng机器学习课程9-补充
Andrew Ng机器学习课程9-补充 首先要说的还是这个bias-variance trade off,一个hypothesis的generalization error是指的它在样本上的期望误差, ...
- PHP中NULL和‘'的区别
PHP中NULL和‘'区别 null的类型是null,""的类型是string 所以是不同东西 <pre>$a=22;unset($a);var_dump($a);&l ...
- Mowing the Lawn【线性dp + 单调队列优化】
题目链接:https://ac.nowcoder.com/acm/contest/2652/G 题目大意:与上一篇博客 烽火传递 差不多. 1.一共n头羊,若超过m头连续的羊在一起,就会集体罢工,每头 ...
- [DFS]排队(间隔排列)-C++
Description 小Q是班长.在校运动会上,小Q班要进行队列表演.小Q要选出2*N名同学编队,每人都被编上一个号,每一个从1到N的自然数都被某2名同学佩戴,现在要求将他们排成一列,使两个编号为1 ...
- [HTTPS] - 请求API失败(Could not create SSL/TLS secure channel)之解决
背景 在单元测试中请求 HTTPS API 失败. 异常 Result StackTrace: at System.Web.Services.Protocols.WebClientProtocol. ...
- 《Mysql - 幻读》
一:准备 - 为了深入了解幻读,准备数据. CREATE TABLE `t` ( `id` ) NOT NULL, `c` ) DEFAULT NULL, `d` ) DEFAULT NULL, PR ...
- Ribbon【自定义客户端】
Ribbon的加载策略是懒加载,即第一次请求的时候才加载对应上下文,正是这个原因,很多时候第一次调用显得很慢,甚至会超时,所以,可以通过指定ribbon具体服务名称来开启饿加载,即在工程启动的时候,加 ...
- STL源码剖析——iterators与trait编程#4 iterator源码
在前两节介绍了迭代器的五个相应类型,并讲述如何利用traits机制提取迭代器的类型,但始终是把iteartor_traits类分割开来讨论,这影响我们的理解,本节将给出iteator的部分源码,里面涵 ...