深入理解Activity的启动模式

2018-03-23  本文已影响0人  whd_Alive

总结一下启动模式,以便日后回顾,整理自官方文档

前言

Activity的启动模式很重要,与回退栈以及重复实例息息相关,此文将就启动模式介绍以下内容:

前置知识

示意图

LaunchMode

LaunchMode的类别:

如看不下去= =,可直接跳至下方四种模式的异同

1. standard 标准模式

默认的启动方式,特点是每次启动时,都会创建活动的新实例,所有该活动的实例位于同一个task栈,遵循first in last out。

2. singleTop 栈顶复用

和standard很类似的启动方式,也可以创建很多实例,特点是如果启动目标Activity时,前台已经有一个目标Activity的实例(即目标Activity处在task栈顶),则会重用该目标Activity实例,而非创建新实例。同时这个重用的实例,会接收到一个Activity.onNewIntent()的调用,以此获取新的Intent
这个模式使用场景其实很少,通常只会避免相同程序的重复启动,而不同程序间的跳转情况与stardard完全一致,给定以下情景():

3. singletask 栈内复用

该模式下的Activity在系统中只会有一个实例,如果启动时,task栈中存在一个该活动的实例,则会复用该活动实例,并将task栈中该实例之上的activity全部出栈(销毁过程中会调用Activity的生命周期回调方法)。同样的,通过onNewIntent()方法接收新的Intent

4. singleInstance 单例模式

和singleTask类似,系统中只会存在一个活动实例。区别在于singleInstance模式下的Activity所处的task栈中仅存在该Activity一个实例,如果启动其他任何Activity,那么都会在另一个task栈中启动对应的Activity;而对于重复启动自身时,则会复用原Activity,同样通过onNewIntent()方法接收新的intent

四种模式的异同:

四种模式的异同

该图摘自大佬Carson_Ho《Android基础:最易懂的Activity启动模式详解》一文,若我表述不清,可移步原文

LaunchMode的设置方法

在Manifest的Activity配置中进行设置

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="yourPackage">

    <application
        ...>
        <activity android:name="..."
            android:launchMode="standard/singleTop/singleTask/singleInstance">

           
        </activity>
    </application>

</manifest>

至此关于LaunchMode的部分就介绍完了。


Intent Flag

通过Intent.addFlags()设置标志位是另一种设置启动模式的方式,和LuanchMode方式有相交的部分。

注意:LaunchMode和Intent flags的优先级问题:
Intent设置方式的优先级 > Manifest设置方式

更多详见Google官方文档

首先我们先介绍以下最常见的几个标记位属性

等同于SingleTop

等同于SingleTask

清除位于其上层的所有Activity,与singleTask及其类似。区别在于:
当此标记不搭配FLAG_ACTIVITY_SINGLE_TOP标记时,会将已有的实例销毁重建。
而搭配FLAG_ACTIVITY_SINGLE_TOP标记时则会复用已有的实例,并通过onNewIntent()方法得到新的intent

设置后,新Activity不会出现在recent apps里,即无法通过历史列表回到该Activity。等同于在XML中指定Activity的属性android:excludeFromRecents="true"

类似singleTask,区别在于当重复启动目标活动时,不会将位于原活动之上的其他活动出栈,只是将原活动“置顶”。


以下部分好像没那么"常用",至少很少看到有博客总结以下的标记位属性。

启动目标Activity时传递这个标记,则会导致所处的task栈被清空,然后在清空在之后的task栈中启动目标Activity,也就是说,目标Activity成为这个空task栈的root Activity该标志必须配合FLAG_ACTIVITY_NEW_TASK一起使用。

在API 21中废弃,现使用FLAG_ACTIVITY_NEW_DOCUMENT

官方文档中是这么解释的:如果设置,并且这个Intent用于从一个存在的Activity启动一个新的Activity,那么,这个作为答复目标的Activity将会传到这个新的Activity中。这种方式下,新的Activity可以调用setResult(int),并且这个结果值将发送给那个作为答复目标的Activity。
为了理解下面举个例子:Activity A 通过startActivityForResult()启动Activity B,之后Activity B 同样通过startActivityForResult(),但附加FLAG_ACTIVITY_FORWARD_RESULT的flag来启动Activity C。此时将会由C向AsetResult()

一般由系统调用,比如长按home键从历史记录中启动。

此标志仅用于分屏多窗口模式。new Activity显示在启动它的活动(old Activity)的旁边。只能与FLAG_ACTIVITY_NEW_TASK一起使用。另外,如果需要创建现有活动的新实例,则需要设置FLAG_ACTIVITY_MULTIPLE_TASK。

Android P Developer Priview中加入
设置后,如果设备上没有能够处理该intent的app,那么将会启动一个instant app来进行处理。

这个标识用来创建一个新的task栈,并且在里面启动新的activity(所有情况,不管系统中存在不存在该activity实例),经常和FLAG_ACTIVITY_NEW_DOCUMENT或者FLAG_ACTIVITY_NEW_TASK一起使用。这上面两种使用场景下,如果没有带上FLAG_ACTIVITY_MULTIPLE_TASK标识,他们都会使系统搜索存在的task栈,去寻找匹配intent的一个activity,如果没有找到就会去新建一个task栈;但是当和FLAG_ACTIVITY_MULTIPLE_TASK一起使用的时候,这两种场景都会跳过搜索这步操作无条件的创建一个新的task。和FLAG_ACTIVITY_NEW_TASK一起使用需要注意,尽量不要使用该组合除非你完成了自己的顶部应用启动器,他们的组合使用会禁用已经存在的task栈回到前台的功能。

api 21之后加入的一个标识,用来在intent启动的activitytask栈中打开一个document,和documentLaunchMode效果相等,有着不同的documentsactivity的多个实例,将会出现在最近的task列表中。单独使用效果和documentLaunchMode="intoExisting"一样,如果和FLAG_ACTIVITY_MULTIPLE_TASK一起使用效果就等同于documentLaunchMode="always"

禁用activity间的切换动画

Activity不在回退栈中保留,一旦退出就销毁,等同于设置noHistory属性。
该方法会导致onActivityResult()失效,毕竟没有返回的结果了嘛。

禁止activity调用onUserLeaveHint()onUserLeaveHint()作为activity周期的一部分,它在activity因为用户要跳转到别的activity而退到background时使用。比如,在用户按下Home键(用户的操作),它将被调用。比如有电话进来(不属于用户的操作),它就不会被调用。注意:通过调用finish()时该activity销毁时不会调用该函数。

设置之后,再次重新启动一个存在的Activity时,新的Activity会立即finish掉,原本的Activity则会作为栈顶Activity使用。

这个标记在以下情况下会生效:1.启动Activity时创建新的task来放置Activity实例;2.已存在的task被放置于前台。系统会根据affinity对指定的task进行重置操作,task会压入某些Activity实例或移除某些Activity实例。我们结合上面的FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET可以加深理解。

默认情况下通过FLAG_ACTIVITY_NEW_DOCUMENT启动的activity在关闭之后,task中的记录会相对应的删除。如果为了能够重新启动这个activity你想保留它,就可以使用这个flag,最近的记录将会保留在接口中以便用户去重新启动。接受该flag的activity可以使用autoRemoveFromRecents去复写这个request或者调用Activity.finishAndRemoveTask()方法。

把当前新启动的任务置于Home任务之上,也就是按back键从这个任务返回的时候会回到home,即使这个不是他们最后看见的activity,注意这个标记必须和FLAG_ACTIVITY_NEW_TASK一起使用。

如果设置了这个flag,那么在处理这个intent的时候,将会打印相关创建日志。

用来标识该intent的操作是一个后端的操作而不是一个直接的用户交互。

当和FLAG_GRANT_READ_URI_PERMISSION 和/或FLAG_GRANT_WRITE_URI_PERMISSION一起使用时,uri权限在设置重启之后依然存在直到用户调用了revokeUriPermission(Uri, int)方法,这个标识仅为可能的存在状态提供许可,接受的应用必须要调用takePersistableUriPermission(Uri, int)方法去实际的变为存在状态。

当和FLAG_GRANT_READ_URI_PERMISSION 和/或FLAG_GRANT_WRITE_URI_PERMISSION一起使用时,uri的许可只用匹配前缀即可(默认为全部匹配)。

如果设置FLAG_GRANT_READ_URI_PERMISSION这个标记,Intent的接受者将会被赋予读取Intent中URI数据的权限和ClipData中的URIs的权限。当使用于IntentClipData时,所有的URIsdata的所有递归遍历或者其他IntentClipData数据都会被授权。

同上,只是相应的赋予的是写权限

当发送广播时,允许其接受者拥有前台的优先级,更短的超时间隔。

如果这是一个有序广播,不允许接受者终止这个广播,它仍然能够传递给下面的接受者。

如果设置了这个flag,当发送广播的时,动态注册的接受者才会被调用,在AndroidManifest.xml里定义的Receiver 是接收不到这样的Intent的。

如果设置了的话,ActivityManagerService就会在当前的系统中查看有没有相同的intent还未被处理,如果有的话,就由当前这个新的intent来替换旧的intent,所以就会出现在发送一系列的这样的Intent 之后,中间有些Intent 有可能在你还没有来得及处理的时候, 就被替代掉了的情况

设置之后,广播将对instant app中的广播接收器可见。默认不可见。


taskAffinity

每个Activity都有一个taskAffinity属性,用于指定活动具有"亲和力"的task名称,简单来说就是指出该活动希望进入的task。该属性默认为包名,除非Application或者Activity设置该属性。
taskAffinity属性必须要与singleTask启动模式或者allowTaskReparenting属性配对使用,否则没有意义。

allowTaskReparenting属性表明是否允许该Activity更换从属task

taskAffinity设置方法

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.yourpackagename">

    <application
        ...>
        <activity 
            ...
            android:taskAffinity="com.example.yourtaskname"
            android:allowTaskReparenting="true/false"
            >
            ...
        </activity>
    </application>

</manifest>

引用


总结


欢迎关注whd_Alive的简书

上一篇下一篇

猜你喜欢

热点阅读