引言
当面试官说请你介绍一下activity启动模式,大多数人都能整两句,什么栈顶复用啊栈内复用啊,不过,你确定你真的懂启动模式吗?
如果你能回答出下面的问题,那么你可以直接退出当前界面。
假设有如下四个activity:
A(standard)
B(singleTop)
C(singleTask)
D(singleInstance)
它们的启动顺序依次是ABCDABCD,请描述activity栈内变化。
基于交互的分析
例:
1,用户在主屏幕中点击应用的图标启动应用后,弹出了第一Activity界面:A,并依次打开了如下界面 A -> B -> C -> D。
2,此时按下home键返回主屏幕,然后重新点击图标启动这个应用,我们会发现弹出的界面还是 D 而不是界面 A。
3,当我们连续点击返回键时,应用中界面会按照启动顺序反向的依次展示,也就是D -> C -> B -> A -> 主屏幕。
通过这个例子我们可以知道Android系统会为应用暂时性的保存一组Activity启动链,记录启动顺序,这就引出了第一个概念:任务。
任务
先说下任务的定义,Android官方把上述这种为了完成某些工作而链式启动的一系列Activity合集称之为 任务。
我们都知道每个Activity都是互相独立的界面,正是有了任务这样的概念,多个Activity才能够关联起来组成一个完整的应用。
任务可以同时存在多个吗
当然可以!
例:平时我们使用手机经常会在刷微博和聊微信来回切换,每次切换系统都会为我们保存上一次离开的状态。
任务里Activity必须是来自同一个应用吗
当然不是!
例:当我们在社交软件设置用户头像时一般会有拍照和相册两个选项,选择拍照会跳转到摄像机软件,选择相册会跳到系统相册软件。通过这几个软件之间的共同合作完成了一次任务。
任务中的根Activity
通常情况下,我们都是通过设备主屏幕点击应用图标启动应用的,同理设备主屏幕也是大多数任务的起点,而应用中的入口Activity就是这个任务的根Activity,根Activity的声明方式你一定特别熟悉:
<activity android:name=".HelloActivity""> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter></activity>
当用户点击主屏幕应用图标打开应用时,如果该应用最近未曾被使用过,则会创建一个任务,并将该应用中的入口Activity作为任务中的根Activity打开。反之直接把该应用所在的任务调出来置于前台即可。
了解完任务之后,我们就大概知道了上述几个例子中Android系统如何保存Activity使用状态的规则。
返回栈
任务呢是一个特别虚的概念,是为了方便开发者理解才有的它,而系统中真正存储Activity的是一个遵循先进后出原则的数据结构:栈。一般叫它返回栈(任务栈,堆栈,其实叫什么的都有)。
返回栈是任务的实际载体,每个任务中所有的Activity都会按照各自的打开顺序保存在对应的返回栈中。所以Android系统显示界面的顺序是先找到要显示界面所在的任务,然后在对应的返回栈中找到显示的Activity。
值得一提的是由于返回栈存储结构的特殊性,外部只能访问到栈顶的Activity,也就是最后入栈的那个。所以一个Activity想要能显示在屏幕上那么它必须存在于栈顶位置。
进栈与出栈
当前 Activity 启动另一个 Activity 时,新的 Activity 会被推送到堆栈顶部,成为焦点显示在屏幕上。 前一个 Activity 仍保留在堆栈中,但是处于停止状态。
用户按“返回”按钮时,当前 Activity 会从堆栈顶部弹出(Activity 被销毁),而前一个 Activity 恢复执行。如果用户继续按“返回”,堆栈中的相应 Activity 就会弹出,以显示前一个 Activity,直到用户返回主屏幕为止(或者,返回任务开始时正在运行的任意 Activity)。 当所有 Activity 均从堆栈中移除后,任务即不复存在。栈也就会被回收掉。
特殊的任务
通过前面的了解,我们知道如果要打开新的界面需要把Activity实例放到当前任务对应的返回栈的栈顶。该操作是不管该Activity之前有没有实例化过或者栈中是否已经存在了的。
但是,有些特殊情况下,我们会发现一些“例外”。
例1:当来自多个不同任务中的应用选择使用系统浏览器访问网页的时候,浏览器应用并不会在每个任务的返回栈中都创建Activity,而是将所有网页以选项卡的形式展示在同一个界面中。
本例中浏览器应用的Activity如果已经实例化过了就不会重新创建。
例2:小明在微信中向你分享了一条微博内容,你打开后跳转到了微博APP中的该条微博详情页,当你看完内容后按返回键退出该界面发现并不是回到了微信聊天界面,而是来到了微博主页(或上一次在微博中停留的界面)。
本例中微博详情页的Activity虽然是由微信应用所在的任务启动,但是没有加入到微信应用的任务中,而是加入到了微博的任务栈中。
管理任务
很显然上述两个例子在实际使用中并不少见,对于这种特殊的情况我们需要针对性的管理任务,而众所周知的启动模式仅仅是其中的一种。
定义启动模式
定义Activity的启动模式其实就是定义一个Activity的新实例如何(是否)与当前任务做关联。以什么样的方式进入到当前(或其他)任务中。
如果你只说Activity的启动模式有四种,其实是不准确的,因为我们可以通过两种方法定义不同的启动模式:
使用AndroidManifest.xml中定义
在AndroidManifest.xml中<activity>标签下使用lauchMode属性来指定当前这个activity的启动模式。
使用Intent标志定义
在调用startActivity(Intent intent)前,通过调用intent.addFlags()或者intent.setFlags()方法为Intent添加一个标志,用于为将要启动的Activity声明启动模式。
那两者有什么区别呢?
上述两种方法均可以为activity声明启动模式,只是使用情景不同。
如果我们希望某个activity在任何情况下都会执行一种特殊的启动模式,我们就可以采用AndroidManifest.xml的方法声明。
如果我们希望某个activity大多数情况下正常启动,而少数情况下执行特殊的启动模式,我们就可以在需要执行特殊启动模式时在Intent中添加标志声明。
如果一个activity两种方式都声明了的话,使用Intent标志的方式要比AndroidManifest.xml的优先级高。
两种方式中定义的启动模式有些是不一样的,Intent标志中定义的某些启动模式AndroidManifest.xml中没有,反之一样。
我们常说的四种启动模式其实说的是AndroidManifest.xml中定义的。
使用AndroidManifest.xml声明启动模式
在清单文件中声明 Activity 时,您可以使用<activity>
元素的 ][launchMode
属性指定 Activity 应该如何与任务关联。
您可以分配给 launchMode
属性的启动模式共有四种:
standard
singleTop
singleTask
singleInstance
先不用管他们具体的操作是什么,我们首先要知道这四种启动模式可以分为两大类:
standard
和singleTop
该类启动模式的activity可以被多次的实例化,它们的实例可以放到任何任务中,并且可以位于返回栈的任何位置。singleTask
和singleInstance
带有此类启动模式的activity,它们只能有一个实例存在,且实例只能存在于单独的任务中。
standard:标准模式
默认启动模式,启动activity时直接创建新的实例并压入启动它的任务栈顶。
singleTop:栈顶复用模式
该模式唯一与standard
不同的就是,如果启动singleTop
模式的activity时发现当前任务的栈顶已经存在着这个activity的实例,那么就不会创建新的实例,而是调用该实例的onNewIntent()
方法。其他的跟标准模式一样。
singleTask:栈内复用模式
这个模式有些特殊一点,我们先按使用情景介绍它,当我们将要启动该模式的activity时,系统会判断当前是否有它想要的任务栈:
没有它要的任务栈
系统会新创建一个任务,并将该activity实例化作为该任务的根activity。
有它要的任务栈
这时候系统会找到该任务栈,如果任务栈里只有它自己则直接调用该activity实例的onNewIntent()方法。如果任务栈中它的上方还存在别的activity,那么这些activity会被全部弹出栈。
至于什么是“它想要的任务栈”,我们会在下面单独分析。
singleInstance:单例模式
基本上跟singleTask
相同,会为activity单独创建一个任务并能够复用。但是该模式的activity不允许其他activity跟自己存在于同一个任务中,由此 activity 启动的任何 activity 均会被在其他的任务中打开。
使用Intent标志声明启动模式
此方式可以通过调用intent.addFlags(int flags)
或者intent.setFlags(int flags)
方法为Intent添加一个标志,用于为将要启动的Activity声明启动模式。
在开始介绍前,先进行几点扫盲科普:
一个Intent可以设置多个标志,这就是为啥有
addflags()
和setFlags()
两个方法的原因了。为Intent设置标志的参数都是Intent类的静态常量。
设置Intent标志不光只有设置activity启动模式这一个功能,设置不同的参数还有其他功能。
Intent标志中可以对activity启动模式进行操作的标志可多了,我们只介绍特别典型的三种。
作者:吴七禁
链接:https://www.jianshu.com/p/9dd6d473da76
共同学习,写下你的评论
评论加载中...
作者其他优质文章