Android---Android 是如何通过 Activity 进行交互的

news2025/1/27 12:58:11

相信对于 Android 工程师来说,startActivity 就像初恋一般。要求低,见效快,是每一个菜鸟 Android 工程师迈向高级 Android 工程师的必经阶段。经过这么多年的发展,startActivity 在 google 的调教下已经变得愈发成熟,对工程师的要求也越来越高。、

taskAffinity

通过设置不同的启动模式可以实现调配不同的 Task,但是 taskAffinity 在一定程度上也会影响任务栈的调配流程。每一个 Activity 都有一个 Affinity 属性,如果不在清单文件(AndroidManifest.xml)文件中指定,默认为当前应用的包名。taskAffinity 主要有以下几点需要注意:

1. taskAffinity 会默认使 Activity 在新的栈中分配吗?

在一个 Android 项目 LagouTaskAffinity 中,创建两个 Activity,First 和 Second。除了类名之外,其他都是默认配置。

点击 First 中的 Button,从 First 页面跳转到 Second 页面。然后命令行中执行以下命令

adb shell dumpsys activity activities

这个命令会将系统中所有存活的 Activity 信息打印到控制台,如下:

上图中的 TaskRecord 代表一个任务栈。在这个栈中存在两个 Activity 实例,First 和 Secode。其中,Second 位于栈顶。

接下来,修改 Second 的 taksAffinity 为 lagou.affinity,使他也 First 的不同。

重写运行代码,并再次查看任务栈中的代码,如下:

从上图可以看出,虽然 First 和 Second 的 taskAffinity 不同,但是它们都被创建在一个任务栈中。

如果将 Second 的 launchMode(启动模式) 改为 singleTask,再次重新运行,结果如下:

两个 Activity 被分配到了不同的任务栈中。

所以回到上面的问题,taskAffinity 会默认使 Activity 在新的栈中分配吗?

答案是:不会。单纯使用 taskAffinity 不能导致 Activity 被创建在新的任务中,需要配合 singleTask 或者 singleInstance

2. taskAffinity + allowTaskReparenting

allowTaskReparenting 赋予 Activity 在各个 Task 中间转移的特性。一个在后台任务栈中的 Activity A,当有其他任务进入前台,并且 taskAffinity 与 A 相同,则会自动将 A 添加到当前启动的任务栈中。

生活中的场景:

1. 在某外卖App 中下好订单后,跳转到支付宝进行支付。当在支付宝中支付成功后,页面停留在支付宝支付成功页面。

2. 按 Home 键(支付成功的页面加入到了后台任务栈中),在主页面重新打开支付宝,页面上显示的是之前的支付成功页面。(有一个与"支付成功"页面相同taskAffinity的任务进入到前台)

3. 再次进入外面 App,可以发现支付成功页面已经消失。

造成上面现象的原因就是 allowTaskReparenting 属性。

实例演示

分别创建2个Android工程: First 和 TaskAffinityReparent

●在First中有3个Activity: FirstA、 FirstB、 FirstC,打开顺序依次是FirstA -> FirstB -> FirstC 。其中FirstC的taskAffinity为“lagou.affinity” ,且allowTaskReparenting属性设置为true

● TaskAffinityReparent 中只有一个 Activity:ReparentActivity,并且其TaskAffinity也等于“lagou.affinity”。

● 将这两个项目分别安装到手机上之后,打开First App。并从FirstA开始跳转到FirstB,再进入FirstC页面,然后按Home键,使其进入后台任务。

此时,系统中的 Activity 信息如下

● 打开TaskAffinityReparent 项目。屏幕上本应该显示 ReparentActivity,但实际上显示的却是 FirstC的内容。并且系统中 Activity 信息如下:

可以看出 FirstC 已经从后台任务栈中移动到与 ReparentActivity 相同的任务栈中。并且,FirstC 位于栈顶位置,再次点击返回键,才会显示 ReparentActivity 的内容。

通过 Binder 传递数据的限制

Activity 在界面跳转时,使用 Intent 传递数据是最常用的操作。但 Intent 传值偶尔也会导致程序崩溃。如下代码所示:

public void startFirstB(View view){
    Intent intent = new Intent(this, FirstB.class);
    intent.putExtra("bean", new Bean()); // 传递 Bean 中的数据
    startActivity(intent);
}

static class Bean implements Serializable{
    private byte[] data = new byte[1024 * 1024];
    String str = "data string";
}

在 startFirstB 中跳转 FirstB 页面,并通过 Intent 传递 Bean 中数据。但是,执行上述代码会报如下错误

上面日志中的信息是,intent 中的数据过大。最终的原因是 Android 对使用 Binder 传递数据进行了限制,通常情况为 1MB。但是,根据不同版本、不同厂商,这个值会有区别。

解决 Binder 传递数据的限制

1. 减少通过 Intent 传递的数据,将非必须字段使用 transient 关键字修饰。

比如上述代码 Bean 类中,byte 类型数据 data 并非必须使用的数据,则需要避免将其序列化,如下所示:

添加 transient 修饰后,再次运行代码,则不会再报异常。

2. 将对象转化为 Json 字符串,减少数据体积。

将类中数据转化为 JSON 字符串可以减少数据大小,比如使用 Gson.toJson() 方法。大多数时候,将类转化为 Json 字符串之后,还是会超出 Binder 限制,说明实际要传递的数据是很大的。这种情况则需要考虑使用本地持久化来实现数据共享,或者使用 EventBus 来实现数据传递

通过 Process 造成多个 Application

在自定义的 Application 中做一些初始化的操作,比如 App 分包、推送初始化、图片加载库的全局配置等。 

但实际上,Activity 可以在不同的进程中启动。而每一个进程都会创建一个 Application,因此有可能造成 Appcation 的 onCreate() 方法被多次执行,比如以下代码:

RemoteActivity 的 process 为 “lagou.process”,这会导致它在一个新的线程中创建。当中 MainActivity 跳转到 RemoteActivity 时,LagouApplication 会被再次创建。其代码如下:

最终打印日志如下:

可以看出,LagouApplication 的 onCreate() 方法被创建了2次。因此,各自初始化的操作也会执行2次。

解决办法:2种比较好的处理方法:

\bullet onCreate 方法中判断进程的名称,只有在符合要求的进程里,才执行初始化操作;

\bullet 抽象出一个与 Application 生命周期同步的类,并根据不同的进程创建相应的 Application 实例。

后台启动 Activity 失效

试想这样一种情况,当用户正在玩着游戏,手机后台可能有某个下载 apk 的任务,但 apk 下载完之后,突然弹出 apk 安装界面,中断了游戏界面的交互。这种情况便造成了极差的用户体验。

为了避免这种情况的发生,从 Android10(API 29) 开始,Android 系统对后台进程启动 Activity 做了一定限制。主要目的就是避免当前的交互不被打断。

但是,这也造成了一些其它实际问题,例如

在项目中有 Force Update 功能,当用户选择升级之后会在后台进行新的安装包下载任务。正常情况下下载功能要弹出 apk 安装界面,但是在某一版升级时突然很多用户反馈无法弹出下载界面。经过查看抓取的 log 信息,最终发现有个特点就是都发生在 Android 10 版本,是版本兼容问题。

Android 官方建议使用通知来替代直接启动 Activity 操作。也就是,当后台执行的任务执行完毕,并不会直接调用 startActivity 来启动新的界面。而是通过 notificationManager 来发送 notification 到状态栏。这样,既不会影响当前的交互操作,用户也能及时获取后台的进展情况。

 总结

使用startActivity时可能会遇到的问题:

● taskAffinity 实现任务栈的调配;

● 通过Binder传递数据的限制;

● 多进程应用可能会造成的问题;

● 后台启动Activity的限制。
 

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

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

相关文章

Python文件读写实战:处理日常任务的终极工具!

更多资料获取 📚 个人网站:涛哥聊Python Python文件的读写操作时,有很多需要考虑的细节,这包括文件打开方式、读取和写入数据的方法、异常处理等。 在本文中,将深入探讨Python中的文件操作,旨在提供全面的…

Python开发者的宝典:CSV和JSON数据处理技巧大公开!

更多资料获取 📚 个人网站:涛哥聊Python 在Python中处理CSV和JSON数据时,需要深入了解这两种数据格式的读取、写入、处理和转换方法。 下面将详细介绍如何在Python中处理CSV和JSON数据,并提供一些示例和最佳实践。 CSV数据处理…

项目配置vue.config jsconfig babel.config .prettierc .env .eslintrc

.env 在一个产品的前端开发过程中,一般来说会经历本地开发、测试脚本、开发自测、测试环境、预上线环境,然后才能正式的发布。对应每一个环境可能都会有所差异,比如说服务器地址、接口地址、websorket地址…… 等等。在各个环境切换的时候&am…

java的Timer全网最详细总结

1.简介 在Java 1.3版本中引入了Timer工具类,它是一个古老的定时器,通常与TimerTask和TaskQueue一起使用。Timer工具类的实现涉及到TimerTask类、Timer类、TimerQueue类和TimerThread类。其中,TimerQueue和TimerThread类与Timer类位于同一个类…

docker安装skyWalking笔记

确保安装了docker和docker-compose sudo docker -v Docker version 20.10.12, build 20.10.12-0ubuntu4 sudo docker-compose -v docker-compose version 1.29.2, build unknown 编写docker-compose.yml version: "3.1" services: skywalking-oap:image: apach…

创意作品管理软件 Bridge 2024 mac中文版 br2024功能特色

Bridge 2024 mac旨在连接不同的Ad obe应用程序,帮助用户更轻松地管理和编辑他们的创意作品。 Bridge 2024 mac软件特色和功能介绍 一致的用户体验:Bridge 2024现在具有增强的用户界面,可提供与其他Creative Cloud应用程序类似的体验。用户还…

《向量数据库指南》——向量数据库一些技术难点

一些技术难点 在文章的前半部分,我们列举了一些向量数据库应该具备的特性,然后比较了以 Milvus 为代表的向量数据库和 ANN 算法库、向量检索插件的不同之处。接下来,我们来聊聊构建向量数据库过程中会遇到的一些技术难点。 就好像一架飞机一样,内部每个零部件和系统相互连通…

PPP的建链过程

下图是PPP协议整个链路过程需经历阶段的状态转移图: 图1 PPP链路建立过程 PPP运行的过程简单描述如下: 通信双方开始建立PPP链路时,先进入到Establish阶段。 在Establish阶段,PPP链路进行LCP协商。协商内容包括工作方式是SP&am…

解决win10因为WSL问题无法正常启动docker

解决win10无法成功启动dockerdesktop因为WSL问题无法启动 问题起因解决方法 问题起因 因为需要在windows复现一个CVE漏洞,就需要安装在WIN10上装docker,但是在启动的时候出现下面报错。 然后查了一下是因为WSL的版本太低了。更新以后发现打开docker仍然…

C++20中的关键字

文章目录 前言关键字列表关键字含义总结 前言 源于上一篇中提到了 decltype是一个关键字,所以去查了现有的关键字列表,其实我对C/C的关键字还停留在上世纪大概30多个的时代,虽然知道C加了 auto、constexpr 等关键字,但查询过后才…

52 杨辉三角

杨辉三角 题解1 DP 给定一个非负整数 numRows,生成 杨辉三角的前 numRows 行。 在杨辉三角中,每个数是它左上方和右上方的数的和。 示例 1: 输入: numRows 5 输出: [[1],[1,1],[1,2,1],[1,3,3,1],[1,4,6,4,1]] 示例 2: 输入: numRows 1 输出: [[1…

关于操作系统中对进程管理的认识

文章目录 前言1.计算机组成简单认识2.操作系统1). 操作系统的管理2). 操作系统的目的 3.操作系统中的进程管理1). 进程是什么?2).Linux中的进程 前言 在介绍进程之前,我们需要先了解关于进程的一些边缘的操作系统知识 以及硬件知识,因为操作…

leetCode 1143.最长公共子序列 动态规划 + 图解

此题我的往期文章推荐: leetCode 1143.最长公共子序列 动态规划 滚动数组-CSDN博客https://blog.csdn.net/weixin_41987016/article/details/133689692?spm1001.2014.3001.5501leetCode 1143.最长公共子序列 一步步思考动态规划 优化空间复杂度_呵呵哒(&#xf…

Lua 调试库( debug )

一、调试库作用 使用调试库可以获取 Lua 环境运行过程中的变量和跟踪代码执行。 调试库主要分为两类函数:自省函数和钩子函数 自省函数:允许检查一个正在运行中的程序,例如活动函数的栈、当前正在执行的代码行、局部变量的名称和值。钩子函…

Java多线程悲观锁和乐观锁

悲观锁: 一上来就加锁,没有安全感,每次只能一个线程进入访问完毕后,再解锁。 线程安全,性能较差 乐观锁: 一开始不上锁,认为是没有问题的,大家一起跑,等要出现线程安全问…

SpringBoot--手写组件动态更新@Value的值

原文网址:SpringBoot--手写组件动态更新Value的值_IT利刃出鞘的博客-CSDN博客 简介 本文手写组件,动态更新SpringBoot里Value的值(无需重启服务)。 不是可以用RefreshScope吗?为什么要手写组件? 动态更…

docker数据卷+挂载(命令讲解+示例)

在容器中管理数据主要有两种方式: 数据卷(Volumes) 、挂载主机目录 (Bind mounts)。 一、数据卷 数据卷是一个可供一个或多个容器使用的特殊目录,可以在容器之间共享和重用。 特点: 对 数据卷 的修改会立马生效对 …

动态规划简述;斐波那契数列自顶向下和自底向上

概述 动态规划就是把一个问题分解为若干子问题,把子问题的解累加起来,就是当前问题的值。 斐波那契数列(自顶向下) 一个很好的演示demo, 在进行运算时,要用上备忘录(缓存)&#x…

从硬件结构到软件

先说说体系冯诺依曼的体系结构,有利于我们后面理解操作系统,软件再怎么发展,也必须遵守硬件的规则。 一 五大硬件理解 如下图: 1 为什么要有输入输出设备 很久以前,我们都是把指令打成孔,有孔无孔表示0,1&#xff0c…

柯桥日常口语学习|生活英语|实用口语口语天天练

1. How far is it from here? 离这儿有多远? 2. Can you give me a hand? 能帮帮我吗? 3. I cant lift my right arm. 我无法举起我的右手臂。 4. This bridge was built two years ago. 这座桥是在两年前建造的。 5. You should eat more. 你应该…