如何应对 Android 面试官 -> 启动如何进行优化(上)?玩转 Android StartUp

news2024/9/24 21:20:00

前言


在这里插入图片描述

本章主要围绕 App 的启动流程如何优化进行讲解;

将启动优化,首先要了解的就是 app 的启动流程,只有清晰并完善的了解了 启动流程 才能更好的进行优化;

App 启动流程

在将 AMS 的时候,其实已经讲解了 App 的启动流程,感兴趣的可以翻看下我之前的文章;

在这里插入图片描述

这里我们贴一张启动流程图;

整体流程就是当我们点击桌面图标启动某一个应用层的时候,首先会在 Launcher 进程中通过 ActivityManagerProxy 跨进程通信发送 startActivtity 并代理到 system_server(AMS) 进程, AMS 发现这个 Activity 所在的进程已经存在,则直接启动这个 Activity,(这就是所谓的热启动),如果不存在,则通知 Zygote 进程 fork 出一个进程给目标 App 使用,并通知 AMS,由 AMS 来启动目标 Activity;而启动目标 Activity 之前,先启动 Application,在 Application 启动之后才会启动 Activity;

整体可以分为三个大的阶段

  1. 点击桌面 Launcher 的应用图标,通过与 AMS 通信,启动应用的过程;
  2. 应用 Application 执行过程;
  3. 启动 MainActivity 执行过程;

所以这里的启动其实是有三种状态的:冷启动、温启动、热启动

启动方式

冷启动

冷启动是指应用从头开始启动:系统进程在冷启动后才创建应用进程。发生冷启动的情况包括应用自设备启动后或系统终止应用后首次启动

热启动

在热启动中,系统的所有工作就是将 Activity 带到前台。只要应用的所有 Activity 仍驻留在内存
中,应用就不必重复执行对象初始化、布局加载和绘制

温启动

包含了在冷启动期间发生的部分操作;同时,它的开销要比热启动高。有许多潜在状态可视为温启动。例如:

用户在退出应用后又重新启动应用。进程可能未被销毁,继续运行,但应用需要执行
onCreate() 从头开始重新创建 Activity;

系统将应用从内存中释放,然后用户又重新启动它。进程和 Activity 需要重启,但传递到 onCreate() 的已保存的实例 state bundle 对于完成此任务有一定助益;

启动时长统计

Displayed

app启动完成之后,ActivityManager 会打印一个 Displayed 展示启动时间;

adb 命令

adb shell am start -s -w 「packageName/ .activityName」

  • waitTime 总的耗时,包括前一个应用Activity pause的时间和新应用启动的时间
  • thisTime 表示一连串启动Activity的最后一个Activity的启动耗时
  • totalTime 表示新应用启动的耗时,包括新进程的启动和Activity的启动,但不包括前一个应用Activity pause的耗时;

在这里插入图片描述

插桩 + systrace

通过插桩,我们可以看到应用主线程和其他线程的函数调用流程;

CPU Profile

AS 之后,我们一般都是通过 CPU Profile 对 App 的启动耗时进行采样优化;

当我们需要对一个 app 进行采样的时候,我们需要在 Edit Configuration 中进行相关配置信息的打开

在这里插入图片描述

选择 Method Trace 之后,使用 profile 的方式启动 App

在这里插入图片描述

开启 trace 之后,采集一段时间(跳转到第一个页面之后)可以点击 stop 停止采集;

在这里插入图片描述

之后生成对应的 trace 信息;

其中橙色表示系统方法执行时间,绿色表示 app 方法执行时间,蓝色表示三方 sdk 执行时间;

每一个方法,x 轴越大,表示花费的时间越多,通过放大,可以看到我们每一个方法的执行时间,包括 Application 类加载等的创建时间

重点关注绿色区域,逐个的分支查看 app 中耗时的方法;

右侧区域分为四种分析方式:Summary、Top Down、Flame Chat、Bottom Up

Summary 并不是很方便的查看方法的细节;

我们切换到 Flame Chat(火焰图) 来看下:

在这里插入图片描述
这个就是和 Summary 反向的分析图,从下往上分析;

我们切换到 Top Down(主要分析耗时) 来看下:
在这里插入图片描述

Top Down 比较直观的看到每个方法的执行耗时,以及内部方法的执行耗时;
在这里插入图片描述

App 启动优化方式


根据启动流程的三大阶段,对应的三个阶段的优化方式;

第一阶段的优化 主要是桌面 Launcher 应用与 AMS 的交互,以及 AMS 启动应用的过程,这个阶段主要是系统 Framework 层在做,优化空间基本没有;

第二阶段的优化,对于 Application 的优化,主要包括三部分的优化,一是 attachBaseContext 的优化,二是 onCreate 回调方法的优化,三是应用执行到 MainActivity 之前的白屏处理;

第三阶段的优化,主要是第一个 Activity 运行的阶段,直到 Activity 执行完成 onResume 函数,对应的就是 onCreate、onStart、onResume 的优化;

应用执行到 MainActivity 之前的白屏处理

Activity 真正展示的是 Window,对应的唯一实例就是 PhoneWindow,也就是到 PhoneWindow 展示之后,用户才能看到实际的内容;

这个流程其实是:点击 Launcher 桌面图图标到第一个 Activity 的 PhoneWindow 展示之前,这个期间内应该展示什么来规避黑屏或者白屏的 case;

产生这个黑白屏的 case 其实跟我们设置的启动的第一个 Activity 的主题有关系,也就是 windowBackground 依赖这个 theme,如果 theme 设置的是白色主题,那么 windowBackground 默认就是白色,启动就会白屏;

在这里插入图片描述

根据流程图,我们进入 PhoneWindowManager 的 addSplashScreen 方法看下,黑屏开始的地方在哪里?

public StartingSurface addSplashScreen(IBinder appToken, int userId, String packageName,
        int theme, CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes,
        int icon, int logo, int windowFlags, Configuration overrideConfig, int displayId) {
      
    // 省略部分代码
    // 黑白屏开始的地方,就是 addView 添加要显示的 View 的时候;
    wm.addView(view, params);
    return view.getParent() != null ? new SplashScreenSurface(view, appToken) : null;
}

那么,怎么优化这个黑白屏的问题,我们可以通过 PhoneWindowManager 的源码来看下:

private void addSplashscreenContent(PhoneWindow win, Context ctx) {
    final TypedArray a = ctx.obtainStyledAttributes(R.styleable.Window);
    final int resId = a.getResourceId(R.styleable.Window_windowSplashscreenContent, 0);
    a.recycle();
    if (resId == 0) {
        return;
    }
    final Drawable drawable = ctx.getDrawable(resId);
    if (drawable == null) {
        return;
    }

    // We wrap this into a view so the system insets get applied to the drawable.
    final View v = new View(ctx);
    v.setBackground(drawable);
    win.setContentView(v);
}

可以看到,PhoneWindowManager 通过获取 theme 中定义的 windowSplashscreenContent 来获取一个 drawable 设置给 PhoneWindow;

PS:这个 API 需要 API >= 26,如果最低版本小于 26 则还是通过 windowBackground 来设置;

那么,可能会有人有疑问了,这个 windowSplashscreenContent 属性比 windowBackground 强大在了哪些地方呢?

windowBackground 只能设置一张图片,而 windowSplashscreenContent 借助 Jetpack 的 SplashScreen 可以展示一个开屏动画;

attachBaseContext 优化

可以参考字节的 MutilDex 优化启动速度,核心是:去掉了 dex 转 zip 的操作,优化了启动速度,而不是多进程;

onCreate 优化

onCreate 方法中,如果使用 setContentView 方式,目前只能通过减少 xml 层级的方式来降低启动耗时,因为 setContentView 中充斥着大量的反射逻辑来创建 View;

所以,如果 xml 的绘制比较简单,建议使用 new View 的方式,通过 addView 来实现 View 的创建和绘制;

onResume 优化

如果页面布局是 ViewPager + Fragment 的方式,通常采用懒加载的方式,来进行页面渲染的优化;

布局层级的优化

使用 约束布局 替换普通的布局,优化渲染层级,减少绘制时长;

使用布局的异步加载 AsynLayoutInflater

AsyncLayoutInflater(this).inflate(R.layout.activity_debug, null, object : OnInflateFinishedListener{
    override fun onInflateFinished(view: View, p1: Int, p2: ViewGroup?) {
        setContentView(view)
    }
})

但是 AsyncLayoutInflater 的使用也是有一些限制的,我们可以先尝试下能不能在实际的项目中使用它;

延迟任务优化

通过 handler 的 addIdleHandler 方法添加延迟任务,会在主线程空闲的时候执行;

线程优化

线程的优化主要在于减少 CPU 调度带来的波动,让应用的启动时间更加稳定;

  • 控制线程数量;线程的优化一方面是控制线程数量,线程数量太多会相互竞争 CPU 资源,因此要有统一的线程池,并且根据机器性能来控制数量;
  • 检查线程间的锁;
    • 防止主线程因为其他线程的锁而等待空转
    • 为各个任务建立依赖关系,最终构成一个有向无环图。对于可以并发的任务,会通过线程池最大程度提升启动速度,通过启动框架进行优化,例如:Android StartUp、Aplha、mmkernel

GC 优化

  • 在启动过程,要尽量减少 GC 的次数,避免造成主线程长时间的卡顿;
  • 通过 systrace 单独查看整个启动过程 GC 的时间;
  • 启动过程避免进行大量的字符串操作,特别是序列化跟反序列化过程。一些频繁创建的对象,例如网络库和图片库中的 Byte 数组、Buffer 可以复用。如果一些模块实在需要频繁创建对象,可以考虑移到 Native 实现;
  • 监控启动过程总 GC 的耗时情况,特别是阻塞式同步 GC 的总次数和耗时
// GC使用的总耗时,单位是毫秒
Debug.getRuntimeStat("art.gc.gc-time");
// 阻塞式GC的总耗时
Debug.getRuntimeStat("art.gc.blocking-gc-time");

系统调优优化

在启动过程,我们尽量不要做系统调用,例如 PackageManagerService 操作、Binder 调用等待;

  • 在启动过程也不要过早地拉起应用的其他进程,System Server 和新的进程都会竞争 CPU 资源。特别是系统内存不足的时候,当我们拉起一个新的进程,可能会成为“压死骆驼的最后一根稻草”。它可能会触发系统的 low memory killer 机制,导致系统杀死和拉起(保活)大量的进程,从而影响前台进程的 CPU;

I/O 优化

  • 启动过程不建议出现网络 I/O;
  • 启动过程尽可能的减少磁盘 I/O,只解析启动过程用到的数据项则会很大程度减少解析时间,启动过程适合使用随机读写的数据结构
    • 可以将 ArrayMap 改造成支持随机读写、延时解析的数据存储方式

数据重排

原理:Dex 文件用的到的类和安装包 APK 里面各种资源文件一般都比较小,但是读取非常频繁。我们可以利用系统这个机制将它们按照读取顺序重新排列,减少真实的磁盘 I/O 次数;

  • 类重排
    • 第一步、启动过程类加载顺序可以通过复写 ClassLoader 得到
    • 第二步、然后通过 ReDex(facebook开源) 的Interdex调整类在 Dex 中的排列顺序;
  • 资源文件重排
    • 通过修改 Kernel 源码,单独编译了一个特殊的 ROM;
    • 支付宝 App 构建优化解析:通过安装包重排布优化 Android 端启动性能

类加载优化

通过 Hook 的方式 去掉 类加载的过程中 verify class 的步骤;
在这里插入图片描述

下一张预告


Android StartUp 原理解析

欢迎三连


来都来了,点个关注,点个赞吧,你的支持是我最大的动力~

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

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

相关文章

喜报! 炼石入选中国信通院《数据安全产业技术产品服务全景图》

近日,在2024中国国际大数据产业博览会“数据安全产业发展”交流活动上,中国信息通信研究院安全研究所副所长魏薇发布了《数据安全产业技术产品服务全景图》(以下简称“全景图”)。全景图从数据安全产业的概念和内涵出发&#xff0…

强大的3款自动爬虫利器,再也不用手撸代码了

爬虫是一种自动浏览互联网的程序,它按照一定的算法顺序访问网页,并从中提取有用信息。爬虫通常由以下几部分组成: - 用户代理(User-Agent):模拟浏览器访问,避免被网站识别为机器人。 - 请求处…

Springcould -第一个Eureka应用 --- day02

标题 Eureka工作原理Spring Cloud框架下的服务发现Eureka包含两个组件,分别是:Eureka Server与Eureka Client。Eureka Server:Eureka Client: 搭建Eureka Server步骤:步骤1:创建项目,引入依赖步…

iMeta | 中科院植物所周世良团队和河北工程大学刘艳磊团队揭示现代丝绸之路东段植物物种多样性格局及其影响因素

现代丝绸之路东段植物物种多样性格局及其影响因素研究 iMeta主页:http://www.imeta.science 研究论文 ● 原文链接DOI: https://doi.org/10.1002/imt2.74 ● 2023年1月9日,中国科学院植物研究所周世良团队与河北工程大学刘艳磊团队在iMeta在线发表了题…

【32单片机篇】项目:智能台灯

一、项目需求 1. 红外传感器检测是否有人,有人的话实时检测距离,过近则报警;同时计时,超过固定时间则报警; 2. 按键 1 切换工作模式:智能模式、按键模式、远程模式; 3. 智能模式下,…

支付宝开放平台-开发者社区——AI 日报「9 月 10 日」

1 Anthropic安全负责人:在超级A!「毀灭」人类之前,我们可以做这些准备 机器之心 丨阅读原文 Anthropic公司为应对A发展带来的港在风险,发布了负责任扩展策路(RSP),旨在通过技术和组织协议管理功能日益强大的Al系统。…

离离原上谱,公司裁员,员工排队抢名额

排队等裁员 别的公司裁员,大多数员工都提心吊胆,最近有家公司裁员,出现了员工排队抢名额的局面。 这家公司是「东风本田」。 近期行业内部传出消息,指出东风本田将面临战略调整,计划实施一次规模较大的裁员行动&#x…

AV1 Bitstream Decoding Process Specification:符号和缩写术语

原文地址:https://aomediacodec.github.io/av1-spec/av1-spec.pdf没有梯子的下载地址:AV1 Bitstream & Decoding Process Specification摘要:这份文档定义了开放媒体联盟(Alliance for Open Media)AV1视频编解码器…

网络安全主动防御技术原理与应用

入侵阻断技术与应用 入侵阻断:网络安全主动防御的技术方法 基本原理:对目标网络攻击行为进行阻断 入侵防御系统(IPS) 基本原理:根据网络包特性及上下文进行攻击行为判断老控制包转发 工作机制:类似路由…

2024下半年软考机考操作指南来了!赶紧收藏!

自2023年下半年软考全部科目改革为机考方式后,到现在已经进行过两场考试,鉴于有很多考生是初次参加软考,就给大家介绍下关于软考机考的具体操作指南,希望对大家有所帮助。 一、2024年软考机考考试流程 1、进入机考系统后&#xf…

AI人工智能将推动人类发展

AI人工智能将推动人类发展 AI(人工智能)作为一种前沿技术,正以前所未有的速度改变着我们的世界,并在多个领域展现出巨大的潜力,这些潜力无疑将推动人类社会的发展。以下是一些AI如何推动人类发展的主要方面&#xff1…

php代码实例强制下载文件代码例子

php代码实例强制下载文件代码例子 $filename $_GET[file]; //Get the fileid from the URL // Query the file ID $query sprintf("SELECT * FROM tableName WHERE id %s",mysql_real_escape_string($filename)); $sql mysql_query($query); if(mysql_num_rows…

「 自动化测试 」面试题..

1.你会封装自动化测试框架吗? 自动化框架主要的核心框架就是分层PO模式:分别为:基础封装层BasePage,PO页面对象层,TestCase测试用例层。然后再加上日志处理模块,ini配置文件读取模块,unittestd…

MybatisX-Generator自动代码生成插件

一、概述 MybatisX-Generator是mybatis-plus的代码自动生成插件,用在idea的开发工具上,我们可以idea上安装这个插件,然后通过idea自带的数据库进行使用,打开idea的数据库database,链接一个数据库,然后对你…

MLP 多层感知机

为了拟合更特殊的函数,在网络中加入多个隐藏层,克服线性的限制。最后一层可以看作线性predictor。 一、 1.最简单流程 输入x矩阵,含有n个样本,每个样本有d个特征。经过隐藏层H将维度转化为h,在经过最后的输出层O将维…

浙江搞一场知识竞赛活动要多少钱

浙江省属于国内比较富裕地方,消费相比其他地方较高,在那里举办一场知识竞赛活动根据规模划分:小型知识竞赛的预算大致在2-3万;中型知识竞赛活动3-10万之间,高端知识竞赛10至30万元之间或更高。以上费用均未考虑场地和选…

十年电商经验分享:从0-1打单品保姆级教程(下)

接着上一篇《十年电商经验分享:从0-1打单品保姆级教程(上)》,各位觉得有参考意义的商家朋友们可以点赞收藏一下。 5、制作 sku 图片以及 sku 名称卖点 这里很多商家可能不太注意,这个也是优化转化率最好的一块内容&a…

【数据结构与算法 | 灵神题单 | 删除链表篇】力扣2487, 237

1. 力扣2487:从链表中删除节点 1.1 题目: 给你一个链表的头节点 head 。 移除每个右侧有一个更大数值的节点。 返回修改后链表的头节点 head 。 示例 1: 输入:head [5,2,13,3,8] 输出:[13,8] 解释:需…

数字影像产业基地:绿色、智能、创新,如何并存发展?

在当今快速发展的时代,数字影像产业基地正以独特的魅力展现着绿色、智能、创新并存发展的崭新风貌。 绿色,是数字影像产业基地的底色。随着环保意识的不断提高,基地积极践行绿色发展理念。 智能,是数字影像产业基地的核心竞争力。…

4 个步骤带你快速上手 Einstein Copilot for Tableau

如果你的企业仍未部署或希望迁移至 Tableau Cloud,可考虑订阅 Tableau 高级套件。 自 Einstein Copilot for Tableau 发布以来,相信部分用户已经尝试过在 Tableau Cloud 中借助 AI 对话助理,快速解决数据分析中的问题,获得更准确的…