文档章节

《Android深入透析》之 浅析Activity启动模式

朵朵和糖糖
 朵朵和糖糖
发布于 2014/10/09 18:48
字数 3148
阅读 154
收藏 0
点赞 0
评论 0

前言:

      Activity的启动模式是一个既基础又容易忽视的问题,但是这个问题有个深刻的认识,对程序员写一个稳定高效的Android程序帮助很大,今天,在B哥引导下,我们对Activity启动模式、Intent Flags做了一番很好的探究,可以这么说,如果你不熟悉或了解Activity的启动模式或者Flags怎么用,今后你在实际开发中,绝对会被困扰,回过头来重新学习这一节,举个例子:有人写出的客户端,为什么崩溃了,底下仍然有一个乃至N个该应用的界面,如果你熟读并且准确理解此章,必然不会出此错误。

探究历程:

     ①   什么是栈

     ②   Activity栈

     ③   Task

     ④  Activity启动模式

     ⑤  Activity栈和Task联系

     ⑥  Intent Flags 

     ⑦  Activity相关属性taskAffinity

 1.    什么是栈

1.1   

       栈是一种常用的数据结构,栈只允许访问栈顶的元素,栈就像一个子弹梭子(如图所示),每次都只能取栈顶上的东西,而对于栈就只能每次访问它的栈顶元素,从而可以达到保护栈顶元素以下的其他元素.”先进后出”或”后进先出”就是栈的一大特点,先进栈的元素总是要等到后进栈的元素出栈以后才能出栈.递归就是利用到了系统栈,暂时保存临时结果,对临时结果进行保护.

 

1.2   定义栈(Stack)

      栈的定义栈(Stack)是限制仅在表的一端进行插入和删除运算的线性表。

(1)  通常称插入、删除的这一端为栈顶(Top),另一端称为栈底(Bottom)。

(2)  当表中没有元素时称为空栈。

(3)  栈为后进先出(Last In First Out)的线性表,简称为LIFO表。栈的修改是按后进先出的原则进行。每次删除(退栈)的总是当前栈中"最新"的元素,即最后插入(进栈)的元素,而最先插入的是被放在栈的底部,要到最后才能弹出。

 

1.3  栈的操作:压栈、弹栈

 

2.   Activity中的栈

       Android的管理主要是通过Activity栈来进行,当一个Activity启动时,系统会根据其配置将它压入到一个特定的栈中,系统处于运行状态。当用户点击返回或则FINISH()了该Activity,那么它便会被从栈中压出,随之摧毁,按照Activity的生命周期可以知道,如果当前显示的栈中的Activity没有被弹出栈(即调用Activity的ondestory方法),那么通过Intent打开一个新的Activity时候,会将新打开的Activity压入到栈顶.  如下 图(1)

 

                                                                   图(1)

3.   Task

       Task就是Activity的任务栈,它简单的说,就是一组以栈的模式聚集在一起的Activity组件集合,记录着用户的行为。(这里只提它和Activity的启动模式来讲)

 

4.   Activity启动模式

属性:android:launchMode  

作用:通过主配置文件AndroidManifest.xml中activity的launchMode  属性决定Activity如何启动。

描述:这里有四种模式,与Intent对象中的Activity Flags的属性(FLAG_ACTIVITY_*变量)共同作用,来决定Activity如何启动来处理Intent。

四种模式:

    ①   "standard"  --默认模式
    ②   "singleTop"
    ③   "singleTask"
    ④   "singleInstance"

以下举例说明它们的区别:

A.    standard

       Activity的默认加载方法,该方法会启动创建一个新的activity实例,同时将该实例压入到栈中(不管该activity是否已经存在在Task栈中,都是采用new操作),并调用这个新Activity的OnCreate()方法。。

例如: 栈中顺序是A B C D ,此时D通过Intent跳转到A,那么栈中结构就变成 A B C D A ,点击返回按钮的 显示顺序是 D C B A,依次摧毁。如下图(2)

       一句话记忆:无论栈中是否已经创建过,它都会创建一个新的并置于栈顶并显示,调用oncreate方法。

       使用场景:默认的Activity的启动模式。

                                                       图(2) 

B.    singleTop

       假设栈内已存在A B C D

       singleTop模式下,分以下两种情况。

1)    当前App应用展示的是D或者栈顶是D,如果这个时候通过Intent需要打开D,那么不会重新创建一个新的D实例,而是调用栈顶的D,并调用它的onNewIntent方法(注意不是oncreate方法,只有创建一个新的实例Activity才会调用Oncreate方法)所以栈中的结构依旧为A B C D。如下图(3)

                                                      图(3)

       思考: 那么,让我们想想,这种情况什么时候会发生呢?好像有点矛盾,当前的显示的或者栈顶的activity,通过Intent或者打开同样的activity,有这种用法和必要吗?

      古语有云:万物皆有其法,android提供了这种用法,那么一定有其道理,至于如何应用和怎样应用,那么有待读者验证,在这里我们只是抛砖引玉,不过可以提供一个这样的场景论证其有效性。

      比如:当前的app栈顶为A,这时候,突然来了个消息通知,这个消息通知需要通过Intent去打开A,那么我们点击这个消息通知打开A的时候,此场景就复现了,它将不会创建一个新的A,而是复用栈顶的A,并执行onNewIntent方法。

2)     当前App应用展示的是D或者栈顶是D,如果这个时候需要打开B,那么由于B不处于栈顶,所以会新建一个B实例并压入到栈中,并调用新创建B的onCreate方法,结构就变成了A B C D B。如下图(4)

                                                     图(4)

      一句话记忆:只有需要打开的A在栈顶,那么不会创建一个新的A,并调用onNewIntent方法,如果需要打开的A不在栈顶,那么不论A在栈中有还是没有,都会创建一个新的A放入栈顶,并执行onCreate方法。

 C.    singleTask

       singleTask模式下,Task栈中有且只能有一个对应Activity的实例。

例如:当前栈的结构为:A B C D。

       此时D通过Intent打开B,则栈的结构变成了:A B。其中的C和D被栈弹出销毁了,也就是说位于B之上的实例都被销毁了。而不会创建一个新的B实例,而是使用栈中原有的B,此时调用原B的onNewIntent()方法。如下图(5),(6)

                                                  图(5)

                                                    图(6)

       经验谈:此模式较为常见,在activity栈中,因为应用需要,通常需要打开多个相同或者不同的Activity,那么这样,Activity栈会越来越大,从而消耗的内存也会越来越大,如果Activity配置了这个属性就无敌了,它会怎么做呢?如果在已经打开A后打开了B C D E…等等,如果这个时候你需要打开A,但又想销毁B C D E…的时候,此属性就满足需求了,这时候你会发现你内存使用就下降了,因为B C D E…已经被销毁。

        经验谈:当然,并不是说设置了singleTask就通用一切了,前面说过了,每种用法都有其自身道理,singleTask想做到的是,显示当前堆栈中已存在的Activity并不重新创建,而是复用,如果堆栈中已存在需要打开的Activity,那么先将此栈中Activity之上的其它Activity销毁,露出已存在的Activity,并执行它的onNewIntent方法。

      当然,如果当前栈中不存在,那就创建一个新的置于栈顶。

      一句话记忆:只要A存在栈内,那么就将A之上的全部销毁(不包含A),同时显示并复用A,执行onNewIntent方法。否则,创建一个新A置于栈顶。

D.   singleInstance

      singleInstance模式下,会创建一个新的Task栈。

      例如:当前栈的结构为:A B C D。

      在D中通过Intent打开E(E的模式为singleInstance),那么会新建一个Task 栈2,栈Task 1中结构依旧为A B C D,栈2中结构为E,此时屏幕中显示E。

      此类模式,通常会在打开另一个App才会使用。比如:打电话,使用平率高,耗资源的应用。在应用中打开微信、新浪微博等客户端。如下图(7)

                                                       图(7)

5.   Activity栈和Task联系

      Task简单的就是一组以栈的模式聚集在一起的Activity组件集合,类似于一个填充了Activity的容器,最先加入的Activity会处于容器最下面,最后加入的处于容器最上面,而从Task中取出Activity是从最顶端先取出,最后取出的是最开始添加Activity,这就是后进先出(Last In First Out)模式,而Activity在Task中的顺序是可以控制的,在Activity跳转时用到Intent Flag可以设置新建activity的创建方式(这里就涉及到了Intent Flag的使用)。

 

6.    Intent Flags 

       Flags: 表示Intent的标志位,常用于Activity的场景中,它和Activity的启动模式有着密切的联系,简单说,flag的有效组合(通常用“|“组合使用)决定如何打开Activity。

下面列举的是和本文主题相关的Flags属性:

(1)——  Intent.FLAG_ACTIVITY_NEW_TASK (默认)

      默认的跳转类型,它会重新创建一个新的Activity。

(2)——  FLAG_ACTIVITY_SINGLE_TOP

      这个FLAG就相当于启动模式中的singletop,请参考singletop说明。

(3)——  FLAG_ACTIVITY_CLEAR_TOP

       这个FLAG启动的Activity会把要启动的Activity之上(包含自身)的Activity全部弹出栈空间。例如:原来栈中的结构是A B C D ,从D中跳转到B,释放顺序为D C B,然后重新创建B置于栈顶,栈中的结构就变为了A B了。(这个方法可以用来关闭多个Activity)

       经验:需要销毁栈中A之前的多个activity,但并不想销毁A,就需要FLAG_ACTIVITY_CLEAR_TOP和FLAG_ACTIVITY_SINGLE_TOP组合使用

(4)——  FLAG_ACTIVITY_CLEAR_TASK

       这个Flag使用的前置条件为:API level 11 以上版本,并且需要与FLAG_ACTIVITY_NEW_TASK一起使用。

此标识,用于释放当前栈中所有的activity,然后再创建新的Activity对象置于栈顶。

       例如栈中原有A B C D,需要从D跳转到B,依次释放D C B A,然后创建B,置于栈顶。 

7.    Activity相关属性taskAffinity

       Activity 中的 android:taskAffinity 这个属性介绍:

       Activity为Task拥有的一个affinity。拥有相同的affinity的Activity理论上属于相同的Task(在用户的角度是相同的“应用程序”)。Task的affinity是由它的根Activity决定的。 
       affinity决定两件事情——Activity重新宿主的Task(参考allowTaskReparenting特性)和使用FLAG_ACTIVITY_NEW_TASK标志启动的Activity宿主的Task。
       默认情况,一个应用程序中的所有Activity都拥有相同的affinity。捏可以设定这个特性来重组它们,甚至可以把不同应用程序中定义的Activity放置到相同的Task中。为了明确Activity不宿主特定的Task,设定该特性为空的字符串。
       如果这个特性没有设置,Activity将从应用程序的设定那里继承下来(参考<application>元素的taskAffinity特性)。应用程序默认的affinity的名字是<manifest>元素中设定的package名。

       android:taskAffinity只有通过标志位为FLAG_ACTIVITY_NEW_TASK的Intent启动Activity时,该Activity的这个属性才会生效,系统才会将具有相同Task亲和力的Task切换到前台,然后启动该Activity,否则该Activity仍然运行在启动它的Task中。

© 著作权归作者所有

共有 人打赏支持
朵朵和糖糖
粉丝 15
博文 6
码字总数 30088
作品 0
朝阳
《Android开发艺术探索》Activity | 生命周期 | 启动模式 | Flags | IntentFilter action data的匹配规则 (一)

说几点 1.文章中大部分(所有吧)的内容都是与《Android开发艺术探索》书中写的内容是一致的,所以可以说是直接copy了一遍(没办法,作者写的太好了)。 2.为什么要copy一遍呢?,主要还是为...

a_zhon ⋅ 05/09 ⋅ 0

Activity启动模式&Task栈

在AMS中,ActivityRecord对应一个Activity,TaskRecord对应一个Task,每个TaskRecord中保存了若干ActivityRecord,TaskRecord由taskId标识,通过getTaskId()可以获取Activity所属的Task。Act...

JasmineBen ⋅ 05/30 ⋅ 0

Activity启动模式的理解

一、理解 Tasks and Back Stack(任务和后台堆栈) 一个 App 对应一个 Task,该app 内的所有 activity 被安排在一个后台堆栈里。 多窗口模式下:一个windows下可能由多个 Task,系统为每个窗...

Agnes2017 ⋅ 05/04 ⋅ 0

Android 面试技能树梳理

相信大家都有面试的经历,相对比面试官的问的一些问题其实都是基础的知识,但就是一些基础的知识我们也不是很完美的回答出来,我们也知道现在的开发人员很多,一家公司一个岗位就会有很多的开...

liu3364575 ⋅ 05/05 ⋅ 0

Android 渗透测试学习手册 第一章 Android 安全入门

第一章 Android 安全入门 作者:Aditya Gupta 译者:飞龙 协议:CC BY-NC-SA 4.0 Android 是当今最流行的智能手机操作系统之一。 随着人气的增加,它存在很多安全风险,这些风险不可避免地被...

apachecn_飞龙 ⋅ 2016/12/06 ⋅ 0

VirtualApk启动插件Activity

插件以APK的形式保存在SD卡上,通过startActivity方式启动Activity需要首先将Activity注册到AndroidManifest.xml,如果没有注册就会出现如下错误。 要实现插件Activity的启动需要解决以下问题...

JasmineBen ⋅ 05/16 ⋅ 0

Android插件化原理(一)Activity插件化

相关文章 Android深入四大组件系列 Android解析AMS系列 Android解析ClassLoader系列 前言 四大组件的插件化是插件化技术的核心知识点,而Activity插件化更是重中之重,Activity插件化主要有三...

刘望舒 ⋅ 05/29 ⋅ 0

Android应用程序窗口(Activity)的窗口对象(Window)的创建过程分析

每一个Activity组件都有一个关联的ContextImpl对象,同时,它还关联有一个Window对象,用来描述一个具体的应用程序窗口。与Activity组件所关联的窗口对象的实际类型为PhoneWindow。 那么创建...

lonamessi ⋅ 05/31 ⋅ 0

Android 【插件化】"偷梁换柱"的高手-VirtualApk源码解析

关于VirtualApk VirtualApk github : https://github.com/didi/VirtualAPK VirtualAPK wiki : https://github.com/didi/VirtualAPK/wiki 工程介绍 工程结构 CoreLibrary是VirtualApk(以下简称......

qq_17250009 ⋅ 04/12 ⋅ 0

Android AIDL浅析及异步调用

AIDL:Android Interface Definition Language,即 Android 接口定义语言。 AIDL 是什么 Android 系统中的进程之间不能共享内存,因此,需要提供一些机制在不同进程之间进行数据通信。 为了使其...

cspecialy ⋅ 05/20 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

BS与CS的联系与区别【简】

C/S是Client/Server的缩写。服务器通常采用高性能的PC、工作站或小型机,并采用大型数据库系统,如Oracle、Sybase、InFORMix或 SQL Server。客户端需要安装专用的客户端软件。 B/S是Brower/...

anlve ⋅ 47分钟前 ⋅ 0

发生了什么?Linus 又发怒了?

在一个 Linux 内核 4.18-rc1 的 Pull Request 中,开发者 Andy Shevchenko 表示其在对设备属性框架进行更新时,移除了 union 别名,这引发了 Linus 的暴怒。 这一次 Linus Torvalds 发怒的原...

问题终结者 ⋅ 今天 ⋅ 0

在树莓派上搭建一个maven仓库

在树莓派上搭建一个maven仓库 20180618 lambo init 项目说明 家里有台树莓派性能太慢。想搭建一个maven私服, 使用nexus或者 jfrog-artifactory 运行的够呛。怎么办呢,手写一个吧.所在这个...

林小宝 ⋅ 今天 ⋅ 0

Spring发展历程总结

转自与 https://www.cnblogs.com/RunForLove/p/4641672.html 目前很多公司的架构,从Struts2迁移到了SpringMVC。你有想过为什么不使用Servlet+JSP来构建Java web项目,而是采用SpringMVC呢?...

onedotdot ⋅ 今天 ⋅ 0

Python模块/包/库安装(6种方法)

Python模块/包/库安装(6种方法) 冰颖机器人 2016-11-29 21:33:26 一、方法1: 单文件模块 直接把文件拷贝到 $python_dir/Lib 二、方法2: 多文件模块,带setup.py 下载模块包(压缩文件zip...

cswangyx ⋅ 今天 ⋅ 0

零基础学习大数据人工智能,学习路线篇!系统规划大数据之路?

大数据处理技术怎么学习呢?首先我们要学习Python语言和Linux操作系统,这两个是学习大数据的基础,学习的顺序不分前后。 Python:Python 的排名从去年开始就借助人工智能持续上升,现在它已经...

董黎明 ⋅ 今天 ⋅ 0

openJdk和sun jdk的区别

使用过LINUX的人都应该知道,在大多数LINUX发行版本里,内置或者通过软件源安装JDK的话,都是安装的OpenJDK, 那么到底什么是OpenJDK,它与SUN JDK有什么关系和区别呢? 历史上的原因是,Ope...

jason_kiss ⋅ 今天 ⋅ 0

梳理

Redux 是 JavaScript 状态容器,提供可预测化的状态管理。 它是JS的状态容器,是一种解决问题的方式,所以即可以用于 react 也可以用于 vue。 需要理解其思想及实现方式。 应用中所有的 stat...

分秒 ⋅ 今天 ⋅ 0

Java 后台判断是否为ajax请求

/** * 是否是Ajax请求 * @param request * @return */public static boolean isAjax(ServletRequest request){return "XMLHttpRequest".equalsIgnoreCase(((HttpServletReques......

JavaSon712 ⋅ 今天 ⋅ 0

Redis 单线程 为何却需要事务处理并发问题

Redis是单线程处理,也就是命令会顺序执行。那么为什么会存在并发问题呢? 个人理解是,虽然redis是单线程,但是可以同时有多个客户端访问,每个客户端会有 一个线程。客户端访问之间存在竞争...

码代码的小司机 ⋅ 今天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部