Activity-任务栈和启动模式
为什么需要了解关于Activity的任务栈,其实最直接的体现就是提高用户交互友好性。
举个例子,当我们去浏览一个新闻客户端的时候,我们进入了新闻详情页,在这个页面有相隔两条的新闻标题,当我们去点击这个标题的时候进入了新的新闻详情页时,如果我们不加以控制会导致什么现象?它会创建出n个新闻详细页的Activity实例,导致用户在退出的时候需要推出多个新闻详情activity,这点在用户体验上是非常不好的,当然对于我们自身的程序也是非常不好的,不断的去创建新的Activity必定会消耗一定的内存,久而久之,应用程序会越来越卡甚至崩溃。
在讲Activity任务栈前,我们应该先知道什么是栈?
简单点来理解,可以把栈比作一个开封的箱子,我们可以往里面塞东西,这里假设塞的东西的底面积和箱子的底面积是相同的,那么这些东西就具备有从下往上一定的顺序,当我们想要取出箱子里面的东西时,我们没有办法一下子拿到箱子最底层的东西,我们只能拿到最上面一层的东西,从上往下。
来看下这张图,这里的箱子就是栈,箱子口可以看作是栈的入口与出口,东西代表数据。栈的特点:具有一定的次序,后进先出(越先放入的东西,越晚出来)。
1、Activity任务栈
好了,在了解了什么是栈之后,我们可以开始进入今天的主题了,在Android的官方文档描述中我们可以知道,任务栈也是栈,具有栈的一切特点。
Activity任务栈,顾名思义是存放Activity任务的栈,这里的任务栈为上图箱子,Activity为上图的东西。
当我们每打开一个Activity的时候它会就往Activity任务栈中压入一个Activity,当我们每销毁一个Activity的时候它会从Activity任务栈中弹出一个Activity,由于安卓系统自身的设计,我们只能在手机屏幕上获取当前一个Activity的焦点即栈顶元素(最上面的Activity),其余的Activity会暂居后台等待系统调用。
1.1、关于任务栈的概念:
- 任务栈是用来提升体验而设计的:
- (1) 程序打开时就创建了一个任务栈, 用于存储当前程序的activity,当前程序(包括被当前程序所调用的)所有的activity属于一个任务栈。
- (2) 一个任务栈包含了一个activity的集合, 可以有序的选择哪一个activity和用户进行交互,只有在任务栈栈顶的activity才可以跟用户进行交互。
- (3) 任务栈可以移动到后台,并且保留了每一个activity的状态. 并且有序的给用户列出它们的任务, 而且还不丢失它们状态信息。
- (4) 退出应用程序时,当把所有的任务栈中所有的activity清除出栈时,任务栈会被销毁,程序退出。
1.2、关于任务栈的缺点:
- (1) 每开启一次页面都会在任务栈中添加一个Activity,而只有任务栈中的Activity全部清除出栈时,任务栈被销毁,程序才会退出,这样就造成了用户体验差,需要点击多次返回才可以把程序退出了。
- (2) 每开启一次页面都会在任务栈中添加一个Activity还会造成数据冗余重复数据太多,会导致内存溢出的问题(OOM)。
2、Activity的4种启动方式
为了解决任务栈产生的问题,Android为Activity设计了启动模式,那么下面的内容将介绍Android中Activity的启动模式,这也是最重要的内容之一。
启动模式(launchMode)在多个Activity跳转的过程中扮演着重要的角色,它可以解决是否生成新的Activity实例,是否重用已经存在的Activity实例,是否和其他实例共用一个任务栈。任务栈是一个具有栈结构的对象,一个任务栈可以管理多个Activity,每启动一个应用,也就创建一个与之对应的任务栈。
- Activity一共有以下四种launchMode模式:1、standard 2、singTop 3、singTask 4、singleInstance,我们可以在AndroidManifest.xml配置
<activity>
的android:launchMode属性为以上四种之一即可。 - 2.1、实践是检验真理的唯一标准
- 下面写个实例,有2个Activity,每个Activity都有一个跳转按钮,第一个Activity点击按钮跳转第二个Activity,第二个Activity点击按钮跳转自身。

- 1 package com.lcw.rabbit.activitydemo;
- 2
- 3 import android.app.Activity;
- 4 import android.content.Intent;
- 5 import android.os.Bundle;
- 6 import android.util.Log;
- 7 import android.view.View;
- 8 import android.widget.Button;
- 9
- 10 public class MainActivity extends Activity {
- 11
- 12 private static final String TAG = "Rabbit";
- 13
- 14 private Button mbButton;
- 15
- 16 @Override
- 17 protected void onCreate(Bundle savedInstanceState) {
- 18 super.onCreate(savedInstanceState);
- 19 setContentView(R.layout.activity_main);
- 20
- 21 Log.i(TAG,"第一个Activity,加入任务栈:"+getTaskId());
- 22
- 23 //点击按钮跳转第二个Activity
- 24 mbButton = (Button) findViewById(R.id.bt_button);
- 25 mbButton.setOnClickListener(new View.OnClickListener() {
- 26 @Override
- 27 public void onClick(View v) {
- 28 startActivity(new Intent(MainActivity.this, SecondActivity.class));
- 29 }
- 30 });
- 31
- 32
- 33 }
- 34
- 35 @Override
- 36 protected void onDestroy() {
- 37 super.onDestroy();
- 38 Log.i(TAG, "第一个Activity,退出任务栈:" + getTaskId());
- 39 }
- 40 }


- 1 package com.lcw.rabbit.activitydemo;
- 2
- 3 import android.app.Activity;
- 4 import android.content.Intent;
- 5 import android.os.Bundle;
- 6 import android.util.Log;
- 7 import android.view.View;
- 8 import android.widget.Button;
- 9
- 10 public class SecondActivity extends Activity {
- 11
- 12 private static final String TAG = "Rabbit";
- 13 private Button mbButton1;
- 14 private Button mbButton2;
- 15
- 16 @Override
- 17 protected void onCreate(Bundle savedInstanceState) {
- 18 super.onCreate(savedInstanceState);
- 19 setContentView(R.layout.activity_second);
- 20 Log.i(TAG, "第二个Activity,加入任务栈:" + getTaskId());
- 21 //点击按钮跳转第二个Activity
- 22 mbButton1 = (Button) findViewById(R.id.bt_button1);
- 23 mbButton1.setOnClickListener(new View.OnClickListener() {
- 24 @Override
- 25 public void onClick(View v) {
- 26 startActivity(new Intent(SecondActivity.this, MainActivity.class));
- 27 }
- 28 });
- 29 mbButton2 = (Button) findViewById(R.id.bt_button2);
- 30 mbButton2.setOnClickListener(new View.OnClickListener() {
- 31 @Override
- 32 public void onClick(View v) {
- 33 startActivity(new Intent(SecondActivity.this, SecondActivity.class));
- 34 }
- 35 });
- 36 }
- 37
- 38 @Override
- 39 protected void onDestroy() {
- 40 super.onDestroy();
- 41 Log.i(TAG, "第二个Activity,退出任务栈:" + getTaskId());
- 42 }
- 43 }

实验一:启动模式standard
系统默认的Activity启动模式是standard,我们这里为了检验再设置一下:

- 1 <activity
- 2 android:name=".MainActivity"
- 3 android:launchMode="standard">
- 4 <intent-filter>
- 5 <action android:name="android.intent.action.MAIN" />
- 6 <category android:name="android.intent.category.LAUNCHER" />
- 7 </intent-filter>
- 8 </activity>
- 9 <activity
- 10 android:name=".SecondActivity"
- 11 android:launchMode="standard"></activity>

现在我们进入第一个Activity的时候,点击按钮启动第二个Activity,看下当前任务栈:
当我们点击第二个Activity的按钮,让它跳转自身,看下当前任务栈:
当我们按下Back键,跳转第一个Activity,销毁第二个Activity时,看下当前任务栈:
可以发现,这个任务栈和我们刚上图描述的箱子(Activity任务栈)是一致的,从上往下放东西(Activity),越晚放进去的东西(Activity)在越上面,而后面的t1072则代表当前任务栈的编号ID,ID相同代表它们属于同一个任务栈。
我们从日志文件中也可以看得很清楚:
实验一结论:
在Activity启动模式为standard(默认)的情况下,不管之前有没有Activity实例,每一次启动Activity都会创建一个新的Activity实例,并置于Activity任务栈栈顶。
实验二:启动模式singleTop

- 1 <activity
- 2 android:name=".MainActivity"
- 3 android:launchMode="standard">
- 4 <intent-filter>
- 5 <action android:name="android.intent.action.MAIN" />
- 6 <category android:name="android.intent.category.LAUNCHER" />
- 7 </intent-filter>
- 8 </activity>
- 9 <activity
- 10 android:name=".SecondActivity"
- 11 android:launchMode="singleTop"></activity>

现在我们进入第一个Activity点击按钮跳转第二个Activity,然后再点击按钮跳转自身,看下当前任务栈:
系统日志文件:
然后我们再点击按钮启动第一个Activity,然后点击按钮启动第二个Activity,看下当前任务栈:
系统日志:
实验二结论:
在Activity启动模式为singleTop(栈顶任务唯一)的情况下,如果当前Activity处于栈顶,那么它就不会再去实例化一个新的Activity,当Activity不处于栈顶的时候,会重新实例化一个新的Activity并置于栈顶,此时的任务栈编号为1080。
实验三:启动模式singleTask

- 1 <activity
- 2 android:name=".MainActivity"
- 3 android:launchMode="standard">
- 4 <intent-filter>
- 5 <action android:name="android.intent.action.MAIN" />
- 6 <category android:name="android.intent.category.LAUNCHER" />
- 7 </intent-filter>
- 8 </activity>
- 9 <activity
- 10 android:name=".SecondActivity"
- 11 android:launchMode="singleTask"></activity>

当我们进入第一个Activity点击进入第二个,再启动自身,看下当前任务栈:
系统日志:
现在我们点击启动第一个Activity,再点击启动第二个Activity,看下当前任务栈:
系统日志:
实验三结论:
在Activity启动模式为singleTask(唯一实例)的情况下,当启动Activity的时候,如果当前Activity不存在则实例化一个新的Activity,如果当前Activity在任务栈中已经存在,则会复用这个Activity实例,但这边我们从日志打印可以看出在启动第二个Activity的时候,第一个Activity推出了任务栈,也就意味着当启动模式为singTask的时候,启动已经存在在Activity任务栈中但不在栈顶的Activity时,该Activity会把压在它前面的所有Activity弹出任务栈,此时任务栈编号为1081,属于同一个任务栈。
实验四:启动模式singleInstance

- 1 <activity
- 2 android:name=".MainActivity"
- 3 android:launchMode="standard">
- 4 <intent-filter>
- 5 <action android:name="android.intent.action.MAIN" />
- 6 <category android:name="android.intent.category.LAUNCHER" />
- 7 </intent-filter>
- 8 </activity>
- 9 <activity
- 10 android:name=".SecondActivity"
- 11 android:launchMode="singleInstance"></activity>

现在我们进入第一个Activity点击按钮跳转第二个Activity,再让其跳转自身,看下当前任务栈:
系统日志:
现在点击按钮启动第一个Activity,再点击按钮启动第二个Activity,看下当前任务栈:
系统任务:
点击Back键,直到程序完全退出,看下系统日志:
实验四结论:
在Activity启动模式为singleInstance的情况下,首先我们可以发现的是启动模式为singleInstance的Activity处于不同的任务栈(Task编号不同),并保证不再有其他的Activity实例进入,它还是和singleTask一样保持唯一实例,然后它的退出顺序是不再是根据调用顺序,而是在不同的任务栈中,从上往下退出。
在共用一个Activity实例时,期间发生了什么?
在上诉模式中,当我们的Activity涉及到同一实例的时候,期间Activity做了哪些事情?在Android官方文档中我们可以知道期间虽然没有新实例化一个Activity,但是调用了onNewIntent方法。
现在我们在第二个Activity里添加一个onNewIntent方法:
- 1 @Override
- 2 protected void onNewIntent(Intent intent) {
- 3 super.onNewIntent(intent);
- 4 Log.i(TAG, "第二个Activity,执行onNewIntent");
- 5 }
1、在standard(默认)启动模式下,我们来回的去跳转Activity,看下日志打印,发现是不会调用onNewIntent方法的,因为它不是一个实例。
2、在singleTop模式下,我们从第一个Activity跳转到第二个Activity,再从第二个Activity跳转自身,再跳转第一个Activity,看下日志打印,我们可以发现,当第二个Activity置于栈顶的时候,由于重用了实例,所以调用了onNewIntent方法。
3、当singleTask和singleInstance模式下也是一样的,因为重用了实例,所以会调用onNewIntent方法,且onNewIntent方法是在前一个Activity的onStop方法后(当前ActivityonReStart方法前)立即调用的。
Activity-任务栈和启动模式的更多相关文章
- Android零基础入门第77节:Activity任务栈和启动模式
通过前面的学习,Activity的基本使用都已掌握,接下来一起来学习更高级的一些内容. Android采用任务栈(Task)的方式来管理Activity的实例.当启动一个应用时,Android就会为之 ...
- Activity的四种启动模式任务栈图解
转载本专栏文章,请注明出处,尊重原创 .文章博客地址:道龙的博客 今天带来另一篇Activity的文章--Activity的四种启动模式.该篇文章,会以图文讲解的方式带你彻底掌握Activity的启动 ...
- Android教程 -07 Activity的任务栈和启动模式
Activity是由任务栈管理的,一般情况下一个应用程序只有一个任务栈. 什么是栈? 栈是一种常用的数据结构,栈只允许访问栈顶的元素,栈就像一个杯子,每次都只能取杯子顶上的东西 栈的特点就是先进后出, ...
- Android Activity的4种启动模式详解(示例)
转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/5233269.html 先介绍下Android对Activity的管理,Android采用Task来管理多个A ...
- Activity的四种启动模式
Activity有四种启动模式: 1. standard,默认的启动模式,只要激活Activity,就会创建一个新的实例,并放入任务栈中,这样任务栈中可能同时有一个Activity的多个实例. 2. ...
- Activity的四个启动模式
/** * Activity有四种启动模式(android:launchMode) * 分别是: * 1. standard(默认),可以不停的在栈中创建新的Activity * 2. singleT ...
- Activity的四种启动模式-图文并茂
1.对于使用standard 模式的活动,系统不会在乎这个活动是否已经在返回栈中存在,每次启动都会创建该活动的一个新的实例. 例如A启动A,A再接着启动A,A继续启动A,然后再分别出栈,如图所示 ...
- Activity的四种启动模式和onNewIntent()
转自:http://blog.csdn.net/linghu_java/article/details/17266603 Android中Activity启动模式详解 在Android中每个界面都 ...
- 【安卓面试题】Activity和Task的启动模式有哪些?每种含义是什么?举例说明各自的应用场景
Activity和Task的启动模式有哪些?每种含义是什么?举例说明各自的应用场景 Activity的启动模式 (Launchmode) 有4种 1.standard 默认模式,不需要配置 含义: 启 ...
- 详解Activity的四种启动模式
在Android中每个界面都是一个Activity,切换界面操作其实是多个不同Activity之间的实例化操作.在Android中Activity的启动模式决定了Activity的启动运行方式. Ac ...
随机推荐
- PythonOpenCV--Rtrees随机森林
360确实很个性,哈哈,你个貔貅,只吃不吐! Rtrees介绍!参考链接:http://docs.opencv.org/modules/ml/doc/random_trees.html 原文链接:Py ...
- java连接AD域
import org.springframework.boot.autoconfigure.SpringBootApplication; import java.util.Hashtable; imp ...
- Zbrush 4R7中的镜像功能是怎么使用的?
ZBrush一款3D图形绘制软件,功能十分强大,在雕刻建模的时候镜像工具是我们经常要用到的,它可以方便快捷雕刻对称的模型,那么ZBrush®中怎样镜像呢,本文小编将做详细介绍. ZBrush 3D镜像 ...
- MyBatis 基础入门
MyBatis 是一个半自动化的持久层的框架,能让开发者专注SQL本身 JDBC 连接数据库的硬编码问题,通过config,mapper配置文件解决 Mybatis开发需要关注的文件 l POJO类( ...
- centos7 修改默认语言
vi /etc/locale.conf # 修改成英文 LANG="en_US.UTF-8" # 修改成中文 LANG="zh_CN.UTF-8"
- MyBatis源码分析(各组件关系+底层原理
MyBatis源码分析MyBatis流程图 下面将结合代码具体分析. MyBatis具体代码分析 SqlSessionFactoryBuilder根据XML文件流,或者Configuration类实例 ...
- [SCOI2008]奖励关_状压动归_数学期望
Code: #include<cstdio> #include<algorithm> using namespace std; const int maxn = 20; dou ...
- Day 10 函数
函数 1.什么是函数? 函数就是具备某一功能的工具,事先将工具准备好就是函数的定义,遇到应用场景拿来就用就是函数的调用 2.为何用函数? 如果不使用函数,写程序会遇到这三个问题 1.程序冗长 2.程序 ...
- Docker:分布式系统的软件工程革命(上)
转自:http://cxwangyi.github.io/story/docker_revolution_1.md.html Docker:分布式系统的软件工程革命(上) 作者:王益 最后更新:201 ...
- [JSOI2018]战争(闵可夫斯基和)
害怕,可怜几何题 果然不会 题目就是说给你两个凸包,每次询问给你一个向量 \(c\) 问你能不能从两个凸包 \(A\) , \(B\) 里分别找到一个点 \(a\) , \(b\) 满足 \(a+c= ...