Android OtherAndroid开发Android开发经验谈

终于懂了之 Activity 的四种启动模式

2017-12-09  本文已影响93人  Vivi成长吧

前言

Android 开发者几乎都知道 Activity 有四种启动模式(standard,singleTop,singleTask,singleInstance)。当然,对于刚开始接触 Android 的开发者,可能一开始不了解或不理解,但是工作个一年两年以后,肯定会接触到这个知识点的。网上有太多太多这方面的文章,但是或多或少都不那么准确(当然也有准确的,只是我看到的文章有限……)。最近把四种启动模式,taskAffinity 属性及 FLAG_ACTIVITY_CLEAR_TOP,FLAG_ACTIVITY_NEW_TASK 和 FLAG_ACTIVITY_SINGLE_TOP 这三种Flag 放在一起,仔细的研究了一下,终于弄懂了它们之间错综复杂的关系。

什么是任务(Task)

首先,我们先来简单了解一下在 Android 应用程序中,任务(Task)是个什么样的概念。我们知道,Activity 是 Android 应用程序的四大组件之一,在应用程序运行时,用户为了完成某个功能而执行的一系列操作(页面跳转)就形成了一个 Activity 序列,这个序列在 Android 应用程序中就称之为任务,它是从用户体验的角度出发,把一组相关的 Activity 组织在一起而抽象出来的概念。

Task 是一组相互关联的 Activity 的集合,它是存在于 framework 层的一个概念,控制界面的跳转和返回。这个 Task 存在于一个称为 back stack的数据结构中,也就是说,framework 是以栈的形式管理用户开启的 Activity。这个栈的基本行为是,当用户在多个 Activity 之间跳转时,执行压栈操作,当用户按返回键时,执行出栈操作。

对初学者来说,在开发 Android 应用程序时,对任务的概念可能不是那么的直观,一般我们只关注如何实现应用程序中的每一个 Activity。事实上,Android 系统中的任务更多的是体现是应用程序运行的时候,因此,它相对于 Activity 来说是动态存在的,这就是为什么我们在开发时对任务这个概念不是那么直观的原因。

不同的应用的 Activity 可以在同一个任务里,也就是说 Task 是跨应用的,这样保证了用户操作的连贯性,用户体验比较好。不同任务之间切换,界面会闪一下,不连贯。

神奇的 taskAffinity

不过,我们在开发 Android 应用程序时,可以配置 Activity 的任务属性的,即告诉系统,这个 Activity 是要在新的任务中启动呢,还是在已有的任务中启动,亦或是其它的 Activity 能不能与这个 Activity 共享同一个任务,这个神奇的属性就是 taskAffinity。

在 AndroidManifest.xml ,我们通过属性

android:taskAffinity="july.com.tempdemo.xxx"

可以为每个Activity 设置其 taskAffinity。如果不设置,默认情况下,所有的 Activity 的taskAffinity 都一样,都为app的包名。

这里关于 taskAffinity 我们记住两点就可以:

三个Flag

Intent 类里定义了很多 FLAG,这里我只说三种:

Flag 组合设置:

Activity 的四种启动模式

我们在 AndroidManifest.xml 里为 Activity 设置 launchMode:


set launchMode.png

standard 启动模式

这是 Activity 默认的启动模式,这种模式下,每次 startActivity 都会在栈顶创建一个新的实例,在同一个任务中可以存在多个Activity 的实例。

singleTop 启动模式

栈顶复用,也就是说,要启动 singleTop 模式的 Activity,如果它恰好在当前栈顶,那么直接复用,执行其 onNewIntent 方法。否则,就重新创建一个实例入栈。

singleTask 启动模式

设置了"singleTask"启动模式的 Activity,在系统中只有一个实例,当再次启动该 Activity 时,会重用已存在的任务和实例,并且会调用这个实例的 onNewIntent()方法,将 Intent 实例传递到该实例中。

设置了"singleTask"启动模式的 Activity 的特点(划重点):
它在启动的时候,会先在系统(所有现存的tasks)中查找属性值 affinity 等于它的属性值 taskAffinity 的任务是否存在;

因此,如果我们想要设置了 "singleTask" 启动模式的 Activity 在新的任务中启动,就要为它设置一个独立的 taskAffinity 属性值。

下面举几个例子,来体验一下singleTask 启动模式。测试 demo 下载地址:https://github.com/JulyDev/ActivityLaunchMode
ps: 这个 demo 改了很多次,想到什么情况就改一下,来验证自己的想法。因为随机组合条件太多了,不可能一一保存下来。所以最好自己写个 demo 亲自验证一下。

例子1. 有三个 Activity,MainActivity,SecondActivity, ThirdActivity, 其中SecondActivity设置为 singleTask ,其他都是standard启动模式,并且onClick事件里启动时也不加任何 Flag 或 Action。然后 MainActivity 启动SecondActivity,SecondActivity 再启动 ThirdActivity。

<activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
        <activity
            android:name=".SecondActivity"
            android:launchMode="singleTask"
            >
        </activity>
        <activity
            android:name=".ThirdActivity"
            >
        </activity>

启动 app,依次打开这三个 Activity,结果如下:


singleTask only.png

可以发现,设置为 singleTask 的 SecondActivity,是跟 MainActivity 在同一个 task 里。为什么呢?因为都没有设置 taskAffinity 啊,默认都是包名,也就是相同的,那么启动 SecondActivity 时,根据 taskAffinity ,找到了MainActivity 所在的task,这里面是没有 SecondActivity 实例的,于是创建一个实例入栈。

例子2. 现在,在例子1的基础上,给 SecondActivity 设置一个不同的 taskAffinity :

<activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
        <activity
            android:name=".SecondActivity"
            android:launchMode="singleTask"
            android:taskAffinity="july.com.tempdemo.second"
            >
        </activity>
        <activity
            android:name=".ThirdActivity"
            >
        </activity>

测试结果如下:


singleTask && taskAffinity.png

可以看到,此时 SecondActivity 新建了一个 task,不同于 MainActivity 所在的 task。
仔细看我们还发现,由 SecondActivity 启动的ThirdActivity 跟 SecondActivity 在同一个 task 里面。

singleInstance 启动模式

总是在新的任务中开启,并且这个新的任务中有且只有这一个实例,也就是说被该实例启动的其他 Activity 会自动运行于另一个任务中。当再次启动该 Activity 的实例时,会重用已存在的任务和实例。并且会调用这个实例的onNewIntent()方法,将 Intent 实例传递到该实例中。

设置了 singleInstance 的 Activity,整个系统只有一个实例,独占一个栈,且由它启动的 Activity 根据目标 Activity 的 taskAffinity 来选择进哪个 task,若不存在对应的 task,则新建一个 task 并新建一个目标 Activity 的实例入栈。
举例:
ThirdActivity 设置为 singleInstance 模式。其他都为 standard模式。MainActivity 启动 SecondActivity,SecondActivity 启动 ThirdActivity,ThirdActivity 又启动 SecondActivity。

<activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/>

                <category android:name="android.intent.category.LAUNCHER"/>
            </intent-filter>
        </activity>
        <activity
            android:name=".SecondActivity"
            >
        </activity>
        <activity
            android:name=".ThirdActivity"
            android:launchMode="singleInstance"
            >
        </activity>
        

测试结果如下:


singleInstance.png

可以看到,ThirdActivity 单独在一个 task 里,而由 ThirdActivity(singleInstance) 启动的 SecondActivity,根据 affinity 找到了 MainActivity 所在的 task,并且直接在栈顶创建一个实例(不管栈里是否已经存在 SecondActivity 的实例)。这里跟 singleTask 模式不一样。设置了 singleTask 模式的 Activity, 启动的普通 Activity ,直接就放在当前栈顶了(见singleTask 启动模式一节的例子2)。

参考资料:
1.http://m.blog.csdn.net/luoshengyang/article/details/6714543

2.http://blog.csdn.net/zhangjg_blog/article/details/10923643

上一篇 下一篇

猜你喜欢

热点阅读