什么?按Home键SingleInstance Activity销毁了???

news2024/12/26 23:08:40

前段时间,突然有朋友询问,自己写的SingleInstance Activity在按home键的时候被销毁了,刚听到这个问题的时候,我直觉怀疑是Activity在onPause或者onStop中发生了Crash导致闪退了,但是安装apk查看现象,没有发现异常日志,这究竟是怎么回事呢?编写测试Demo来详细探索下

Demo代码说明

Demo日志很简单,包含MainActivity和SingleInstanceActivity两个页面,在MainActivity中的TextView点击事件中启动SingleInstanceActivity,在SingleInstanceActivity中的TextView点击事件中调用moveTaskToBack(true)切回后台,随后在MainActivity界面按Home键返回桌面,就可以看到SingleInstanceActivity被销毁了,示例代码如下所示:

// MainActivity.kt
class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MyApplicationTheme {
                // A surface container using the 'background' color from the theme
                Surface(
                    modifier = Modifier
                        .fillMaxSize()
                        .clickable { onBtnClick() },
                    color = MaterialTheme.colorScheme.background
                ) {
                    Greeting("Android")
                }
            }
        }
    }
    fun onBtnClick() {
         startActivity(Intent(this,                                  SingleInstanceActivity::class.java))
    }
}

@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {
    Text(
        text = "Hello $name!",
        modifier = modifier
    )
}

@Preview(showBackground = true)
@Composable
fun GreetingPreview() {
    MyApplicationTheme {
        Greeting("Android")
    }
}
// SingleInstanceActivity.java
public class SingleInstanceActivity extends ComponentActivity {
    private static final String TAG = "SingleInstanceActivity";
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    Log.d(TAG,"SingleInstanceActivity onCreate method called",new Exception());
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_single_instance);
    findViewById(R.id.move_back).setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            moveTaskToBack(true);
        }
    });
  }

  @Override
  protected void onDestroy() {
    Log.d(TAG,"SingleInstanceActivity onDestroy method called",new Exception());
    super.onDestroy();
  }
}
<!-- AndroidManifest.xml文件中application节点的内容-->
<application
    android:allowBackup="true"
    android:dataExtractionRules="@xml/data_extraction_rules"
    android:fullBackupContent="@xml/backup_rules"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true"
    android:theme="@style/Theme.MyApplication"
    tools:targetApi="31">
    <activity
        android:name=".SingleInstanceActivity"
        android:launchMode="singleInstance"
        android:exported="true" />
    <activity
        android:name=".MainActivity"
        android:exported="true"
        android:label="@string/app_name"
        android:theme="@style/Theme.MyApplication">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
</application>

调用栈回溯

即然SingleInstanceActivity被销毁了,那么我们只需要在Activity生命周期中添加日志,来看下onDestroyed函数是怎么驱动调用的即可,从Activity生命周期可知,在Framework中框架通过ClientLifecycleManager类来管理Activity的生命周期变化,在该类的scheduleTransaction函数中,Activity的每一种生命周期类型均被包装成一个ClientTransaction来处理,在该函数中添加日志,打印调用栈,即可确定是那个地方销毁了SingleInstanceActivity,添加日志的代码如下:

24-2-3

随后编译framework.jar并push到设备上,查看日志,可以看到SingleInstanceActivity是在Task类的removeActivities方法中被销毁的,日志如下:

24-2-4

按照如上的思路,逐步类推,添加日志,查看调用栈,我们最终追溯到ActivityThread的handleResumeActivity,在该函数的最后,添加的IdlerHandler里面会执行RecentTasks的onActivityIdle方法,在该函数的调用流程里,会判断当前resume的Activity是不是桌面,是的话在HiddenTask不为空的情况下,就会执行removeUnreachableHiddenTasks的逻辑,销毁SingleInstanceActivity(这里的代码分支为android-13.0.0_r31)。

完整的正向调用流程如下图所示:

SingleInstance Task release process 1

remove-hidden-task机制

前文中我们已经跟踪到Activity销毁的调用流程,那么为什么要销毁SingleInstanceActivity呢?我们继续看前文中的日志,可以看出Activity销毁的原因是:remove-hidden-task。

24-2-6

那么这个remove-hidden-task到底是用来干嘛的呢?我们来看下代码提交信息:

24-2-1

从代码提交说明不难看出,这里的意思是:当我们向最近任务列表中添加一个任务时,会移除已不可达/未激活的Task,这里我们的SingleInstanceActivity所在的Task被判定为不可达/未激活状态,所以被这套机制移除了。

不可达/未激活的Task

那么为什么SingleInstanceActivity被认为是不可达的呢?我们进一步追踪代码,可以看到RencentTasks.removeUnreachableHiddenTasks移除的是mHiddenTasks中的任务,代码如下:

24-2-7

这样我们就只需要搞清楚什么样的Task会被加入mHiddenTasks中即可,mHiddenTasks.add的调用代码如下所示:

24-2-8

24-2-9

从上述代码可知,在removeForAddTask中通过findRemoveIndexForAddTask来查找当给定Task添加到最近任务列表时,需要被移除的Task,在findRemoveIndexForAddTask中最典型的一种场景就是当两个Task的TaskAffinity相同时,当后来的Task被添加到最近任务列表时,前一个Task会被销毁,这也就意味着在SingleInstanceActivity按Home键,MainActivity也会被销毁,经过实践,确实是这样。

解决方案

前文中已探讨了remove-hidden-task的运行机制,那么解决方案也就很简单了,给SingleInstanceActivity添加独立的TaskAffinity即可(注意:此时SingleInstanceActivity会显示在最近任务中,如果不想显示,请指定android:excludeFromRecents=“true”)。

影响范围

经排查,Google Pixel记性从Android 12开始支持该特性,针对国内定制厂商而言,大多数应该是在Android 13跟进的,大家可以测试看看。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/784170.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

摸索graphQL在前端vue中使用过程(四)

请求网址https://hasura.io/learn/graphql&#xff0c;他这个Authorization好像每天就会一次变化&#xff0c;需要注意。 之前用到了一种类型ID&#xff0c;也就是说&#xff0c;在GraphQL的查询标量的过程中。 标量:就是被查询的字段名称。 这里再补充一点知识&#xff0c;统…

Android 包体积资源优化实践

1 插件优化 插件优化资源在得物App最新版本上收益12MB。插件优化的日志在包体积平台有具体的展示&#xff0c;也是为了提供一个资源问题追溯的能力。 1.1 插件环境配置 插件首先会初始化环境配置&#xff0c;如果机器上未安装运行环境则会去oss下载对应的可执行文件。 1.2 图…

Windows 在VMware16.x安装Win11系统详细教程

文章目录 一、准备二、创建虚拟机1. 创建新的虚拟机2. 选择虚拟机硬件兼容性3. 安装客户机操作系统4. 选择客户机操作系统5. 命名虚拟机6. 固件类型7. 处理器配置8. 此虚拟机内存9. 网络类型10. 选择I/O控制器类型11. 选择磁盘类型12. 选择磁盘13. 指定磁盘容量14. 指定磁盘文件…

【深度学习】日常笔记15

训练集和测试集并不来⾃同⼀个分布。这就是所谓的分布偏移。 真实⻛险是从真实分布中抽取的所有数据的总体损失的预期&#xff0c;然⽽&#xff0c;这个数据总体通常是⽆法获得的。计算真实风险公式如下&#xff1a; 为概率密度函数 经验⻛险是训练数据的平均损失&#xff0c;⽤…

python机器学习(四)线性代数回顾、多元线性回归、多项式回归、标准方程法求解、线性回归案例

回顾线性代数 矩阵 矩阵可以理解为二维数组的另一种表现形式。A矩阵为三行两列的矩阵&#xff0c;B矩阵为两行三列的矩阵&#xff0c;可以通过下标来获取矩阵的元素&#xff0c;下标默认都是从0开始的。 A i j : A_{ij}: Aij​:表示第 i i i行&#xff0c;第 j j j列的元素。…

N位分频器的实现

N位分频器的实现 一、 目的 使用verilog实现n位的分频器&#xff0c;可以是偶数&#xff0c;也可以是奇数 二、 原理 FPGA中n位分频器的工作原理可以简要概括为: 分频器的作用是将输入时钟频率分频,输出低于输入时钟频率的时钟信号。n位分频器可以将输入时钟频率分频2^n倍…

linux进阶-I.MX 6ULL

目录 启动模式&#xff08;8引脚设置启动模式&#xff09; 对应原理图 boot ROM程序 空偏移 映像向量表&#xff08;Image vector table&#xff0c;IVT&#xff09; IVT结构体 Boot data DCD&#xff08;外设寄存器配置信息&#xff0c;初始化关键外设&#xff09; NXP…

如何使用 After Effects 导出摄像机跟踪数据到 3ds Max

推荐&#xff1a; NSDT场景编辑器助你快速搭建可二次开发的3D应用场景 在本教程中&#xff0c;我将展示如何在After Effects中跟踪实景场景&#xff0c;然后将相机数据导出到3ds Max。 1. 项目设置 步骤 1 打开“后效”。 打开后效果 步骤 2 转到合成>新合成以创建新合…

Docker Compose(九)

一、背景&#xff1a; 对于现代应用来说&#xff0c;大多数都是通过很多的微服务互相协同组成一个完整的应用。例如&#xff0c;订单管理、用户管理、品类管理、缓存服务、数据库服务等&#xff0c;他们构成了一个电商平台的应用。而部署和管理大量的服务容器是一件非常繁琐的事…

【图像处理】使用自动编码器进行图像降噪(改进版)

阿里雷扎凯沙瓦尔兹 一、说明 自动编码器是一种学习压缩和重建输入数据的神经网络。它由一个将数据压缩为低维表示的编码器和一个从压缩表示中重建原始数据的解码器组成。该模型使用无监督学习进行训练&#xff0c;旨在最小化输入和重建输出之间的差异。自动编码器可用于降维、…

宋浩线性代数笔记(二)矩阵及其性质

更新线性代数第二章——矩阵&#xff0c;本章为线代学科最核心的一章&#xff0c;知识点多而杂碎&#xff0c;务必仔细学习。 重难点在于&#xff1a; 1.矩阵的乘法运算 2.逆矩阵、伴随矩阵的求解 3.矩阵的初等变换 4.矩阵的秩 &#xff08;去年写的字&#xff0c;属实有点ugl…

Android 之 Canvas API 详解 (Part 2) 剪切方法合集

本节引言&#xff1a; 本节继续带来Android绘图系列详解之Canvas API详解(Part 2)&#xff0c;今天要讲解的是Canvas 中的ClipXxx方法族&#xff01;我们可以看到文档中给我们提供的Clip方法有三种类型&#xff1a; clipPath( )&#xff0c;clipRect( )&#xff0c;clipRegion(…

Mybatis的基本操作--增删改查

目录 查看数据 无参数 一个参数 多个参数 添加数据 修改数据 删除数据 查看数据 分三种情况&#xff1a;无参&#xff0c;有一个参数&#xff0c;有多个参数的情况。 &#xff08;这里的详细操作步骤是博主的上一篇博客写的&#xff1a;初识Mybatis&#xff0c;并创建第…

2023年VSCode插件最新推荐(54款)

本文介绍前端开发领域常用的一些VSCode插件&#xff0c;插件是VSCode最重要的组成部分之一&#xff0c;本文列出了我自己在以往工作经验中积累的54款插件&#xff0c;个人觉得这些插件是有用或有趣的&#xff0c;根据它们的作用&#xff0c;我粗略的把它们分成了代码管理、文本…

css实现纹理条纹,波点背景效果

css实现纹理条纹&#xff0c;波点背景效果 本文目录 css实现纹理条纹&#xff0c;波点背景效果效果一&#xff1a;水平条纹效果二&#xff1a;竖向条纹效果三&#xff1a;斜条纹效果四&#xff1a;网格效果五&#xff1a;象棋盘1效果六&#xff1a;象棋盘2效果七&#xff1a;红…

代码重构的时机与方法

&#x1f431; 个人主页&#xff1a;不叫猫先生&#xff0c;公众号&#xff1a;前端舵手 &#x1f64b;‍♂️ 作者简介&#xff1a;2022年度博客之星前端领域TOP 2&#xff0c;前端领域优质作者、阿里云专家博主&#xff0c;专注于前端各领域技术&#xff0c;共同学习共同进步…

机器人制作开源方案 | 智能垃圾桶

1. 功能说明 智能垃圾桶是一种利用物联网技术和智能感知能力的智能设备&#xff0c;旨在提高垃圾分类和处理的效率。通常具备以下特点和功能&#xff1a; ① 智能感知&#xff1a;智能垃圾桶配备各种传感器&#xff0c;如压力传感器、红外线传感器等&#xff0c;可以实时感知…

Flutter实现点击头像更新头像的功能,本地相册选取和调用相机两种方式的实现

文章目录 需求实现的效果如图代码实现代码分析用InkWell包住了我们的头像&#xff0c;这样来监听点击头像的事件用showDialog弹出提示框让用户选择是从相册里选择头像还是使用相机来拍照用image_picker实现从设备的相册或相机中选择图片或拍照 需求 Flutter实现点击头像更新头…

数字孪生管控系统,智慧园区楼宇合集

智慧园区是指将物联网、大数据、人工智能等技术应用于传统建筑和基础设施&#xff0c;以实现对园区的全面监控、管理和服务的一种建筑形态。通过将园区内设备、设施和系统联网&#xff0c;实现数据的传输、共享和响应&#xff0c;提高园区的管理效率和运营效益&#xff0c;为居…