Android应用启动优化笔记整理
应用启动相关流程与优化
应用启动主要涉及SystemServer进程 和 app进程。
SystemServer进程负责app进程创建和管理、窗口的创建和管理(StartingWindow 和 AppWindow)、应用的启动流程调度等。
App进程被创建后,进行一系列进程初始化、组件初始化(Activity、Service、ContentProvider、Broadcast)、主页面的构建、View加载。
1.应用启动方式
启动方式:热启动、冷启动、温启动三种。
冷启动
冷启动耗时最多,冷启动常见场景APP首次启动或者APP完全被杀死。
温启动
当启动应用时,后台已有该应用的进程,Activity因为内存不足被回收。系统会从已有的进程中来启动这个Activity。
温启动场景:第一种是首先用户按连续按返回退出了app,最后重新启动app,第二种是首先系统收回了app的内存,最后重新启动app。
热启动
热启动时,系统将activity带回前台。如果应用程序的所有activity存在内存中,那么应用程序可以避免重复对象初始化、渲染、绘制操作。
热启动常见的场景如: 当我们按了Home键或其它情况app被切换到后台,再次启动app的过程。
2.应用冷启动关键流程
- 用户Click事件触发IPC操作,会执行Process的start方法来创建进程。
- ActivityThread执行main方法,是App进程的入口,相当于Java进程的main方法,在其中会执行消息循环的创建与主线程Handler的创建。
- Handler创建完成之后,就会执行到 bindApplication 方法,在这里使用了反射去创建 Application以及调用了 Application相关的生命周期。
- Application生命周期之后,便会执行Activity的生命周期,在Activity LifeCycle结束之后,就会执行到 ViewRootImpl,这时才会进行真正的一个页面的绘制。
优化方向
创建Application、启动主线程、创建HomeActivity、加载布局、布置屏幕和界面首帧绘制完成后,启动完成。
APP 层可以优化的相关流程:Application atttachBaseContext、Appication onCreate 和 Activity LifeCycle三个流程。
3.归因分析
程序运行最根本的是需要得到CPU时间片,如果一个任务需要较多的CPU时间执行,那么它将影响其他任务的执行,从而影响整体任务队列的运行;
线程切换涉及到 CPU调度,而CPU调度会有系统资源的开销,所以大量的线程频繁切换也会产生巨大的性能损耗;
IO和锁的等待会直接阻塞任务的执行,不能充分地利用CPU等系统资源。
4.启动指标
第一个是启动开始,启动开始是进程创建的时间
第二个是启动结束,首页首屏渲染完成的时间;
第三个是启动时长,启动时长是指启动结束的时间戳减去启动开始的时间戳;
5.启动优化价值
启动耗时增长可能缩减App用户的留存。
启动性能优化目标是以低端机为重点,辐射中高端机,通过技术和产品上的深度优化,可感知的提升用户体验,实现扩大用户规模、提升留存和提升收入。
6.优化方案整理
1.Application优化
Application 阶段通常用于初始化比较核心的业务库。在应用开发早期,我们并没有对这个阶段的启动任务进行管控,导致这里往往会堆积有大量的强业务相关的代码。精简基于的原则是:
- Application 中的任务应当是全局基础任务
- Application 创建时应当尽量减少网络请求操作
- Application 创建时不允许有强业务相关的任务
- Application 创建时尽量减少有 Json 解析处理和 IO 操作的工作
Application中的启动任务要尽可能的少,主要分为基础库初始化,功能配置和全局配置这三大类。基础类库主要是对网络库,日志库等基础库进行初始化配置,除了主进程外,其余的进程也依赖这些任务,移除它们会对全局的稳定性造成影响。它们也是启动任务中占比最大的,耗时最多的一类任务,因此降低它们的耗时也是后面持续优化的重点。功能配置主要是对一些全局相关的业务功能的前置配置,例如对业务缓存的预加载,特定业务前置等,移除它们会造成业务有损,在这种情况下,我们需要找到业务诉求和功能配置之间的平衡点。全局配置主要是对于全局 UI 配置,文件路径的处理操作,它们占比少,耗时少,是首页创建的前置任务,因此暂不处理。
任务排布的核心是处理好任务的前后依赖问题。
1.要基于进程进行任务排布,对 Application 的任务进行细致划分,将任务的运行精细到进程级,避免在非主进程中执行了不必要的任务。
2.懒加载。这里主要是对一些基础任务进行改造,将任务初始化和任务启动拆分开来,将启动工作移出 Application 创建流程,同时对其进行精简,去除冗余逻辑。在创建对象时,可以延迟其成员对象的创建,灵活使用 by lazy 等关键字,使对象轻量化。
3.进程收敛。多进程可以实现模块隔离,同时避免单个进程内存占比过高导致的内存上限限制,其劣势在于多进程会导致应用整体内存占用过多,触发低内存的概率更高。此外,如果应用启动时内存占比过高,可能会导致手机进行内存回收,占用大量的CPU资源,这反应在用户的体验上就是启动慢,应用卡。对比多进程的优劣,采用的策略是尽量延后主进程以外的进程启动,同时通过进程合并来减少进程的数量。
4.线程收敛。对于多核CPU来说,适当的线程数量能够提升效率,但是如果线程泛滥则会导致 CPU 负载过重。多线程并发,本质上就是多个线程轮流获取 CPU 使用权的过程。在负载超重的情况下,过多的线程争抢时间片,除了降低启动速度外,也会导致主线程卡顿,影响用户体验。在做这方面优化时,需要确保全局使用统一的线程池。同时很多二方和三方 SDK 也是创建子线程的大户,这时候需要和相关的技术部门进行沟通,去除不合理的线程创建。另一方面,避免在启动阶段进行网络请求,也是减少线程数的关键所在。
Application 优化是整个启动流程的关键,合理的任务编排不仅能够降低 Application 的创建时长,对后续的首页创建也有非常大的优化效果。
2.启动链路
执行完 Application 创建之后,应用进程的主要工作就是创建 Activity。这里需要注意的是,从 Application 到 Activity 里还藏有很多 post 到主线程中的任务,以及注册的 ActivityLifecycleCallbacks 的回调监听,它们会偷偷增加从 Application 到 Activity 的时间间隙。ActivityLifecycleCallbacks 注册通常和业务相关,它的注册比较隐蔽。
关于主线程消息耗时,使用 Profiler 和 Systrace 对启动流程进行耗时定位时,可以发现很多问题。因为各种原因,Application 中任务会将耗时的工作 post 到主线程中来,表面上看 Application 的创建时间缩短了,但是总体上启动时间却被扩大了。对于耗时点应该定位到根本原因进行解决,而不是一味地 post 出去,这样治标不治本。
缩短启动到首页的链路,是我们优化的重点。
在一般启动流程中,loading 页面启动页面,承担有路由和权限请求两个任务。
1.在通常情况下,用户启动 App,在 loading 页面判断是否登录,如果未登录,则进入登录页面。
2.如果用户已经登录,则判断是否需要展示开屏页,如果需要则进入开屏页,等到开屏结束,就跳回 loading 界面,再进入首页。
即使没有开屏页,用户启动 APP 到展示首页,最起码要经过两个 Activity 的启动。启动链路缩短的核心在于将 loading,main 和开屏页合并为一个页面。这样做不仅可以最起码减少一次 Activity 的启动,同时也可以在展示开屏页时并行处理其他的任务。
实现这个功能的代码逻辑就是将 main 页面设置为启动页,首页和开屏页封装为两个 fragment,根据业务逻辑进行展示。用户点击 icon 进入到首页,如果判断已登录,则执行首页前置任务和首页 UI 渲染,同时判断是否加载开屏页 fragment。
3.首页优化
懒加载
首页大致情况如下图:
APP 会加载首页的四个 TabFragment,比较耗时。
延迟动态等另外3个Fragment 的创建和加载。考虑到首页展示时只有第一个 fragment 是可见的。对首页进行懒加载。首页使用的是通用的 ViewPager2+tabLayout 的架构形式。ViewPager2 天然支持懒加载的操作,为了避免在页面切换时,已有的fragment 被回收,增大 viewPager2 内部的 recyclerView 的缓存池大小。
((RecyclerView)mViewPager.getChildAt(0)).setItemViewCacheSize(mFragments.size());
该方案是将其他页面的创建和渲染推迟到了切换时,如果页面比较重并且手机性能比较差的话,在切换时会有明显的卡顿和白屏情况,这也是无法接受的。
所以要对首页和各个fragment进行了改造。
4.ViewStub实现懒加载。
View 的创建是首页渲染耗时的大户。使用 LayoutInflater 去加载 xml 文件,这里面涉及到了 xml 解析,然后进行反射生成实例的过程,总体上是比较耗时的。对其中比较简单的 xml,使用了代码进行构建,但是对于复杂的布局文件,使用代码构建耗时巨大,并且不可维护。建议通过 ViewStub 的形式延后加载。
5.Json解析处理
Json 解析操作也是需要进行优化的点。Json 解析耗时的原因本质上是在解析时,从 Json 数据到对象的创建是通过反射操作进行对象生成和赋值的,对象越复杂,那么耗时就越长。对于首页的主接口来说,返回对象的解析在低端机上的耗时可能超过 UI 的渲染的耗时。可以采取的方案是将首页的数据对象使用 Kotlin 进行重构,并对相关对象使用 @JsonClass(generateAdapter = true) 进行标注,它会在编译期间对标注的对象生成对应的解析适配器,从而缩短解析时间。
6.XML 解析优化
1.XML异步解析。
2.compose 的方案。
相关文章:

Android应用启动优化笔记整理
应用启动相关流程与优化 应用启动主要涉及SystemServer进程 和 app进程。 SystemServer进程负责app进程创建和管理、窗口的创建和管理(StartingWindow 和 AppWindow)、应用的启动流程调度等。 App进程被创建后,进行一系列进程初始化、组件初…...
图像bytes字节串二进制转十六进制及bytes转为图像
目录前言正文二进制与十六进制的bytes互转读取bytes为图像法1:直接写入f.read的结果法2: 转换为PIL或Numpy前言 参考: 8. python基础之基础数据类型–bytes - CSDN python 16进制与图片互转 - CSDN 正文 二进制与十六进制的bytes互转 bytes保存的是原始的字节(二…...

信息安全与数学基础-笔记-②同余
知识目录同余完全剩余系剩余类完全剩余系❀简化剩余系❀欧拉函数逆元!欧拉定理 !同余 a,b 两个数字,都模m,当两个数字模m后余的数一样即为同余。 例子: a bq r (mod m),这里的a 和 r 就是同余 ÿ…...
网络安全法
目录正文第一章第二章第三章第四章第五章第六章 法律责任第七章 附则正文 学习网络安全应该知道网络安全法 第一章 总则 第一条: 为了保障网络安全,维护网络空间主权和国家安全、社会公共利益,保护公民、法人和其他组织的合法权益,促进经济…...

django框架开发部署项目
前言:相信看到这篇文章的小伙伴都或多或少有一些编程基础,懂得一些linux的基本命令了吧,本篇文章将带领大家服务器如何部署一个使用django框架开发的一个网站进行云服务器端的部署。 文章使用到的的工具 Python:一种编程语言&…...
Unity记录1.3-入门-第一阶段总结
文章首发及后续更新:https://mwhls.top/4447.html,无图/无目录/格式错误/更多相关请至首发页查看。 新的更新内容请到mwhls.top查看。 欢迎提出任何疑问及批评,非常感谢! 汇总:Unity 记录 摘要:第一阶段的总…...
Linux入门篇-文件管理
简介 简单的文件管理。 ⽂件内容的查看 ⽂本⽂件内容的查看 cat ⽂本⽂件的path1 ⽂本⽂件的path2 head ⽂本⽂件的path ,显示⽂件的前10⾏内容 head -n 5 ⽂本⽂件的path , 显示⽂件的前5⾏内容 head -5 等于head -n 5tail ⽂本⽂件的path, 显示⽂件的后10⾏内容…...

如何从错误中成长?
在上一篇文章“技术人的犯错成本”里,我和你聊了技术人可能会犯的各式各样的错误,也举了很多例子,说明了技术人犯错的成本。在竞争激烈的互联网时代,试错当然是好事,但了解错误成本,避免不应该犯的错误&…...
谈谈一个程序员的职场心得(真有用)
谈谈一个程序员的职场心得 我会分为三个部分:软件开发,职场协作和认知成长,每个部分精简成 7 条心得。 软件开发 若无必要,勿增实体。 这是奥卡姆剃刀的定义,所谓剃刀就是法则,是奥卡姆这个英国学者提出来…...
Pytest:一个卓有成效的测试工具
大家都知道,目前最流行的Python单元测试框架有三种,分别是unittest, nose和pytest。其中unittest是Python自带的测试框架,但问题是比较老了,赶不上时代发展了(哈哈哈);nose2定位是带插件的unitt…...

Compose 动画 (三) : AnimatedVisibility 从入门到深入
1. AnimatedVisibility 是什么 AnimatedVisibility可以实现Compose组件的显示和隐藏,并且可以指定显示/隐藏时候的动画效果。(EnterTransition/ExitTransition) 和 animateXxxAsState、animateContentSize、Crossfade、AnimatedContent 这几个API一起,都…...

网络基础(二)
目录 应用层 再谈 "协议" 协议是一种 "约定". socket api的接口, 在读写数据时, 都是按 "字符串" 的方式来发送接收的. 如果我们要传输一些"结构化的数据" 怎么办呢? 为什么要转换呢? 如果我们将struct message里面…...

Java线程知识点总结
文章目录Java 线程基础线程简介什么是进程什么是线程进程和线程的区别创建线程ThreadRunnableCallable、Future、FutureTaskCallableFutureFutureTaskCallable Future FutureTask 示例线程基本用法线程休眠线程礼让终止线程守护线程线程通信wait/notify/notifyAlljoin管道线程…...
数据结构——第三章 栈与队列(4)
队列的应用1.基于队列的医院挂号模拟系统2.队列的运用1.基于队列的医院挂号模拟系统 代码实现分享 2.队列的运用 问题描述:某运动会设立N个比赛项目,每个运动成员可以参加1~3个项目。试问如何安排比赛日程,既可以使同一运动员参加的项目不…...
华为机试HJ73-计算日期到天数转换
HJ73 计算日期到天数转换 题目描述: 描述 根据输入的日期,计算是这一年的第几天。 保证年份为4位数且日期合法。 进阶:时间复杂度:O(n) ,空间复杂度:O(1) 输入描述: 输入一行,每行…...
【阅读笔记】你不知道的JavaScript--this与对象2
目录this默认绑定隐式绑定隐式丢失显示绑定API 调用上下文new 绑定this 绑定优先级其余绑定例外对象字面量与对象属性描述符迭代器遍历this 默认绑定 默认绑定适配 独立函数调用 默认绑定 this 指向全局对象; 故直接调用函数,该函数内部的 this 即指向全…...

单板TVS接地不当造成辐射骚扰超标问题分析-EMC
【摘要】 某产品EMC辐射骚扰测试超标,通过近远场扫描配合定位分析,逐步找出骚扰源、传播路径,最终通过修改 PCB 走线切断传播路径解决此问题。 1 故障现象 某产品在进行 EMC 研发摸底测试时发现,整机辐射骚扰垂直方向测试超标&a…...

用Python Flask为女朋友做一个简单的网站(附可运行的源码)
🌟所属专栏:献给榕榕🐔作者简介:rchjr——五带信管菜只因一枚😮前言:该专栏系为女友准备的,里面会不定时发一些讨好她的技术作品,感兴趣的小伙伴可以关注一下~👉文章简介…...

vue3+rust个人博客建站日记5-所有界面
没有数据的前端,是没有灵魂的。明明标题是vue3 rust ,但日记撰写至今,似乎只有第一篇提及了Rust,这可不行。是时候一股作气,完成大部分页面绘制工作了! 最后再说一次,时间要加速了。 ——普奇神…...
青少年软件编程C++一级真题(202212)
1、输入一个整数x,输出这个整数加1后的值,即x1的值。 时间限制:1000 内存限制:65536 输入 一个整数x(0 ≤ x ≤ 1000)。 输出 按题目要求输出一个整数。 样例输入 9样例输出 10 #include<iost…...

iOS 26 携众系统重磅更新,但“苹果智能”仍与国行无缘
美国西海岸的夏天,再次被苹果点燃。一年一度的全球开发者大会 WWDC25 如期而至,这不仅是开发者的盛宴,更是全球数亿苹果用户翘首以盼的科技春晚。今年,苹果依旧为我们带来了全家桶式的系统更新,包括 iOS 26、iPadOS 26…...
Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以?
Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以? 在 Golang 的面试中,map 类型的使用是一个常见的考点,其中对 key 类型的合法性 是一道常被提及的基础却很容易被忽视的问题。本文将带你深入理解 Golang 中…...
逻辑回归:给不确定性划界的分类大师
想象你是一名医生。面对患者的检查报告(肿瘤大小、血液指标),你需要做出一个**决定性判断**:恶性还是良性?这种“非黑即白”的抉择,正是**逻辑回归(Logistic Regression)** 的战场&a…...

关于iview组件中使用 table , 绑定序号分页后序号从1开始的解决方案
问题描述:iview使用table 中type: "index",分页之后 ,索引还是从1开始,试过绑定后台返回数据的id, 这种方法可行,就是后台返回数据的每个页面id都不完全是按照从1开始的升序,因此百度了下,找到了…...
Golang dig框架与GraphQL的完美结合
将 Go 的 Dig 依赖注入框架与 GraphQL 结合使用,可以显著提升应用程序的可维护性、可测试性以及灵活性。 Dig 是一个强大的依赖注入容器,能够帮助开发者更好地管理复杂的依赖关系,而 GraphQL 则是一种用于 API 的查询语言,能够提…...

全球首个30米分辨率湿地数据集(2000—2022)
数据简介 今天我们分享的数据是全球30米分辨率湿地数据集,包含8种湿地亚类,该数据以0.5X0.5的瓦片存储,我们整理了所有属于中国的瓦片名称与其对应省份,方便大家研究使用。 该数据集作为全球首个30米分辨率、覆盖2000–2022年时间…...
基于数字孪生的水厂可视化平台建设:架构与实践
分享大纲: 1、数字孪生水厂可视化平台建设背景 2、数字孪生水厂可视化平台建设架构 3、数字孪生水厂可视化平台建设成效 近几年,数字孪生水厂的建设开展的如火如荼。作为提升水厂管理效率、优化资源的调度手段,基于数字孪生的水厂可视化平台的…...
linux 错误码总结
1,错误码的概念与作用 在Linux系统中,错误码是系统调用或库函数在执行失败时返回的特定数值,用于指示具体的错误类型。这些错误码通过全局变量errno来存储和传递,errno由操作系统维护,保存最近一次发生的错误信息。值得注意的是,errno的值在每次系统调用或函数调用失败时…...

DIY|Mac 搭建 ESP-IDF 开发环境及编译小智 AI
前一阵子在百度 AI 开发者大会上,看到基于小智 AI DIY 玩具的演示,感觉有点意思,想着自己也来试试。 如果只是想烧录现成的固件,乐鑫官方除了提供了 Windows 版本的 Flash 下载工具 之外,还提供了基于网页版的 ESP LA…...

2025盘古石杯决赛【手机取证】
前言 第三届盘古石杯国际电子数据取证大赛决赛 最后一题没有解出来,实在找不到,希望有大佬教一下我。 还有就会议时间,我感觉不是图片时间,因为在电脑看到是其他时间用老会议系统开的会。 手机取证 1、分析鸿蒙手机检材&#x…...