转载请注明出处:http://blog.csdn.net/sinat_14849739/article/details/78072401
本文出自Shawpoo的专栏
我的简书:简书

一、为什么需要启动模式

在Android开发中,我们都知道,在默认的情况下,如果我们启动的是同一个Activity的话,系统会创建多个实例并把它们一一放入任务栈中。当我们点击返回(back)键,这些Activity实例又将从任务栈中一一移除,遵循的原则是“后进先出”(先进后出)。

这里我们考虑一个问题,当我们多次启动同一个Activity,系统也会创建多个实例放入任务栈中,这样岂不是很耗费内存资源?为了解决这一问题,Android为Actiivty提供了启动模式。

Activity的启动模式有四种:standard、singleTop、singleTask和singleInstance

二、启动模式的分类

1、standard:标准模式

这种启动模式为标准模式,也是默认模式。每当我们启动一个Activity,系统就会相应的创建一个实例,不管这个实例是否已经存在。这种模式,一个栈中可以有多个实例,每个实例也都有自己的任务栈。而且是谁启动了此Activity,那么这个Activity就运行在启动它的Activity所在的栈中。

  • Manifest中配置:
    对于标准模式,android:launchMode=”standard”可以不写,因为默认就是standard模式。
<activity
android:name=".StandardActivity"
android:launchMode="standard" >
</activity>
  • 使用案例:

MainActivity有一个按钮,点击按钮会打开StandardActivity。打开StandardActivity也有一个按钮,点击也是启动一个StandardActivity。并且我们在onCreate()方法中打印TaskId和hashCode值。
打开步骤:MainActivity->StandardActivity->StandardActivity->StandardActivity

MainActivity:

public class MainActivity extends AppCompatActivity {

    private static final String TAG = MainActivity.class.getSimpleName();

    @Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_demo); findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
StandardActivity.open(MainActivity.this);
}
}); Log.e(TAG, "———onCreate(): TaskId: " + getTaskId() +", hashCode: " + hashCode()); } }

StandardActivity :

/**
* 启动模式:Standard(标准模式)
*/ public class StandardActivity extends AppCompatActivity { private static final String TAG = StandardActivity.class.getSimpleName(); @Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_launch_mode); findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
open(StandardActivity.this);
}
}); Log.e(TAG, "———onCreate(): TaskId: " + getTaskId() +", hashCode: " + hashCode());
} public static void open(Context context) {
context.startActivity(new Intent(context, StandardActivity.class));
} }

控制台打印log如下:

通过案例的log分析,可以得出标准模式下,每当打开一次Activity就会创建一个新的实例,因为hashCode值都不同,而且都创建在启动它的Activity所属的任务栈中,也就是MainActivity所在的任务栈中,因为它们的任务栈Id一致。

  • 分析总结:

标准模式下,只要启动一次Activity,系统就会在当前任务栈新建一个实例。

  • 使用场景:

正常的去打开一个新的页面,这种启动模式使用最多,最普通 。

2、singleTop:栈顶复用模式

这种启动模式下,如果要启动的Activity已经处于栈的顶部,那么此时系统不会创建新的实例,而是直接打开此页面,同时它的onNewIntent()方法会被执行,我们可以通过Intent进行传值,而且它的onCreate(),onStart()方法不会被调用,因为它并没有发生任何变化。

  • Manifest中配置:
 <activity
android:name=".SingleTopActivity"
android:launchMode="singleTop">
</activity>
  • 使用案例:

MainActivity仍然是一个按钮,点击按钮打开SingleTopActivity,SingleTopActivity有两个按钮,一个是打开SingleTopActivity,一个是打开OtherActivity,OtherActivity有一个按钮,点击按钮可以打开SingleTopActivity。而且我们在onCreate()、onNewIntent()打印taskId和hashCode值。

打开步骤:MainActivity->SingleTopActivity->SingleTopActivity->OtherActivity->SingleTopActivity->SingleTopActivity

MainActivity:

public class MainActivity extends AppCompatActivity {

    private static final String TAG = MainActivity.class.getSimpleName();

    @Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main_demo); findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
SingleTopActivity.open(MainActivity.this);
}
}); Log.e(TAG, "———onCreate(): TaskId: " + getTaskId() +", hashCode: " + hashCode()); }

SingleTopActivity :

/**
* 启动模式:栈顶复用模式
*/ public class SingleTopActivity extends AppCompatActivity { private static final String TAG = SingleTopActivity.class.getSimpleName(); @Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_launch_mode); findViewById(R.id.btn).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
open(SingleTopActivity.this);
}
});
findViewById(R.id.btn_other).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
OtherActivity.open(SingleTopActivity.this);
}
}); Log.e(TAG, "———onCreate(): TaskId: " + getTaskId() +", hashCode: " + hashCode());
} @Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
Log.e(TAG, "———onNewIntent(): TaskId: " + getTaskId() +", hashCode: " + hashCode());
} public static void open(Context context) {
context.startActivity(new Intent(context, SingleTopActivity.class));
} }

OtherActivity:

public class OtherActivity extends AppCompatActivity {

    private static final String TAG = OtherActivity.class.getSimpleName();

    @Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_other); findViewById(R.id.btn_singleTop).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
SingleTopActivity.open(OtherActivity.this);
}
}); Log.e(TAG, "———onCreate(): TaskId: " + getTaskId() +", hashCode: " + hashCode());
} public static void open(Context context) {
context.startActivity(new Intent(context, OtherActivity.class));
} }

控制台打印log如下:

首先,因为它们的taskId值都相同,所以它们同属于一个任务栈。其次,第一次启动SingleTopActivity的时候会执行onCreate()方法新建一个实例,然后再次启动SingleTopActivity页面会回调onNewIntent(),说明没有创建新的实例,而且hashCode值没有发生改变。此时我们继续打开另一个Activity,这时OtherActivity处于栈顶,我们继续启动SingleTopActivity,这时发现又是执行了onCreate(),说明又重新创建了新的实例,当我们继续启动SingleTopActivity,发现回调了onNewIntent(),同样hashCode值没有发生改变,证明没有重新创建实例。

  • 分析总结:

通过上述案例归纳为以下三点:

1、当前栈中已有该Activity的实例并且该实例位于栈顶时,不会创建实例,而是复用栈顶的实例,并且会将Intent对象传入,回调onNewInten()方法;
2、当前栈中已有该Activity的实例但是该实例不在栈顶时,其行为和standard启动模式一样,依然会创建一个新的实例;
3、当前栈中不存在该Activity的实例时,其行为同standard启动模式。

  • 使用场景:

这种模式应用场景的话,假如一个新闻客户端,在通知栏收到了3条推送,点击每一条推送会打开新闻的详情页,如果为默认的启动模式的话,点击一次打开一个页面,会打开三个详情页,这肯定是不合理的。如果启动模式设置为singleTop,当点击第一条推送后,新闻详情页已经处于栈顶,当我们第二条和第三条推送的时候,只需要通过Intent传入相应的内容即可,并不会重新打开新的页面,这样就可以避免重复打开页面了。

3、singleTask:站内复用模式

在这个模式下,如果栈中存在这个Activity的实例就会复用这个Activity,不管它是否位于栈顶,复用时,会将它上面的Activity全部出栈,因为singleTask本身自带clearTop这种功能。并且会回调该实例的onNewIntent()方法。其实这个过程还存在一个任务栈的匹配,因为这个模式启动时,会在自己需要的任务栈中寻找实例,这个任务栈就是通过taskAffinity属性指定。如果这个任务栈不存在,则会创建这个任务栈。不设置taskAffinity属性的话,默认为应用的包名。

  • 使用案例:

MainActivity仍然是一个按钮,点击按钮打开SingleTaskActivity,SingleTaskActivity有两个按钮,一个是打开SingleTaskActivity,另一个同样是打开OtherActivity,OtherActivity有一个按钮,点击按钮可以打开SingleTaskActivity。同样我们在onCreate()、onNewIntent()打印taskId和hashCode值。

打开步骤:MainActivity->SingleTaskActivity->SingleTaskActivity->OtherActivity->SingleTaskActivity->SingleTaskActivity

代码和SingleTop基本一样,只有Manifest中配置不同,这里不再赘述。

1、不设置taskAffinity属性,也就是默在同一个任务栈中。

Manifest中配置:

<activity
android:name=".SingleTaskActivity"
android:launchMode="singleTask">
</activity>

控制台打印log如下:

首先,因为发现它们的taskId值都相同,所以它们同属于一个任务栈。其次,第一次启动SingleTaskActivity的时候会执行onCreate()方法新建一个实例,然后再次启动SingleTaskActivity页面会回调onNewIntent(),说明没有创建新的实例,而且hashCode值没有发生改变。此时我们继续打开另一个Activity,然后继续启动SingleTaskActivity,这时发现仍然只回调onNewIntent(),说明没有创建新的实例,当我们继续启动SingleTaskActivity,仍然只是回调了onNewIntent(),此过程中发现hashCode值始终没有发生改变,证明引用都是同一个的实例。

2、设置taskAffinity属性,singleTask所在的Activity与启动它的Activity处于不同的任务栈中。

<activity
android:name=".SingleTaskActivity"
android:launchMode="singleTask"
android:taskAffinity="${applicationId}.singleTask">
</activity>

指定了taskAffinity后,我们发现除了taskId有区别外,其他调用基本没有什么区别。因为MainActivity没有指定taskAffinity属性,默认为包名,与SingleTaskActivity不同,所以在启动SingleTaskActivity时,发现这个栈不存在,系统首先会创建这个栈然后将SingleTaskActivity压入栈中。之后我们发现只要栈中存在SingleTaskActivity这个实例,就会直接引用。

3、通过adb shell dumpsys activity activities查看当前运行的Activity

执行完上面的步骤,我们通过上面的信息得出,发现只有MainActivity和SingleTaskActivity在运行,而且也可以看出确实有1909和1910两个任务栈。那OtherActivity哪去了?那是因为SingleTaskActivity具有ClearTop的功能,当复用SingleTashActivity的时候会将栈中SingleTaskActivity之上的Activity全部清掉,所以OtherActivity其实是被销毁了。

  • 分析总结:

在复用的时候,首先会根据taskAffinity去找对应的任务栈:
1、如果不存在指定的任务栈,系统会新建对应的任务栈,并新建Activity实例压入栈中。
2、如果存在指定的任务栈,则会查找该任务栈中是否存在该Activity实例
      a、如果不存在该实例,则会在该任务栈中新建Activity实例。
      b、如果存在该实例,则会直接引用,并且回调该实例的onNewIntent()方法。并且任务栈中该实例之上的Activity会被全部销毁。

  • 使用场景:

SingleTask这种启动模式最常使用的就是一个APP的首页,因为一般为一个APP的第一个页面,且长时间保留在栈中,所以最适合设置singleTask启动模式来复用。

4、singleInstance:单实例模式

单实例模式,顾名思义,只有一个实例。该模式具备singleTask模式的所有特性外,与它的区别就是,这种模式下的Activity会单独占用一个Task栈,具有全局唯一性,即整个系统中就这么一个实例,由于栈内复用的特性,后续的请求均不会创建新的Activity实例,除非这个特殊的任务栈被销毁了。以singleInstance模式启动的Activity在整个系统中是单例的,如果在启动这样的Activiyt时,已经存在了一个实例,那么会把它所在的任务调度到前台,重用这个实例。

  • Manifest中配置:
<activity
android:name=".SingleInstanceActivity"
android:launchMode="singleInstance">
</activity>
  • 使用案例:

使用上面同样的代码进行测试:

通过测试发现,在第一次打开SingleInstanceActivity的时候,由于系统不存在该实例,所以系统会新建一个任务栈来存放该Activity实例,而且只要打开过一次该Activity,后面无论什么时候再次启动该Activity,都会直接引用第一次创建的实例,而且会回调该实例的onNewIntent()方法。

  • 分析总结:

启动该模式Activity的时候,会查找系统中是否存在:
1、不存在,首先会新建一个任务栈,其次创建该Activity实例。
2、存在,则会直接引用该实例,并且回调onNewIntent()方法。
特殊情况:该任务栈或该实例被销毁,系统会重新创建。

  • 使用场景:

很常见的是,电话拨号盘页面,通过自己的应用或者其他应用打开拨打电话页面 ,只要系统的栈中存在该实例,那么就会直接调用。

三、总结

在使用APP过程中,不可避免页面之间的跳转,那么就会涉及到启动模式。其实在对界面进行跳转时,Android系统既能在同一个任务中对Activity进行调度,也能以Task(任务栈)为单位进行整体调度。在启动模式为standard或singleTop时,一般是在同一个任务中对Activity进行调度,而在启动模式为singleTask或singleInstance是,一般会对Task进行整体调度。

 

转 Android中Activity的启动模式(LaunchMode)和使用场景的更多相关文章

  1. Android中Activity的启动模式(LaunchMode)和使用场景

    一.为什么需要启动模式在Android开发中,我们都知道,在默认的情况下,如果我们启动的是同一个Activity的话,系统会创建多个实例并把它们一一放入任务栈中.当我们点击返回(back)键,这些Ac ...

  2. Android中Activity的启动模式

    简介 Android中的活动启动方式分为4种:standard, singleTop, singleTask, singleInstance.可以在AndroidManifest.xml中通过给< ...

  3. 【Android学习入门】Android中activity的启动模式

    启动模式简单地说就是Activity启动时的策略,在Androidmanifest.xml文件中的标签android:launchMode属性设置,在Android中Activity共有四种启动模式分 ...

  4. Android中Activity的启动模式和使用场景

    一.为什么需要启动模式 在Android开发中,我们都知道,在默认的情况下,如果我们启动的是同一个Activity的话,系统会创建多个实例并把它们一一放入任务栈中.当我们点击返回(back)键,这些A ...

  5. 【android】android中activity的启动模式

    在AndroidManifest.xml中配置 <activity android:label="第二个应用" android:name=".Demo2Activi ...

  6. Android之Activity的启动模式

    启动模式有4种,分别为:1.standard(默认)  -- 标准2.singleTop  -- 单顶3.singleTask -- 单任务4.singleInstance: -- 单例  -- 如果 ...

  7. Android中Activity启动模式详解

    在Android中每个界面都是一个Activity,切换界面操作其实是多个不同Activity之间的实例化操作.在Android中Activity的启动模式决定了Activity的启动运行方式. An ...

  8. android Activity的启动模式

    Android中Activity启动模式详解   在Android中每个界面都是一个Activity,切换界面操作其实是多个不同Activity之间的实例化操作.在Android中Activity的启 ...

  9. Android开发9——Activity的启动模式

    在Android中每个界面都是一个Activity,切换界面操作其实是多个不同Activity之间的实例化操作.在Android中Activity的启动模式决定了Activity的启动运行方式. 一. ...

随机推荐

  1. kubernetes创建用户

    创建k8s User Account 使用openssl方法创建普通用户 准备工作 1 2 3 4 mkdir /root/pki/ 将k8s ca.pem  ca-key.pem 证书拷贝到此目录 ...

  2. C 数组类型语法总结

    数组类型语法总结 数组指针 和 指针数组 区分 数组指针是一个指针,只对应类型的数组.指针数组是一个数组,其中每个元素都是指针 数组指针遵循指针运算法则.指针数组拥有c语言数组的各种特性 数组类型重命 ...

  3. Pip安装Django超时(time out)解决方法

    (ll_env)learning_log$  pip install Django  执行该命令,始终报错,如上图 解决方法如下: pip install  -i http://pypi.douban ...

  4. idea离线安装lombok插件

    1.查看自己idea版本,2019.1.2,必须安装相同版本的插件 2.从http://plugins.jetbrains.com/plugin/6317-lombok-plugin中下载对应版本的l ...

  5. 『与善仁』Appium基础 — 7、ADB Shell命令的使用

    目录 1.查看进程 2.查看实时资源占用情况 3.查看进程 UID 4.其它ADB Shell命令说明 Android系统是基于 Linux 内核的,也就是说Android系统的底层是Linux系统. ...

  6. Python 爬取 拉钩

    ... from urllib import request from urllib import parse from urllib.error import URLError import jso ...

  7. [luogu4331]数字序列

    令$a'_{i}=a_{i}+n-i$.$b'_{i}=b_{i}+n-i$,代价仍然是$\sum_{i=1}^{n}|a'_{i}-b'_{i}|$,但条件变为了$b'_{i}\le b'_{i+1 ...

  8. [atARC083F]Collecting Balls

    考虑一个构造,对于坐标$(x,y)$,连一条$x$到$y$的边(注意:横坐标和纵坐标即使权值相同也是不同的点),之后每一个连通块独立,考虑一个连通块内部: 每一个点意味着一次删除操作,每一个边意味着一 ...

  9. [JS高程]JavaScript中的RegExp对象

    目录 1. RegExp 1.1 表示格式: 1.2 匹配模式: 1.3 RegExp 实例属性 1.4 RegExp 实例方法 1.4.1 exec() 1.4.1.1 基本用法 1.4.1.2 e ...

  10. c语言是如何解析表达式语句"2+3*4;"的?

    1.  要编译的测试代码: int main(void) { 2+3*4; } 2. 词法分析 词法分析将字符变成token,其中很重要的是token的类型,如字符2的token类型为TK_NUM,这 ...