转载请注明出处: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. 大数据SQL中的Join谓词下推,真的那么难懂?

    听到谓词下推这个词,是不是觉得很高大上,找点资料看了半天才能搞懂概念和思想,借这个机会好好学习一下吧. 引用范欣欣大佬的博客中写道,以前经常满大街听到谓词下推,然而对谓词下推却总感觉懵懵懂懂,并不明白 ...

  2. node 读取文件内容并响应

    node 读取文件内容并响应 const http = require('http'); const fs = require('fs') //创建 Server const server = htt ...

  3. 大白话讲解调用Redis的increment失败原因及推荐使用

    大家在项目中基本都会接触到redis,在spring-data-redis-2.*.*.RELEASE.jar中提供了两个Helper class,可以让我们更方便的操作redis中存储的数据.这两个 ...

  4. 常用的 21 条 Linux 命令,生产力必备

    一.文件和目录 1. cd命令 (它用于切换当前目录,它的参数是要切换到的目录的路径,可以是绝对路径,也可以是相对路径) cd /home 进入 '/ home' 目录 cd .. 返回上一级目录 c ...

  5. 【GIS】GeoServer服务Authkey配置记录

    特别感谢:https://www.cnblogs.com/HandyLi/p/8624507.html 1.服务受控配置 2.授权方式 3.Url模式配置 4.Authkey密钥配置 5.使用 在wm ...

  6. mysql-5.7.30安装

    1.由于在线安装受制于网络环境,所以选择tar包编译安装.      首先去mysql镜像站下载mysql-5.7.30-linux-glibc2.5-x86_64.tar.gz2.上传到linux服 ...

  7. [atAGC051D]C4

    考虑将两次移动作为一个整体,两次移动的效果分为:$s-u$.$u-s$和原地不动 对于从$s$回到$s$路径,必然有前两种效果使用次数相同,假设都为$i$(枚举),那么原地不动的次数$j=\frac{ ...

  8. [atAGC049E]Increment Decrement

    由于每一个操作的逆操作都存在,可以看作将$a_{i}$全部变为0的代价 先考虑第一个问题,即对于确定的$a_{i}$如何处理 如果仅能用第2种操作,定义点$i$的代价为以$i$为左端点或以$i-1$为 ...

  9. 微信小程序的优点(水一篇)

    - 快速的加载 - 更强大的能力 - 原生的体验 - 易用且安全的微信数据开放 - 高效和简单的开发 摘自微信官方文档 https://developers.weixin.qq.com/minipro ...

  10. 基于CarbonData的电信时空大数据探索

    摘要:作为IOT最底层的无线通信网络生成大量与位置相关的数据,用于无线通信网络规划和优化,帮助电信运营商建设更好体验的精品网络,构建万物互联的信息社会. 本文分享自华为云社区<基于CarbonD ...