Android App 秒开实践方案~

news2024/12/25 23:52:39

一、背景

启动速度可以说是一个 APP 的门面,对用户体验至关重要。随着业务不断增加,需要初始化的任务也越来越多,如果放任不管,启动时长会逐步增加,为此雪球客户端针对应用启动时长做了大量优化工作。本文从应用启动基本原理出发,总结了雪球客户端启动优化的思路和遇到的问题。主要包括启动原理介绍、优化方案和线上验证等三方面内容。

二、启动原理

根据 Google 官方文档,应用启动分为以下三种类型:

  • 冷启动
  • 热启动
  • 温启动

冷启动

冷启动是指 APP 进程被杀死(系统回收、用户手动关闭等),启动 APP 需要系统重新创建应用进程,从用户点击应用桌面图标到第一个页面加载完成的全部过程。冷启动是启动类型中耗时最长的一种,也是启动优化最关键的优化点,下面我们来看一下冷启动的启动过程。

从上图可以看出 APP 冷启动可以分为以下三个过程:

  • 用户点击桌面 APP 图标,调用 Launcher.startActivity ,由 Binder 通知 system_server 进程,system_server 内部使用 ActivityManagerService 通知 zygote 创建应用子进程
  • 应用进程创建完成后,会加载 ActivityThread 并调用 ActivityThread.main 方法,用来实例化 ApplicationThread 、Lopper 和 Handler
  • ActivityThread 内部调用 attach 方法进行 Binder 通信回到 system_server 进程,执行 ActivityManagerService.attachApplication 完成 Application 的创建,同时启动第一个 Activity

我们可以换一种通俗易懂的描述:

想象一下把 Launcher 比做手机桌面,桌面里面很多 APP 可以理解成 Launcher 的孩子,zygote 就是一个进程,system_server 好比服务大管家,ActivityThread 好比每个 APP 进程自己的管家。

启动 APP 首先要通知服务大管家 (system_server),服务大管家 (system_server)收到通知后,会跟它的第一对接人 zygote 进程联系,请求 zygote 创建一个属于孩子的家,也就是 APP 自己的进程,进程创建完成后,接下来是属于孩子自己的工作,它开始使用自己的管家 ActivityThread 布置自己的家,可以简单把 Application 比做是大门,把 Activity 比作是卧室,AMS 是装修团队,ActivityThread 会不断和 AMS 交互,直到 Application 和 Activity 创建完毕,至此一个 APP 就启动完成了。

热启动

热启动是指应用程序从后台被唤起,此时应用进程仍然存在,应用启动无需创建子进程,但是可能会重新执行 Activity 的生命周期,在热启动中,系统的所有工作就是将您的 Activity 带到前台,只要应用的所有 Activity 仍驻留在内存中,应用就不必重复执行对象初始化、布局和绘制,例如用户按下 back 或者 home 键回到后台。

温启动

温启动包含了在冷启动期间发生的部分操作,同时它的开销要比热启动高,例如用户在退出应用后又重新启动应用,此时应用进程仍然存在,但应用必须通过调用 onCreate() 从头开始重新创建 Activity

冷启动是三种启动状态中最耗时的一种,启动优化也是在冷启动的基础上进行优化,热启动和温启动相对耗时较少,暂不考虑优化。

三、问题归因

工欲善其事必先利其器,要想优化启动时长,首先必须知道应用启动过程中发生了什么,以及耗时方法是哪些,下图列举了一些 APP 常用的性能检测工具:

adb shell

获取应用启动总时长 adb 命令:adb shell am start -W [packageName]/[packageName.xActivity]

详细使用可参考文档:https://developer.android.google.cn/studio/command-line/adb?hl=zh-cn

参数说明:

Activity:应用启动的第一个Activity

TotalTime:应用启动总时长,包括应用进程创建、Application 创建和第一个 Activity 创建并绘制完成到显示的所有过程,冷启动的情况下我们只需要关注 TotalTime 即可

Displayed

displayed 使用比较简单,我们只需要在 Logcat 中过滤关键字 displayed 即可看到应用启动的总时长,如下图所示,displayed 打印的时长跟 adb shell 几乎相同,也就是一次冷启动的总时长。

adb shell 和 displayed 都可以帮助我们快速获取应用启动时长,但是无法获取具体耗时方法的堆栈信息,应用启动的具体信息我们可以使用 Systrace 和 Traceview 来获取。

Systrace

Systrace 是 Android 平台自带的命令行工具,可记录短时间内的设备活动,并保存在压缩的文本文件中,该工具会生成一份报告,其中汇总了 Android 内核中的数据,例如 CPU 调度程序、磁盘活动和应用线程。

Systrace 工具默认在 Android SDK 里面,路径一般为 Android/sdk/platform-tools/systrace

使用 systrace 生成应用冷启动具体信息

  • 如果没有配置环境变量,先切到 systrace 目录下 cd ~/Library/Android/sdk/platform-tools/systrace
  • 执行 systrace.py -t 10 -o /Users/liuyakui/trace.html -a com.xueqiu.fund

或者直接用绝对路径执行 systrace

详细使用可参考文档:https://developer.android.google.cn/topic/performance/tracing

python ~/Library/Android/sdk/platform-tools/systrace/systrace.py -t 10 -o /Users/liuyakui/trace.html -a com.xueqiu.fund

systrace 报告如下图所示,这里仅摘取了启动优化所需要的主要信息:

  • 区域1代表 CPU 使用率,柱子越高,越密集代表 CPU 使用率越高
  • 区域2代表 CPU 编号,该设备是8核处理器,编号0-7,点击 CPU 编号区域,可以查看当前正在运行的任务
  • 区域3代表所有线程和方法具体耗时情况,可以帮助我们定位具体耗时方法

从上图可以看出在0-3秒内,CPU 平均利用率较低,特别是1-3秒这段时间,CPU 几乎处于闲置状态,提高 CPU 利用率,充分发挥 CPU 的性能,是我们主要的优化方向。

上述三部分区域所提供的信息,基本上可以帮助我们定位启动耗时问题,它提供了 CPU 使用情况以及每个线程工作情况,但它不能告诉我们具体的问题代码在哪里,我们要确定具体的耗时代码,可以使用 Traceview 工具。

Traceview

Traceview 能够以图形化的形式展示线程的工作状态,以及方法的调用堆栈和调用链,我们以 application onCreate 为例,统计 onCreate() 内部详细的方法调用,并生成 trace 报表。

详细使用可参考文档:https://developer.android.google.cn/studio/profile/traceview?hl=zh_cn

@Override
public void onCreate() {
    super.onCreate();
    Debug.startMethodTracing("app_trace");

    //初始化代码...

    //...

    Debug.stopMethodTracing();
}

应用启动完成后,会在 /sdcard/Android/data/com.xueqiu.fund/files 路径下生成一个 app_trace.trace 文件,直接用 AndroidStudio 打开即可,如下图所示:

trace 文件详细展示了每个线程的工作情况,以及每个线程内部具体的方法调用情况,下面简单介绍一下trace 报表中最重要的三块区域:

  • 区域1代表 CPU 使用情况,可以拖拽选择时间段
  • 区域2代表当前线程工作信息,截图所示为当前主线程在0-5s内所有的方法调用情况
  • 区域3代表当前线程内部的方法调用堆栈,以及方法耗时等信息,使用 Top Down 和 Bottom Up 可以对方法正反排序

trace 报表清晰的展示了每个线程对应的所有方法的调用链和耗时情况,很好的帮助我们定位启动过程中具体问题所在,为优化方案提供了重要的参考依据。

四、优化方案

经过上述分析,APP 启动问题主要集中在以下两个阶段:

  • Application 创建
  • 闪屏页绘制

因此下面主要是针对这两方面进行优化

Application 创建优化

从上述 Traceview 报表可以看出,影响 Application 创建的代码主要集中在 initThirdLibs 内部,我们来看一下 initThirdLibs 内部初始化代码执行流程。

initThirdLibs 内部包含了雪球客户端所有的初始化项,这些初始化任务不分主次和优先级都在主线程顺序执行,中间任意一个任务阻塞,都会影响 Application 的创建,而且随着业务不断迭代,初始化任务越来越多,Application 的创建时长也会继续累加。

因此梳理 initThirdLibs 内部任务的优先级,通过合理的方式统一调度,并对各个任务进行延迟初始化是优化 Application 创建的重要内容,延迟初始化主要实现的目标分为以下三点:

  • 提高 CPU 利用率,充分发挥 CPU 性能
  • 初始化任务 Task 处理,降低维护成本和提高任务调度的灵活性
  • 多线程处理,梳理各个 Task 的优先级,形成一个有向无环图

Task 任务流程图如下:

关于启动器实现核心逻辑为,自定义线程池,根据设备 CPU 情况动态计算线程数量,保证所有 Task 任务并发执行,并且相互独立,所有 Task 执行完毕后会最后执行 Final Task 用来做一些收尾的工作,或者有强依赖的任务,也可以放到 Final Task 中进行,这里推荐以下两种实现方式:

  • CountDownLatch
  • 自定义线程池

启动器伪代码如下:

//这里只是一段伪代码,帮助大家理解启动器的基本实现原理

TaskManager manager = new TaskManager();
ExecutorService service = createThreadPool();
final Task1 task1 = new Task1(1);
final Task2 task2 = new Task2(2);
final Task3 task3 = new Task3(3);
final Task4 task4 = new Task4(4);
for (int i = 0; i < n; i++) {
    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            manager.get(i).start();
        }
    };
    service.execute(runnable);
}

Task 调度完成后,将不依赖主线程的初始化任务,移动到并发 Task 中进行延迟初始化,进行统一管理并且避免阻塞主线程,提高 CPU 利用率。

闪屏页绘制优化

目前闪屏页主要承载的是业务广告,通过优化广告加载的逻辑可以间接调整页面的布局结构。

布局结构

闪屏页会预加载广告数据存到本地,每次应用启动从本地读取广告数据,这里我们可以优化无广告页面展示的逻辑,目前闪屏页无广告的时候仍然会加载布局文件,并设置默认的页面停留时长,理论上如果页面无广告,闪屏页创建完成后可以直接进入首页,不用加载页面的布局文件从而减少页面绘制时间,调整后页面广告加载逻辑核心代码如下:

private void prepareSplashAd() {
    //读取广告数据
    String jsonString = PublicSetting.getInstance().getSplashAd();
    if (TextUtils.isEmpty(jsonString)) {
        //无广告,关闭页面,进入首页
        exitDelay();
        return;
    }

    //加载布局文件
    View parentView = inflateView();
    setContentView(parentView);
    //显示广告
    AD todayAd = ads.get(0);
    showSplashAd(todayAd.imgUrl, todayAd.linkUrl);
}

优化结果

经过多个版本的线上数据采样,启动时长明显下降,以华为 Mate 30E Pro 为例,效果对比如下:

优化前

优化后

从上面对比中可以看到,在5年以内的旗舰机型上,启动时长从原来的 1.9s - 2.5s 降低到 0.75s - 1.2s ,整体降低60%左右,可以达到秒开的效果!CPU 活动转为密集型,充分发挥 CPU 的性能,提高了 CPU 的利用率。

五、总结

本文先介绍了应用启动的基本原理,以及如何通过各种检测工具定位影响启动速度的原因,最后重点阐述 Application 创建和闪屏页绘制两个阶段的优化方案。同时它也代表一组最佳实践,在后续的性能优化中,都是不错的选择。

其实启动优化的方案还有很多,但我们除了要关注启动优化本身,更需要制定长远的规划,设计适合自己的方案,为后续业务迭代做好铺垫,避免再次出现启动时长逐步增加的问题。


为了帮助能更好的掌握性能优化相关知识点,这准备了 性能优化知识点汇总和Android 性能监控框架 的学习文档,中间记录了 启动优化、内存优化、UI优化……等知识点,大家可参考:https://0a.fit/dNHYY

内存优化

UI优化

网络优化

Bitmap优化与图片压缩优化

多线程并发优化与数据传输效率优化,大家可参考:https://0a.fit/dNHYY

启动优化

体积包优化

《Android 性能监控框架》

大家可参考:https://0a.fit/dNHYY

《Android Framework学习手册》

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

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

相关文章

[读论文] Monocular 3D Object Reconstruction with GAN inversion (ECCV2022)

概述 项目主页&#xff1a;https://www.mmlab-ntu.com/project/meshinversion/ 方法名称&#xff1a;MeshInversion 输入&#xff1a;单目图像 &#xff08;in the wild&#xff0c;有背景的&#xff0c;没有抠图的&#xff09; 输出&#xff1a;textured 3D mesh key challen…

【metaRTC学习】metaRTC的demo运行说明(一)

metaRTC的github的地址为&#xff1a;GitHub - metartc/metaRTC: A cross-platform WebRTC SDK 其作者杨高峰的博客为&#xff1a;metaRTC的博客_CSDN博客-metaRTC,解决方案领域博主 其博客对其自己的demo的运行说明不够详细&#xff0c;刚入门的会一脸懵&#xff0c;本文对其…

Netty(二)- NIO三大组件之Buffer

文章目录一、Buffer 基本介绍二、Buffer 类及其子类三、Buffer 的使用四、关于Buffer 的注意事项和细节1. put和get的数据类型应该相同2. 可以将一个普通 Buffer 转成只读 Buffer3. 可以使用MappedByteBuffer让文件直接在内存中修改4. 可以通过 Buffer 数组完成读写操作&#x…

富文本编辑器 ck-editor5 的使用

最近在项目中需要用到富文本编辑器&#xff0c;据说ck-editor5很不错&#xff0c;于是就使用它了&#xff0c;不过在期间也遇到了很多问题&#xff0c;这里记录下。 一、引入ck-editor5 文档地址&#xff1a;Predefined builds - CKEditor 5 Documentation 这里有个坑&#…

YOLOv2-yolo9000-batter,faster,stronger 论文精度

参考大佬&#xff1a;同济子豪兄 YOLOv2-yolo9000 yolo9000-batter,faster,stronger Introduction 我们提出了一种新的方法来利用大量的图像分类数据&#xff0c;来扩大当前检测系统的范围。我们的方法使用目标分类的分层视图&#xff0c;允许我们将不同的数据集组合在一起…

个人需求1:代码提交这块的做法

老早之前的代码提交的步骤&#xff0c;真的是很繁琐在这里提交代码&#xff0c;现在和大家分析一下我当初的心得。 1.首先黄老师建立了一个jira号,记得把功能背景/方案说明/测试范围提前了解,问起来的时候也能回答,如下图1: 15324就是我本次的jira号,每次开发前先建立jira号,这…

小区访客导航GIS方案

1 应用功能设计 1.1 小区地图 1.1.1 小区地图浏览 基于GIS平台&#xff0c;对小区地图进行渲染发布&#xff0c;提供可视化显示浏览地图可以选择2D地图模式&#xff0c;用户可以在地图上快速查询和点击选择某个小区内的建筑物、POI等信息&#xff0c;并查看其相关属性信息&…

【现代机器人学】学习笔记二:刚体运动

这一节内容最开始在学之前是有些不屑的&#xff0c;这些坐标变换的内容天天都在玩&#xff0c;有什么复杂的&#xff1f;高翔博士的14讲貌似讲这些内容只用了几页。 不过认真一读才发现自己自大肤浅了。 之前我在北大研究院的时候&#xff0c;有一个实验室双聘的浙农林的老师&…

每天一个面试题:ThreadLocal底层原理和实现Demo

ThreadLocal底层原理和实现Demo每天一个面试题&#xff1a;ThreadLocal实现ThreadLocal的DemoThreadLocal底层原理为什么ThreadLocalMap的key设计为弱引用今天开始一个新专栏&#xff1a;每天一个面试题系列 也没有拿到令人心动的offer&#xff0c;看来自己学习方式和能力还是差…

fastapi_No.25_获取配置项

文章目录方式1&#xff1a;隐藏在环境变量中配置环境变量Windows中配置环境变量Linux中配置环境变量在代码中获取环境变量方式2&#xff1a;隐藏在配置文件中装包在代码中获取配置文件内容在之前的代码中&#xff0c;像数据库连接信息等敏感信息&#xff0c;都直接写在代码中&a…

每日一题 —— 882. 细分图中的可到达节点

882. 细分图中的可到达节点 给你一个无向图&#xff08;原始图&#xff09;&#xff0c;图中有 n 个节点&#xff0c;编号从 0 到 n - 1 。你决定将图中的每条边 细分 为一条节点链&#xff0c;每条边之间的新节点数各不相同。 图用由边组成的二维数组 edgesedgesedges 表示&…

182:vue+openlayers 使用d3实现地图区块呈现不同颜色的效果

第182个 点击查看专栏目录 本示例的目的是介绍演示如何在vue+openlayers中加载解析geojson文件,同时利用d3的颜色功能,使得美国每个州呈现出不同的颜色区块,方便识别。 直接复制下面的 vue+openlayers源代码,操作2分钟即可运行实现效果; 注意如果OpenStreetMap无法加载,…

UNIX环境高级编程_文件IO_文件描述符

这篇文章记录文件描述符&#xff0c;下一篇文章记录文件描述表。 1 文件描述符 先说说什么是文件IO。文件的IO就是文件的输入输出&#xff0c;也就是文件的读写。读和写是以CPU为参考的&#xff0c;从CPU向文件中写入数据&#xff0c;就是写操作&#xff1b;从文件中读取数据…

Aviation turbofan starting model

Aviation turbofan starting model 涡扇发动机(Turbofan)即涡轮风扇发动机,来源于涡轮喷气发动机,主要是为了解决涡轮喷气发动机耗油率过高的问题。其结构特点是流过风扇的空气一部分进入压气机(内涵道),一部分进入由压气机外部通道(外涵道)流过,这部分气流不经过燃烧…

01 - Linux系统概要(再论计算机系统)

---- 整理自狄泰软件唐佐林老师课程 1. 再论计算机系统 计算机系统由躯体和灵魂两部分组成 – 躯体&#xff1a;构成计算机系统的电子设备&#xff08;硬件&#xff09; – 灵魂&#xff1a;指挥躯体完成动作的指令序列&#xff08;软件&#xff09; 躯体核心&#xff1a;中央…

2022-12-05 优化el-tree懒加载选人树

今后就都拼抵抗力了嗷 需求描述 此处有一棵懒加载树&#xff08;可选人&#xff09;&#xff0c;右侧展示已选中的人。且父子关联&#xff0c;可以通过选中一个部门勾选所有子节点。问题是&#xff0c;选中父节点&#xff0c;当子节点未加载时&#xff0c;是获取不到勾选的子…

Redis配置、持久化以及相命令

Redis 什么是Redis Redis&#xff08;远程字典服务器&#xff09;是一个开源的、使用C语言编写的NoSQL数据库 Redis 基于内存运行并支持持久化&#xff0c;采用key-value&#xff08;键值对&#xff09;的存储形式&#xff0c;是目前分布式架构中不可或缺的一环。 Redis服务…

新来的性能测试工程师工资25K,看了他做的性能测试,那才真叫牛

一直深耕于互联网行业的测试工作&#xff0c;前期测试主要以项目为主&#xff0c;也就是 一个人负责2-3个项目 的测试工作&#xff0c;当然包括项目上功能、自动化和性能等一切测试工作。 我有几个朋友也在互联网大厂工作&#xff0c;从他们当中了解到其实真正的互联网大厂&…

【前端CSS】网站都变成灰色了,它是怎么实现的?(含源代码解析)

目录&#xff1a;网站都变成灰色了&#xff0c;它是怎么实现的&#xff1f;一、前言二、如何实现的三、代码的理解3.1 CSS3 filter(滤镜) 属性3.2 定义和使用3.2.1 CSS动画演示3.2.2 JS语法演示3.3 浏览器支持3.4 CSS 语法3.5 Filter 函数四、实例展示4.1 模糊实例4.2 Brightne…

3、JSP——Servlet、IDEA创建Web项目、IDEA创建JSP页面

目录 一、Servlet的概念 二、Servlet的作用 三、IDEA中创建Web项目 四、手动部署 五、自动部署 1、IDEA部署Tomcat服务器 2、IDEA部署JavaWeb项目 3、JSP页面 一、Servlet的概念 &#xff08;1&#xff09;Servlet&#xff1a;Server Applet的简称&#xff0c;是运…