singleTask和singleInstance区别辨析
上次对lauchMode进行了全面的研究(http://www.cnblogs.com/webor2006/p/3979623.html),其中提到了待研究的东西,就是关于singleTask和singleInstance之间的差别,这里就开始一点点找寻答案。
实际上关于这个网上已经有牛人给出解释了,http://blog.csdn.net/wang_zun_ren/article/details/6823257
下面用实验来验证下,最终来进行总结,依然拿来主义,"拿来不可耻,可耻的是技术不研究透":
现有2个项目,taskA、taskB。taskA负责调用taskB中指定的界面。

taskB中有3个界面,a、b、c,每个界面显示它所在的task id。
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
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
Log.d("cexo", "ActivityB onNewIntent()----taskId:" + getTaskId());
}
@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);
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
Log.d("cexo", "ActivityC onNewIntent()");
}
}
其中taskA就是简单调用taskB中的ActivityB,代码如下:
MainActivity.java:
public class MainActivity extends Activity implements OnClickListener {
private Button button;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d("cexo", "taskA onCreate()----taskId:" + getTaskId());
setContentView(R.layout.activity_main);
button = (Button) findViewById(R.id.button);
button.setOnClickListener(this);
}
@Override
public void onClick(View view) {
startActivity(new Intent("com.testb.launchmodeltest.ActivityB"));//跳转到taskB中的ActivityB界面
}
}
下面会设计一些实验来进行对比,分别在场景一样的情况下将ActivityB的lauchMode分别设置为singleTask和singleInstance做对比,会参考博客,但会更加细化,从这些实验中,最后来总结出两者的区别,下面开始:
实验一:"先运行taskB工程,按如下顺序打开页面:ActivityA---->ActivityB---->ActivityC,来观察栈情况~"
singleTask,运行如下:


可以发现三个界面都处于同一个Task中。
singleInstance:

其中ActivityB新开了一个Task,从这个实验可以发现:
singleTask并非一定是单独成栈的,但是singleInstance一定是单独成栈的
实验二:"此时按Home键回到桌面,再切回到前台,来看下退栈的顺序"
singleTask:

其退栈过程是按先进后出的顺序:ActivityC---->ActivityB---->ActivityA,因为这三个是在同一个栈中。
singleInstance:

由于ActivityB在新的栈里,而当前ActivityC所在栈在最前面,所以先退ActivityC所在栈,其中ActivityA跟它同一栈,但是退完了当前栈之后就没了。在新栈中的ActivityC就木见了,这个需要注意!
实验三:"在实验一中可以发现singleTask的并没有新开一个栈,那如果用taskA去调用taskB的lauchMode为singleTask的ActiviyB界面呢?【在taskB工程之前没有打开过ActivityB界面】"

从这个实验中可以总结:“singleTask在同一个进程里,是不会新建Task的,只有跟打开它的在不同进程,打开该界面时才会单独成栈”
实验四:"在实验三的基础上,在taskB界面上,继续点击跳转到ActivityC界面,然后再看一下退栈的顺序"

从实验结果来看,新打开的ActivityC跟ActivityB在同一个进程,也在同一个栈,其退栈时是先退当前栈,然后再退其它栈。
如果在正式退栈之前,进行一个操作,就是按home键,看是否跟实验二中的singleInstance退栈一样,在退完当前栈之后回不到单独成栈的ActivityB界面了:

从中可以发现,切至home之后,singleTask的ActivityB走了onNewIntent(),并且ActivityC被干掉了,回退一次就退出了,并没有回到taskA,可见当切home之后,其退栈过程并非是先退当前栈,再退其它栈了。那taskA程序在按home键之后,就不受栈管理了,要想回到taskA,则需要点击一下该APP既可,如下:

从实验结果来看,只有选择了这个APP,才会回到相对应的界面,当回退完这个APP之后,就不会回退到其它APP了。
而从作者的总结中也可以体会到:"这说明,SingleTask所标注的Activity在被自身的app调用时,是不新建task的,同时,如果系统中存在了这个SingleTask界面的实例时,会将其所在的task切换到前台,并把SingleTask界面之后开启的其他界面全部关闭(有待考证是否关闭)。"
那如果ActivityB是singleInstance时,同样的操作步骤,在不按home,其结果还是一样么?

从中可以发现ActivityC跟ActivityB不在同一个栈,分别新开了不同的栈,回退时,很退当前栈再退其它栈,确实是的。
同样这时在正式退栈之前,进行一个操作,就是按home键,看结果会是怎样:

从中可以发现,跟singleTask不同的地方,就是直接回到了ActivityC界面,再按一次Back键,就直接退出了,ActivityB就不见了,同样如果要回到taskA所在界面,切在home中选择该进程既可,跟singleTask情况一样。
实验五:"继续基于实验四中最后一个步骤,singleInstance时,切换home界面后,再切回界面时,退一次ActivityC之后就退出桌面了,这时ActivityB就不见了,基于这种场景,再次打开taskB走一遍流程"

从中可以发现,再次打开ActivityB时,走的是onNewIntent(),这说明之前消失的ActivityB所在的Task还存在,再打开时则直接切到前台上来了,这个在http://www.cnblogs.com/webor2006/p/3979623.html也有论证,这也是singleInstance的一个特点。
实验五:验证作者的实验:"a界面被调用,这时按Home键返回到桌面,启动taskA,并调用b界面,这时b界面的taskid与a界面的一致,说明b界面与a界面同属于一个task。如果直接运行taskA调用b界面,b的taskid与taskA的界面的taskid不同,说明在新task中实例化了b界面,由b界面调用c界面,c界面的taskid与b界面一致,说明b与c同属于一个task。"
第一步:启动taskB,调用ActivityA界面
第二步:按home键切换到桌面
第三步:启动taskA,调用taskB中的ActivityB界面,观看此时的ActivityB是否是在新的栈中【从下面实验来看,是在新的栈中,而且跟先前打开的ActivityA在同一个栈】
第四步:再由ActivityB调起ActivityC界面,观察ActivityC跟ActivityB是否在同一个栈中【从下面实验来看,是在同一个栈中,所以可以说明如果是被其它APP调用起来的,它所在栈就是自身APP所在栈】
依照上面的步骤,下面来看下实验结果:

总结:
1、SingleTask所标注的Activity在被自身的app调用时,是不新建task的,而如果是被其它APP调用起来的,它所在栈就是自身APP所在栈;而SingleInstance所标注的Activity在被自身的app调用时,一定会新建task的,前提是该Activity没有被打开过,而如果是被其它APP调用起来的,它所在栈不是自身APP所在栈。
2、如果系统中存在了这个SingleTask界面的实例时,会将其所在的task切换到前台,同样SingleInstance也一样;并把SingleTask界面之后开启的其他界面会全部销毁掉,而SingleInstance是不会的。
3、在按home切回桌面,再切回该APP时,如果app中task中有为singleTask的Activity,它之上的Activity都会被清掉,而如果里面有singleInstance的Activity,而它不是当前界面,则它就无法显示了,但是它所在的task还存在,如果之后再调起这个界面则会起该Activity的onNewIntent()方法。
浙公网安备 33010602011771号