Android——你所忽略的launchMode之singleTask与taskAffinity

转载自:http://www.jianshu.com/p/0b908b0624e4

感谢作者。


taskAffinity

taskAffinity是用来指示Activity属于哪一个Task的。taskAffinity能够决定以下两件事情(前提是Activity的launchMode为singleTask或者设置了FLAG_ACTIVITY_NEW_TASK):

  1. re-parent
  2. Activity的宿主Task

默认情况下,在一个app中的所有Activity都有一样的taskAffinity,但是我们可以设置不同的taskAffinity,为这些Activity分Task。甚至可以在不同的app之中,设置相同的taskAffinity,以达到不同app的activity公用同一个Task的目的。

Note:

  • Task的所有Activity只有一个taskAffinity
  • Task的taskAffinity,是由Task的root activity定义

singleTask

通常来说,如果Activity中设置launchMode的属性为SingleTask,当我们启动该Activity时,Android系统首先会检查在Task中,是否存在这个Activity的实例对象

  • 存在:Android系统会直接把Intent路由到那个Task中存在的Activity实例对象上,会通过调用onNewIntent()方法处理,而非创建一个新的Activity实例对象。并且会把在它之上的其它Activity清理掉。
  • 不存在:创建一个Activity实例对象这是肯定的,但是这里有个容易被忽视的盲点——Android系统首先会检查被调用的Activity的taskAffinity与调用方Task的taskAffinity是否一致。如果一致,则新创建的Activity实例对象将会被放在原来的Task之上。否则的话,系统会新建一个Task,然后把新创建的Activity实例对象作为一个根元素放在新Task之上。

理论联系实际

首先创建一个工程,定义三个Activity:FirstActivity、SecondActivity、ThirdActivity。我们将依次打开FirstActivity->SecondActivity->ThirdActivity->SecondActivity,并观察打印效果。界面如下所示,为了便于理解,按钮的文字真实的表现为其实际动作。


task activity.jpg

接下来分别对SecondActivity设置:default行为、singleTask、singleTask & different taskAffinity:

default

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

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme" >
        <activity
            android:name=".FirstActivity"
            android:label="FirstActivity"
            android:theme="@style/AppTheme.NoActionBar" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name=".SecondActivity"
            android:label="@string/title_activity_second"
            android:theme="@style/AppTheme.NoActionBar" >
        </activity>
        <activity
            android:name=".ThirdActivity"
            android:label="@string/title_activity_third"
            android:theme="@style/AppTheme.NoActionBar" >
        </activity>
    </application>

</manifest>

依次打开FirstActivity->SecondActivity->ThirdActivity->SecondActivity,查看log信息:

11-08 20:38:54.890 13971-13971/com.xplee.task.task I/xplee: ***************分割线****************
11-08 20:38:54.890 13971-13971/com.xplee.task.task I/xplee: task id:22 FirstActivity onCreate
11-08 20:38:58.510 13971-13971/com.xplee.task.task I/xplee: task id:22 SecondActivity onCreate
11-08 20:39:01.190 13971-13971/com.xplee.task.task I/xplee: task id:22 ThirdActivity onCreate
11-08 20:39:28.600 13971-13971/com.xplee.task.task I/xplee: task id:22 SecondActivity onCreate

可以发现,在默认行为下,它们都在同一个task之中,并且每次都创建一个实例对象,放到Task上。

singleTask

设置SecondActivity的launchMode为singleTask

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

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity
            android:name=".FirstActivity"
            android:label="FirstActivity"
            android:theme="@style/AppTheme.NoActionBar">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name=".SecondActivity"
            android:label="@string/title_activity_second"
            android:launchMode="singleTask"
            android:theme="@style/AppTheme.NoActionBar"></activity>
        <activity
            android:name=".ThirdActivity"
            android:label="@string/title_activity_third"
            android:theme="@style/AppTheme.NoActionBar"></activity>
    </application>

</manifest>

依次打开FirstActivity->SecondActivity->ThirdActivity->SecondActivity,查看log信息:

11-08 20:49:04.920 17645-17645/com.xplee.task.task I/xplee: ***************分割线****************
11-08 20:49:04.920 17645-17645/com.xplee.task.task I/xplee: task id:24 FirstActivity onCreate
11-08 20:49:15.300 17645-17645/com.xplee.task.task I/xplee: task id:24 SecondActivity onCreate
11-08 20:49:17.170 17645-17645/com.xplee.task.task I/xplee: task id:24 ThirdActivity onCreate
11-08 20:49:22.710 17645-17645/com.xplee.task.task I/xplee: task id:24 SecondActivity onNewIntent
11-08 20:49:23.060 17645-17645/com.xplee.task.task I/xplee: task id:24 ThirdActivity onDestroy

从log信息可以发现,其行为基本与default类似,只有在ThirdActivity启动SecondActivity时,行为有差异:SecondActivity会被重用(onNewIntent()被系统调用),并且将原来SecondActivity之上的ThirdActivity清除。这就是我们文中最开头介绍的singleTask的作用。

singleTask & different taskAffinity

设置SecondActivity的launchMode为singleTask,并且设taskAffinity为com.xplee.task.task.SecondActivity,用来区分app默认的taskAffinity

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

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity
            android:name=".FirstActivity"
            android:label="FirstActivity"
            android:theme="@style/AppTheme.NoActionBar">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity
            android:name=".SecondActivity"
            android:label="@string/title_activity_second"
            android:launchMode="singleTask"
            android:taskAffinity="com.xplee.task.task.SecondActivity"
            android:theme="@style/AppTheme.NoActionBar"></activity>
        <activity
            android:name=".ThirdActivity"
            android:label="@string/title_activity_third"
            android:theme="@style/AppTheme.NoActionBar"></activity>
    </application>

</manifest>

依次打开FirstActivity->SecondActivity->ThirdActivity->SecondActivity,查看log信息:

11-08 20:58:18.300 22445-22445/com.xplee.task.task I/xplee: ***************分割线****************
11-08 20:58:18.310 22445-22445/com.xplee.task.task I/xplee: task id:26 FirstActivity onCreate
11-08 20:58:29.340 22445-22445/com.xplee.task.task I/xplee: task id:27 SecondActivity onCreate
11-08 20:58:30.920 22445-22445/com.xplee.task.task I/xplee: task id:27 ThirdActivity onCreate
11-08 20:58:33.140 22445-22445/com.xplee.task.task I/xplee: task id:27 SecondActivity onNewIntent
11-08 20:58:33.480 22445-22445/com.xplee.task.task I/xplee: task id:27 ThirdActivity onDestroy

可以发现,这一次app有两个不同的Task。这是因SecondActivity被设置了taskAffinity,与app默认的taskAffinity不一样,而系统在启动启动模式为singleTask的SecondActivity时,会先比较taskAffinity。系统发现它们不一致,因此系统创建一个新的Task(与FirstActivity不在同一个Task),并以SecondActivity作为root activity。

最后当ThirdActivity启动SecondActivity时,由于singleTask的关系,SecondActivity会被重用(onNewIntent()被系统调用),并且将原来SecondActivity之上的ThirdActivity清除

总结

  1. singleTask和taskAffinity在Task Stack的处理中,有密切关系,需要协同分析和使用。

  2. singleTask只会决定Activity在一个Task中的行为,并不能决定Activity与Task的从属关系

  3. taskAffinity犹如一个标志,只有它能够决定Activity与Task的从属关系

### 实现跨应用程序 SingleTask Activity 跳转 在 Android 中,`singleTask` 启动模式允许一个 `Activity` 在整个任务栈中仅存在一次实例。当尝试启动该 `Activity` 时,如果其已经在任务栈中,则会将其带到前台并重用现有的实例。 为了实现两个不同应用程序之间通过 `singleTask` 模式的 `Activity` 进行跳转: #### 配置目标 Application 的 Manifest 文件 确保接收方应用的目标 `Activity` 设置了合适的属性来支持单次任务行为[^2]: ```xml <activity android:name=".TargetActivity" android:launchMode="singleTask" android:taskAffinity="" android:excludeFromRecents="true"> <!-- Intent Filter --> </activity> ``` - `android:launchMode="singleTask"` 表明此活动在整个系统范围内只会有一个实例。 - `android:taskAffinity=""` 取消默认的任务亲和力设置,使得这个 `Activity` 不会被放置到发起者的任务堆栈里而是创建新的独立任务栈。 - `android:excludeFromRecents="true"` 将防止该 `Activity` 出现在最近使用的程序列表中。 #### 发起端 Application 的处理方式 发送意图(Intent)给另一个应用程序中的特定 `Activity`: ```java // 创建显式 intent 或者隐式 intent 并指定要打开的应用包名类路径 Intent intent = new Intent(Intent.ACTION_VIEW); intent.setClassName("com.example.targetapp", "com.example.targetapp.TargetActivity"); // 添加标记位 FLAG_ACTIVITY_NEW_TASK 和 FLAG_ACTIVITY_CLEAR_TOP 来确保按照 singleTask 方式启动 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP); context.startActivity(intent); // context 是 Context 类型的对象比如 Activity 或 Service ``` 上述代码片段展示了如何配置源应用程序以正确的方式向目的应用程序传递请求,并且遵循了 `singleTask` 的启动逻辑[^1]。 #### 接收端 Application 对应的处理机制 在接收到广播或者其他形式的通知之后,可以通过覆盖 `onNewIntent()` 方法捕获传入的新意图数据: ```java @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); setIntent(intent); // 更新当前 activity 的 intent // 处理新传来的参数... } ``` 这一步骤非常重要,因为每当有新的意图指向同一个 `singleTask` 活动时,它不会被销毁重建而只是回调此函数以便开发者可以获取最新的输入信息。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值