Activity启动模式与任务栈(Task)、TaskAffinity应用场景【转】

2023-05-16

转载请注明出处(谢谢):
Activity启动模式与任务栈(Task)全面深入记录(下)_activities waiting to finish 任务栈_zejian_的博客-CSDN博客

通过上一篇文件的分析,我们对Activity的启动模式有了比较清晰的了解后,本篇我们将继续对Activity启动模式的相关参数和任务栈分析,接下来我们就继续上一篇的问题,如何通过taskAffinity属性在同一个应用中创建多个任务栈进行探究。

任务栈之taskAffinity属性

TaskAffinity特点如下:

  • TaskAffinity 参数标识着Activity所需要的任务栈的名称,默认情况下,一个应用中所有Activity所需要的任务栈名称都为该应用的包名。
  • TaskAffinity 属性一般跟singleTask模式或者跟allowTaskReparenting属性结合使用,在其他情况下没有实际意义。
  • TaskAffinity属性的值不能与当前应用包名相同,否则其值跟作废没两样。

TaskAffinity和singleTask启动模式结合使用

当TaskAffinity和singleTask启动模式结合使用时,当前Activity的任务栈名称将与TaskAffinity属性指定的值相同,下面我们通过代码来验证,我们同过MainActivity来启动ActivityA,其中MainActivity启动模式为默认模式,ActivityA启动模式为singleTask,而TaskAffinity属性值为android:taskAffinity="com.zejian.singleTask.affinity" MainActivity和ActivityA代码如下:


package comzejian.myapplication;
 
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
 
public class MainActivity extends AppCompatActivity {
 
    private Button btn;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        btn= (Button) findViewById(R.id.btn);
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent i = new Intent(MainActivity.this,ActivityA.class);
                startActivity(i);
            }
        });
    }
}

ActivityA.class 代码如下:


package comzejian.myapplication;
 
import android.app.Activity;
import android.os.Bundle;
 
/**
 * Created by zejian
 * Time 16/7/26.
 * Description:
 */
public class ActivityA extends Activity{
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_a);
    }
}

清单文件代码如下:


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="comzejian.myapplication">
 
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <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=".ActivityA"
                    android:launchMode="singleTask"
                    android:taskAffinity="com.zejian.singleTask.affinity"
            />
 
    </application>
</manifest>

现在我们启动MainActivity,后再启动ActivityA,然后我们通过 adb shell dumpsys activity activities 命令查看此时栈的情况:

我们可以清楚地看到两个任务栈,其中一个是id=249,栈名称为com.zejian.singleTask.affinity的任务栈,该栈包含了ActivityA,另外一个则是id=248,栈名为默认包名的任务栈,包含了MainActivity。到此我们也明白了,我们确实可以通过singleTask与android:taskAffinity属性相结合的方式来指定我们Activity所需要的栈名称,使相应的Activity存在于不同的栈中,图解如下:

当TaskAffinity和allowTaskReparenting结合使用

首先我们来聊聊allowTaskReparenting属性,它的主要作用是activity的迁移,即从一个task迁移到另一个task,这个迁移跟activity的taskAffinity有关。当allowTaskReparenting的值为“true”时,则表示Activity能从启动的Task移动到有着affinity的Task(当这个Task进入到前台时),当allowTaskReparenting的值为“false”,表示它必须呆在启动时呆在的那个Task里。如果这个特性没有被设定,元素(当然也可以作用在每次activity元素上)上的allowTaskReparenting属性的值会应用到Activity上。默认值为“false”。这样说可能还比较难理解,我们举个例子,比如现在有两个应用A和B,A启动了B的一个ActivityC,然后按Home键回到桌面,再单击B应用时,如果此时,allowTaskReparenting的值为“true”,那么这个时候并不会启动B的主Activity,而是直接显示已被应用A启动的ActivityC,我们也可以认为ActivityC从A的任务栈转移到了B的任务栈中。这就好比我们在路边收养了一只与主人走失了的猫,养着养着突然有一天,主人找上门来了,这只猫也就被带回去了。我们通过图解来更好地理解这种情景:

我们通过代码层面来验证一下,我们创建两个应用分别为ActivityTask(简称A应用)和ActivityTask2(简称B应用),其中A包含ActivityA,B包含ActivityC,我们通过ActivityA启动B应用中的ActivityC,再回到桌面,启动B应用,此时我们观察A应用和B应用各自栈的变化(因为A,B为不同的应用所以taskAfinity属性值肯定不同,所以这里我们就没必要指定了)。
ActivityA及其清单文件代码如下:

import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
 
/**
 * Created by zejian
 * Time 16/7/23.
 * Description:
 */
public class ActivityA extends Activity {
 
    private Button btnC;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_a);
 
        btnC= (Button) findViewById(R.id.mainC);
        btnC.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(Intent.ACTION_MAIN);
                intent.addCategory(Intent.CATEGORY_LAUNCHER);
                ComponentName cn = new ComponentName("com.cmcm.activitytask2", "com.cmcm.activitytask2.ActivityC");
                intent.setComponent(cn);
                startActivity(intent);
            }
        });
    }
}


<activity android:name=".ActivityA">
     <intent-filter>
           <action android:name="android.intent.action.MAIN" />
           <category android:name="android.intent.category.LAUNCHER" />
     </intent-filter>
</activity>

ActivityA及其清单文件代码如下:


package com.cmcm.activitytask;
 
package com.cmcm.activitytask;
 
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
 
/**
 * Created by zejian
 * Time 16/7/23.
 * Description:
 */
public class ActivityC extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_c);
    }
}


<activity android:name=".ActivityC" android:exported="true"    
      android:allowTaskReparenting="true">
</activity>

我们通过AcitivityA启动B应用的ActivityC后,内存中栈的如下:

我们可以看到ActivityA和ActivityC同在一个栈中,接着我们回到桌面启动B应用,此时内存中的任务栈如下:

我们发现ActivityC从A应用的任务栈直接移动到B应用的任务栈,这也就符合我们前面所说的现象了。而当我们修改allowTaskReparenting为false时,再运行,然后重复上面的操作,查看内存中任务栈的变化:
A应用启动B应用的ActivityC时

回到桌面再启动B应用时

对比发现,如果allowTaskReparenting值为false时,ActivityC并不会直接从A应用的任务栈迁移到B应用的任务栈,而是B应用直接重新创建了ActivityC的实例。到此我们对于allowTaskReparenting和taskAffinity属性的了解就已经相当深入了,不过有点需要说明的是allowTaskReparenting仅限于singleTop和standard模式,这是因为一个activity的affinity属性由它的taskAffinity属性定义(代表栈名),而一个task的affinity由它的root activity定义。所以,一个task的root activity总是拥有和它所在task相同的affinity。由于以singleTask和singleInstance启动的activity只能是一个task的root activity,因此allowTaskReparenting仅限于以standard 和singleTop启动的activity,大家可以自行测试一下,这里我们就不测试了哈,下面我们再来说说它们可能应用用场景。

TaskAffinity与allowTaskReparenting和singleTask结合时可能发生的应用场景

  • TaskAffinity与singleTask应用场景

假如现在有这么一个需求,我们的客户端app正处于后台运行,此时我们因为某些需要,让微信调用自己客户端app的某个页面,用户完成相关操作后,我们不做任何处理,按下回退或者当前Activity.finish(),页面都会停留在自己的客户端(此时我们的app回退栈不为空),这显然不符合逻辑的,用户体验也是相当出问题的。我们要求是,回退必须回到微信客户端,而且要保证不杀死自己的app.这时候我们的处理方案就是,设置当前被调起Activity的属性为:

LaunchMode=""SingleTask" taskAffinity="com.tencent.mm"

其中com.tencent.mm是借助于工具找到的微信包名,就是把自己的Activity放到微信默认的Task栈里面,这样回退时就会遵循“Task只要有Activity一定从本Task剩余Activity回退”的原则,不会回到自己的客户端;而且也不会影响自己客户端本来的Activity和Task逻辑。

  • TaskAffinity与allowTaskReparenting应用场景

一个e-mail应用消息包含一个网页链接,点击这个链接将出发一个activity来显示这个页面,虽然这个activity是浏览器应用定义的,但是activity由于e-mail应用程序加载的,所以在这个时候该activity也属于e-mail这个task。如果e-mail应用切换到后台,浏览器在下次打开时由于allowTaskReparenting值为true,此时浏览器就会显示该activity而不显示浏览器主界面,同时actvity也将从e-mail的任务栈迁移到浏览器的任务栈,下次打开e-买了时并不会再显示该activity
  到此TaskAffinity就全部介绍完了,最后我们再来了解几个跟任务栈相关的属性参数;

清空任务栈

Android系统除了给我提供了TaskAffinity来指定任务栈名称外,还给我提供了清空任务栈的方法,在一般情况下我们只需要在<activity>标签中指明相应的属性值即可。

  • android:clearTaskOnLaunch

这个属性用来标记是否从task清除除根Activity之外的所有的Activity,“true”表示清除,“false”表示不清除,默认为“false”。这里有点我们必须要注意的,这个属性只对任务栈内的root Activity起作用,任务栈内其他的Activity都会被忽略。如果android:clearTaskOnLaunch属性为“true”,每次我们重新进入这个应用时,我们只会看到根Activity,任务栈中的其他Activity都会被清除出栈。
  比如一个应用的Activity A,B,C,其中clearTaskOnLaunch设置为true,C为默认值,我们依次启动A,B,C,点击HOME,再在桌面点击图标。启动的是A,而B,C将都被移除当前任务栈。也就是说,当Activity的属性clearTaskOnLaunch为true时将被优先启动,其余的Activity(B、C)都被移除任务栈并销毁,除非前面A已经finish销毁,后面的已注册clearTaskOnLaunch为true的activity(B)才会生效。
  特别地,如果我们的应用中引用到了其他应用的Activity,这些Activity设置了android:allowTaskReparenting属性为“true”,则它们会被重新宿主到有共同affinity的task中。

  • android:finishOnTaskLaunch

finishOnTaskLaunch属性与clearTaskOnLaunch 有些类似,它们的区别是finishOnTaskLaunch是作用在自己身上(把自己移除任务栈,不影响别的Activity),而clearTaskOnLaunch则是作用在别人身上(把别的Activity移除任务栈),如果我们把Activity的android:finishOnTaskLaunch属性值设置为true时,离开这个Activity所依赖的任务栈后,当我们重新返回时,该Activity将会被finish掉,而且其他Activity不会受到影响。

  • android:alwaysRetainTaskState

alwaysRetainTaskState实际上是给了当前Activity所在的任务栈一个“免死金牌”,如果当前Activity的android:alwaysRetainTaskState设置为true时,那么该Activity所在的任务栈将不会受到任何清理命令的影响,一直保持当前任务栈的状态。

好了,到此本篇也就完结,相信通过两篇的记录我们对Activity的启动模式和任务栈都有相对清晰的了解了哈。

  • Android之Activity生命周期的浅析(一)

  • Android之Activity生命周期浅析(二)

  • Activity启动模式与任务栈(Task)全面深入记录(上)

  • Activity启动模式与任务栈(Task)全面深入记录(下)

    主要参考:
    《android开发艺术探索》
    google官网
    对SingleTask和TaskAffinity的理解 - Android移动开发技术文章_手机开发 - 红黑联盟

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Activity启动模式与任务栈(Task)、TaskAffinity应用场景【转】 的相关文章

  • JAVA中String的底层解析

    JAVA中String 是Final类不能被继承 JAVA 对String的处理和一般Class有所不同 这文章主要是解释一下String的存储模式和java的字符串常量池的机制 xff0c 和几个涉及底层的引用问题解析 首先提出几个问题
  • 面向单片机编程(二)-开端,点亮第一个LED

    上一章中我们已经认识了单片机 xff0c 并且完成了开发环境的搭建 xff0c 这章中我们正式开始进入单片机编程的学习 xff0c 本章的学习目标是点亮一盏LED灯 一 创建一个Keil工程 1 双击运行Keil uVision5 xff0
  • ROS2——创建ROS2工作空间

    ROS2中的工作空间类似我们常说的概念 工程 xff0c 是我们在ROS中开发具体项目的空间 xff0c 所有功能包的源码 配置 编译都在该空间下完成 我们可能会同时开发多个项目 xff0c 就会产生多个工作空间 xff0c 所以工作空间之
  • C++ Primer (暂时完结)

    C 43 43 Primer C 43 43 预备知识泛型编程第二章 开始学习C 43 43 2 1 3 C 43 43 预处理器 和iostream 文件名称空间第二章 输出输入endl控制符换行符规范的书写格式c 43 43 源码风格
  • Futaba S-BUS controlled by mbed(使用mbed控制Futaba S-BUS)

    原文地址 xff1a Futaba S BUS controlled by mbed 代码地址 xff1a SBUS Library Introduction xff08 简介 xff09 The Futaba S BUS protocol
  • STorM32 BGC 相关

    下载 storm32 bgc v130电路图 xff08 http download csdn net detail hxiaohai 9901379 xff09 drv8313器件手册 xff08 http download csdn n
  • TMC5160步进电机驱动芯片开发手记

    2018年 xff0c Trinamic推出了新的型号TMC5160 xff0c 自带6点速度曲线 StealthChop和SpreadCycle静音防抖技术等很不错的功能 xff0c 目前使用官方BOB板进行开发测试 xff0c 在初步参
  • 导航过程各坐标系之间转换

    做导航最烦人的就是坐标系之间的转换 xff0c 每次都要去推相应的公式 xff0c 所以特地写点干货 xff0c 解决你坐标系转换问题 1 经纬高转地心 xff08 1 xff09 式1中 xff0c 为纬度 xff0c 为经度 xff0c
  • requestAuthentication详解

    欢迎关注我的公众号 xff1a 目前刚开始写一个月 xff0c 一共写了18篇原创文章 xff0c 文章目录如下 xff1a istio多集群探秘 xff0c 部署了50次多集群后我得出的结论 istio多集群链路追踪 xff0c 附实操视
  • 运行SLAM遇到的问题

    运行的代码来自与高翔的博客 1 绝对路径 将程序中的相对路径改成绝对路径 xff0c 否则找不到图片 2 pcl问题 在CMakeLists中要加上一行代码 list REMOVE ITEM PCL LIBRARIES 34 vtkproj
  • python tcping(ping命令)

    python tcping xff08 ping命令 使用tcping库中的ping方法使用如下 xff1a span class token keyword from span tcping span class token keywor
  • RecycledViewPool的使用和堆内存分析

    RecycledViewPool在 ViewPager 43 RecyclerView的场景下可以大放光彩 下面就来验证一下它的优点 xff1a 首先我们自定定义一个View放在ViewHolder中 xff1a public class
  • SX1261/2芯片开发那些事儿(一)时钟源选择

    相信大家第一次接触LoRa芯片时 阅读Semtech官方Demo代码或者自己进行项目开发时 xff0c 不知道对于待机模式 xff0c 是使用STDBY RC还是STDBY XOSC呢 xff1f 今天我们就来介绍射频芯片SX1261 2该
  • 解决Ubuntu20.04安装ROS过程镜像源问题

    解决Ubuntu20 04安装ROS过程镜像源问题 问题背景问题描述解决方案参考链接 问题背景 我是先安装了 Windows10 子系统 WSL wsl install 并安装Ubuntu20 04 wsl install d Ubuntu
  • 解决 qt.qpa.xcb: could not connect to display 问题

    2022 07 21更新 现在WSL2已经可以直接运行 Linux GUI 了 xff01 xff01 xff01 不再需要安装Xserver xff08 XLaunch xff09 之类的 xff01 xff01 xff01 参考微软的官
  • 解决在VScode中调试C++代码断点无效、断点错位的问题

    问题背景 最近在学习高翔博士的经典教程 视觉SLAM十四讲 xff08 第2版 xff09 xff0c 使用其配套的Github中C 43 43 代码进行学习 xff0c 在调试时发现断点无效 错位的问题 xff0c 查阅了一些资料 xff
  • Ubuntu系统安装在移动固态硬盘,实现在不同电脑即插即用

    Ubuntu系统安装在移动固态硬盘 xff0c 实现在不同电脑即插即用 一 前期准备二 制作系统启动盘2 1 Ubuntu20 04系统下载2 2 制作U盘启动盘 三 磁盘分区 xff08 重点 xff09 四 Ubuntu系统安装 xff
  • 详解C/C++代码的预处理、编译、汇编、链接全过程

    1 C C 43 43 运行的四个步骤 编写完成一个C C 43 43 程序后 xff0c 想要运行起来 xff0c 必须要经过四个步骤 xff1a 预处理 编译 汇编和链接 每个步骤都会生成对应的文件 xff0c 如下图所示 xff08
  • VSCode调试C++代码的多种方案

    以下内容均针对 Linux 操作系统 xff08 包括Windows的Linux子系统WSL2 xff09 本文是对Linux系统中使用VSCode编译调试C 43 43 代码的系列文章的总结 xff0c 前面三篇文章如下 xff1a 详解
  • 【ROS】rostopic常用命令

    记录ROS查看话题的常用命令 xff1a rostopic 查看节点列表 xff1a rostopic list 查看节点信息 xff0c 如相机节点的内参K矩阵 畸变D矩阵 分辨率等等 rostopic echo camera camer

随机推荐

  • 【ROS笔记】设置、修改ROS环境变量

    ROS设置环境变量 ROS安装到Ubuntu后 xff0c 默认在 opt路径下 xff0c 由于在使用过程中需要频繁在终端使用ROS命令 xff0c 需要对其环境变量进行设置 Ubuntu默认使用终端为bash xff0c 在bash中设
  • 【ROS笔记】工作空间(workspace)、功能包(package)的介绍及创建

    初接触ROS时 xff0c 对其文件结构容易产生困惑和混淆 xff0c 比如 catkin ws到底是什么 xff1f catkin ws src目录下存放的是什么 xff1f catkin ws src路径下怎么还会有其他的src 目录
  • RecyclerView中倒计时item的优雅方案

    本文介绍在RecyclerView中使用倒计时楼层 xff0c 并且每秒刷新显示倒计时 没有纠结于样式 xff0c 主要介绍代码结构和设计模式 先看一下效果 xff1a 我们采取的是观察者模式的方法 xff0c 启动一个handler xf
  • bluerov

    ov这个东西其实不难 xff0c 比较麻烦的是密封 xff0c 防水等工作 xff0c 而非一些技术性工作 xff08 所谓技术性工作就是算法 xff0c 人工智能等 xff09 现在供玩家玩的Rov xff0c 基本模式是这样的 xff0
  • 在线教程 | 用「网红项目」DeepSOCIAL 进行社交距离监测

    首发自 xff1a 公众号 HyperAI超神经 内容一览 xff1a YOLO v4 是一个实时的 高精度的目标检测模型 xff0c 本教程将详细讲解如何基于 YOLO v4 和 SORT 算法 xff0c 实现在多目标条件下的人群距离检
  • C#实现服务端/客户端的tcp异步通信完整示例

    demo 通过一个简单的demo xff0c 实现服务器和客户端之间的tcp异步通信 消息进行了Des加密和解密 运行动图如下 xff1a 图中有三个程序在运行 xff0c 最左边是服务端 xff0c 另外两个是客户端 xff0c 客户端可
  • C++封装HTTP

    C 43 43 封装HTTP Client 简介HTTP请求头封装HTTP请求头基本格式post请求头封装post请求头 HTTP Client 以及HTTP Server交互HTTP消息解析TCP封装HTTP Client的坑 Trans
  • android 自定义多功能进度条

    自定义进度条 xff0c 直接在布局文件里传入颜色值 xff0c 不用自己再去写样式 xff0c 还可以 效果图 xff1a git地址
  • 树莓派L298N电机驱动程序连接图文教程

    手里有一块树莓派3闲置很久 xff0c 不知干点啥好呢 看到不少网友用树莓派做智能小车 xff0c 好像很好玩的酱紫 xff0c 就到淘宝买了一些配件 对于硬件小白的我来说安装过程并不轻松 xff0c 网上看了很多资料但有的地方介绍的并不很
  • 一个比较好用的socket 类封装(封装http 请求)

    name myhttp h function http 请求 copyright author mark date 2008 05 13 ifndef MY HTTP INCLUDE define MY HTTP INCLUDE inclu
  • Odroid U3 烧写镜像文件

    在Ubuntu下往Odroid U3板子的eMMC卡里烧写xubuntu镜像文件 xff0c 主要步骤如下 xff1a 1 下载要烧写的镜像文件 xff0c 按照需要 xff0c 我下载的是桌面版的xubuntu 13 04 desktop
  • 解决odroid-XU3的HDMI输出问题

    odroid XU3的板子直接通过microHDMI口连接显示器 xff0c 并没有显示 解决方法是修改 media boot boot ini文件 xff0c 取消屏蔽与HDMI设置相关语句 重启之后 xff0c 解决问题 由于不同的显示
  • 现有的 TypeRef 应有对应的 TypeDef(Impl),但它没有

    现有的 TypeRef 应有对应的 TypeDef Impl xff0c 但它没有 本人需要在C 中调用C 43 43 代码 xff0c 故而建立了一个CLR项目 xff0c 建立了四个文件 xff0c 分别是两个纯C 43 43 文件 C
  • [zed2i] 相机内参数获取

    内置对应的程序 xff1a 双目SDK校正方法 关灯避免反射 xff0c 使得房间的灯光尽可能的黑 xff0c 拿着相机对准屏幕的标定板 xff08 可以不关灯 xff09 开始校正 xff0c 红圈是目标 xff0c 需要移动蓝圈 xff
  • Unity 2D独立开发手记(八):基于A*算法的简易寻路

    被生活 43 43 了一个多月 xff0c 都没时间上来吹比了 因为破游戏准备设计敌人了 xff0c 苦于Unity自带的导航系统迟迟不适配2D项目 xff0c 即便用最新的NavMeshSurface把3D当成2D来用 xff0c 也和我
  • 新建keil工程每一个文件夹的作用

    问 xff1a 头文件stm32f10x sdio c的作用 xff1f 有时候在添加路径的时候为什么不添加src xff1f 有些工程里就添加了 新建工程的每一个文件夹的作用 core xff0c obj xff0c STM32 FWLI
  • VScode使用时常见问题

    写在篇首 xff0c vscode各种蜜汁bug xff0c 记录一下解决方案供大家参考 1 无法使用C 43 43 万能头文件 include xff1c bits stdc 43 43 h xff1e 解决方案 在刷题的时候 xff0c
  • Socket网络编程总结

    网络中进程之间如何通信 Java最初是作为网络编程语言出现的 xff0c 其对网络提供了高度的支持 xff0c 使得客户端和服务器的沟通变成了现实 xff0c 而在网络编程中 xff0c 使用最多的就是Socket 像大家熟悉的QQ MSN
  • linux tcp并发式服务器应用SELECT函数编写实例源代码(转载)

    include lt stdio h gt include lt stdlib h gt include lt unistd h gt include lt errno h gt include lt string h gt include
  • Activity启动模式与任务栈(Task)、TaskAffinity应用场景【转】

    转载请注明出处 xff08 谢谢 xff09 xff1a Activity启动模式与任务栈 Task 全面深入记录 xff08 下 xff09 activities waiting to finish 任务栈 zejian 的博客 CSDN