建立分位制,用标准去量化优化效果 - 启动优化为例
Android开发的四年多时间中,逐渐将自己的工作重心从业务移动到小型项目的架构设计,在此过程中代码的书写有了更高的标准和要求,性能优化从此伴随着工作脚步, 为什么要进行性能优化呢?
- 页面访问时长从1s增加到3s,用户跳出率增加32%
- 崩溃率直接影响用户留存
- 卡顿会让用户选择放弃
等等
在互联网蓝海逐渐缩小的时代,圈人依然是每个互联网公司的目标,当有足够大的使用人数时变现、营销等都将能实现,所以提升用户体验是不可或缺的部分
背景:
在团队开发中,如何定义优化的效果,我想是可以进行量化的,将自己的应用的各个部分进行解体,完全掌握,然后和业内特别是竞品进行比较,做到知己知彼,才能百战百胜。
以下以启动优化作为案例:
一、建立启动优化耗时标准
针对优化我们需要知道优化的当前目标和优化后的目标是多少?该目标就是 — 启动耗时
3.1 统计启动耗时
很多应用把启动结束时间的统计放到首页刚出现的时候,这对用户是不负责任的,看到一个首页,但是停住十几秒都不能滑动,这对用户来说完全没有意义 ,所以我们需要着眼于从点击图标到用户可操作的整个过程
3.2 建立分位置
- 比如应用启动耗时从500ms~1500ms对应分为值为100 ~ 0(分)
比如应用启动耗时80分位值为我们APP的标准启动耗时分位值,而80分位值的对应时间为600ms, 所有用户的启动分位值计算出满足80分位值的用户,占比就说明有多少的情况下用户启动APP都能在 600ms 内完成,而我们需要做的就是让更多的用户达到指定的分位值。
- 此时我们通过在线上打点统计用户实际的启动耗时、机型归属(确定低、中、高端机)
(以下为假设APP启动机型和分位值统计所得数据)
| 机型归属 | 分位值 | 人数 |
|---|---|---|
| 2008 | 90 | 1000 |
| 2009 | 70 | 1200 |
| 2010 | 60 | 200 |
| 2011 | 80 | 3000 |
| 2012 | 20 | 4000 |
| 2013 | 80 | 4500 |
| 2014 | 90 | 3000 |
| 2015 | 90 | 3000 |
| 2016 | 90 | 4000 |
| 2017 | 80 | 8000 |
| 2018 | 70 | 10000 |
| 2019 | 90 | 12000 |
| 2020 | 88 | 20000 |
| 2021 | 87 | 10000 |
| 2022 | 98 | 10000 |
(以下为数据对应图表)

(以下针对上述图表进行结论总结)

- 达到分位值的用户
总人数为93900人,达标用户总数为78500人,其中高端机用户为74500人,低端机用户为4000人
达标率 = 78500/93900 ≈ 84%
低端机占比 = 4000 / 78500 ≈ 5%
总人数:

达标用户

其中高端机

低端机占比

至此我们就知道了应用目前的情况
一、启动过程分析

- T1 预览窗口显示
系统在拉起进APP程之前,会先根据APP的 Theme 属性创建预览窗口。当然如果我们禁用预览窗口或者将预览窗口指定为透明,用户在这段时间依然看到的是桌面。(用户可能觉得是手机卡顿而非应用卡顿)
- T2 闪屏显示
在APP进程和闪屏窗口页面创建完毕,并且完成一系列 inflate view、onmeasure、onlayout 等准备工作后,用户终于可以看到欢迎页面。
- T3 主页显示
在完成主窗口创建和页面显示的准备工作后,用户可以看到APP的主界面。
- T4 界面可以操作
在启动完成后,APP会有比较多的工作需要继续执行(初始化的东西按需分配至不同阶段),(比如微信聊天和朋友圈界面的预加载、小程序框架和进程的准备等。在这些工作完成后,用户才可以真正开始愉快地聊天。)
二、启动问题分析
以上4个启动的关键阶段,可以推测一下启动过程中越到的问题和平常在使用中会出现的问题相结合,基本为以下三个:
- 点击图标之后很久不响应
如果我们禁用了预览窗口或者指定了透明的皮肤,那用户点击了图标之后,需要 T2 时间才能真正看到应用闪屏。对于用户体验来说,点击了图标,过了几秒还是停留在桌面,看起来就像没有点击成功,这在中低端机中更加明显。
- 首页显示太慢
现在应用启动流程越来越复杂,闪屏广告、
热修复框架、插件化框架、大前端框架,所有准备工作都需要集中在启动阶段完成。上面说的 T3 首页显示时间对于中低端机来说简直就是噩梦,经常会达到十几秒的时间。
- 首页显示后无法操作
既然首页显示那么慢,那我能不能把尽量多的工作都通过异步化延后执行呢?很多应用的确就是这么做的,但这会造成两种后果:要么首页会出现白屏,要么首页出来后用户根本无法操作。
四、优化工具
对于优化Android拥有非常多的优化工具,Traceview、Nanoscope (非常真实,不过暂时只支持 Nexus 6P 和 x86 模拟器,无法针对中低端机做测试)。
systrace 可以很方便地追踪关键系统调用的耗时情况,但是不支持应用程序代码的耗时分析。
综合来看,“systrace + 函数插桩”似乎是比较理想的方案,而且它还可以看到系统的一些关键事件,例如 GC、System Server、CPU 调度等。
- 查看支持的类型
python systrace.py --list-categories
通过插桩,我们可以看到应用主线程和其他线程的函数调用流程。它的实现原理非常简单,就是将下面的两个函数分别插入到每个方法的入口和出口。
class Trace {public static void i(String tag) {Trace.beginSection(name);}public static void o() {Trace.endSection();}
}
当然这里面有非常多的细节需要考虑,比如怎么样降低插桩对性能的影响、哪些函数需要被排除掉(可在debug环境下使用)
只有准确的数据评估才能指引优化的方向,这一步是非常非常重要的。
五、优化方式
在拿到整个启动流程的全景图之后,我们可以清楚地看到这段时间内系统、应用各个进程和线程的运行情况
可分为以下优化模块:
5.1 闪屏优化
- 预览窗口window属性设置,让体验出现跟手感觉
- 合并闪屏和主页面的Activity,减少一个Activity会给线上带来100ms的优化 (管理复杂)
5.2 业务梳理
我们首先需要梳理清楚
- 当前启动过程正在运行的每一个模块,哪些是一定需要的、哪些可以砍掉、哪些可以懒加载。
- 可以根据业务场景来决定不同的启动模式,例如通过扫一扫启动只需要加载需要的几个模块即可。对于中低端机器,我们要学会降级,学会推动产品经理做一些功能取舍。但是需要注意的是,懒加载要防止集中化,否则容易出现首页显示后用户无法操作的情形。
5.3 线程优化
- 线程优化就像做填空题和解锁题,我们希望能把所有的时间片都利用上,因此主线程和各个线程都是一直满载的。当然我们也希望每个线程都开足马力向前跑,而不是作为接力棒。所以线程的优化主要在于减少 CPU 调度带来的波动,让应用的启动时间更加稳定
从具体的做法来看,线程的优化一方面是控制线程数量,线程数量太多会相互竞争 CPU 资源,因此要有统一的线程池,并且根据机器性能来控制数量。
线程切换的数据我们可以通过卡顿优化中学到的 sched 文件查看,这里特别需要注意 nr_involuntary_switches 被动切换的次数。
可以使用命令
proc/[pid]/sched
- 其中:
-
- nr_voluntary_switches: 主动上下文切换次数,因为线程无法获取所需资源导致上下文切换,最普遍的是IO。
- nr_involuntary_switches: 被动上下文切换次数,线程被系统强制调度导致上下文切换,例如大量线程在抢占CPU。
- 另一方面是检查线程间的锁
各APP做启动优化时并行初始化是优化的手段之一,但是有的情况下并发初始化会因为书写而不起作用
线程内部会持有一个锁,主线程很快就执行完了,其他任务因为这个锁而等待。这样的话主线程就出现了空转的场景,可以通过systrace看锁等待事件,我们要以此排查是否需要做对应的优化

在做初始化优化时,一般会将业务进行优先级区分,业务优先级规定业务初始化的时机,比较火的启动框架大多采用:为各个人物建立依赖关系,最终构成一个有向无环图,对于可以并发的任务,会通过线程池最大程度提升启动速度。
当出现并-总模式时,配置不恰当就会出现以下问题:

即:主线程一直等待taskC 结束,空转2950ms
5.4 GC优化
在启动过程,要尽量减少 GC 的次数,避免造成主线程长时间的卡顿,特别是对 Dalvik 来说,我们可以通过 systrace 单独查看整个启动过程 GC 的时间。
python systrace.py dalvik -b 90960 -a com.sample.gc
也可以采用Debug.startAllocCounting 来监控启动过程中GC的耗时情况,特别是阻塞式同步 GC 的总次数和耗时
// GC使用的总耗时,单位是毫秒
Debug.getRuntimeStat("art.gc.gc-time");
// 阻塞式GC的总耗时
Debug.getRuntimeStat("art.gc.blocking-gc-time");
如果我们发现主线程出现比较多的 GC 同步等待,那就需要通过 Allocation 工具做进一步的分析启动过程避免进行大量的字符串操作,特别是序列化跟反序列化过程。一些频繁创建的对象,例如网络库和图片库中的 Byte 数组、Buffer 可以复用。如果一些模块实在需要频繁创建对象,可以考虑移到 Native 实现。
Java 对象的逃逸也很容易引起 GC 问题,我们在写代码的时候比较容易忽略这个点。我们应该保证对象生命周期尽量的短,在栈上就进行销毁。
Java 逃逸分析 https://segmentfault.com/a/1190000016803174
5.5 系统调用优化
- 通过 systrace 的 System Service 类型,我们可以看到启动过程 System Server 的 CPU 工作情况。在启动过程,我们尽量不要做系统调用,例如 PackageManagerService 操作、Binder 调用等待。
- 在启动过程也不要过早地拉起应用的其他进程,System Server 和新的进程都会竞争 CPU 资源。特别是系统内存不足的时候,当我们拉起一个新的进程,可能会成为“压死骆驼的最后一根稻草”。它可能会触发系统的 low memory killer 机制,导致系统杀死和拉起(保活)大量的进程,从而影响前台进程的 CPU。
在我们应用中IM进程和File下载进程的拉起时机需要做调整
5.6 高级性能优化
可以更多的参考腾讯的matrix, 从性能的根本问题上进行了解决 https://github.com/Tencent/matrix
设备分级:
上文提到的设备分级,在这里说明一下
核心思想:
设备分级是指Device-year-class ,它是Facebook 开源的一个Android库,实现原理是将一个手机的内存(RAM)、CPU内存和clock speed((主频)时钟速度)三个方面映射到指定的一个组合中,通过这个验证来判断你是那一年的,规格的高低决定你是”那一年“的而不是手机的生产年份。(当然我们可以根据自己的需求定义这些参数,比如音视频公司可以从CPU、GPU等音视频相关参数出发定位等)
实现流程图

作用
针对机型进行适配,让每一份努力都能展现到对应的用户面前
相关文章:
建立分位制,用标准去量化优化效果 - 启动优化为例
Android开发的四年多时间中,逐渐将自己的工作重心从业务移动到小型项目的架构设计,在此过程中代码的书写有了更高的标准和要求,性能优化从此伴随着工作脚步, 为什么要进行性能优化呢? 页面访问时长从1s增加到3s&#…...
Modbus 通信协议 二
Modbus 常用缩写 通用Modbus帧结构 -应用数据单元(ADU) Modbus数据模型 Modbus ADU 和 PDU 的长度 Modbus PDU结构 串行链路上的 Modbus 帧结构 Modbus 地址规则 ASCLL 模式 和 RTU 模式的比较 RTU 模式 RTU 模式位序列 帧格式 帧的标识与鉴别 CRC 循环冗…...
关于系统设计的一些思考
0.前言 当我们站在系统设计的起点,面对一个新的需求,我们该如何开始呢?这是许多处于系统分析与设计领域的新手常常思考的问题。有些人可能会误以为,只要掌握了诸如面向对象、统一建模语言、设计模式、微服务、Serverless、Servic…...
Java 第19章 IO流 课堂练习+本章作业
文章目录 Buffered流拷贝二进制文件创建文件写入文本读取文本文件存读Properties文件 Buffered流拷贝二进制文件 package com.hspedu.chapter19.outputStream;import java.io.*;public class BufferedCopy02 {public static void main(String[] args) {String srcFilePath &q…...
一键制作电子样册,提升企业品牌形象
电子样册作为一种新型的宣传方式,具有许多优势。首先,它打破了传统纸质宣传册的局限性,可以随时随地展示企业的产品和服务。其次,电子样册可以通过多媒体形式展示企业的品牌形象,包括图片、视频、文字等多种形式&…...
Linux 的引导与服务控制
一 开机启动过程 bios加电自检-->mbr-->grub-->加载内核文件-->启动进程 1 bios家电自检 检测硬件是否正常,然后根据bios中的启动项设置,去找内核文件 2 mbr 因为grup太大,第一个扇区存不下所有的grub程序,所以分为2部分指…...
多输入多输出 | MATLAB实现SSA-CNN麻雀算法优化卷积神经网络多输入多输出预测
多输入多输出 | MATLAB实现SSA-CNN麻雀算法优化卷积神经网络多输入多输出预测 目录 多输入多输出 | MATLAB实现SSA-CNN麻雀算法优化卷积神经网络多输入多输出预测预测效果基本介绍模型背景程序设计参考资料 预测效果 基本介绍 MATLAB实现SSA-CNN麻雀算法优化卷积神经网络多输入…...
高端电流检测方案
随着过去传统的“开环”系统被智能和高效率“闭环”设计所取代,准确的电流检测在多种应用中变得越来越重要。常见的电流检测方法,需要将检流电阻串联进被测电流通路,再用放大电路放大检流电阻上的压降。这个放大电路常被称之为电流检测放大器…...
IP地址、子网掩码与网络地址
一、IP地址 在 TCP/IP 体系中,IP 地址是一个最基本的概念。IP地址的作用:实现和网上的其他设备进行通信 IP地址的表示方法 互联网上的每台主机(或路由器)的每个接口都分配一个全世界唯一的IP地址。该IP地址由ICANN分配。 IP地址…...
python 深度学习 记录遇到的报错问题10
本篇继python 深度学习 解决遇到的报错问题9_module d2l.torch has no attribute train_ch3-CSDN博客 一、CUDA error: no kernel image is available for execution on the device CUDA kernel errors might be asynchronously reported at some other API call,so the stackt…...
linux下docker搭建Prometheus +SNMP Exporter +Grafana进行核心路由器交换机监控
一、安装 Docker 和 Docker Compose https://docs.docker.com/get-docker/ # 安装 Docker sudo apt-get update sudo apt-get install -y docker.io# 安装 Docker Compose sudo apt-get install -y docker-compose二、创建配置文件及测试平台是否正常 1、选个文件夹作为自建…...
Github 2023-12-31 开源项目日报 Top10
根据Github Trendings的统计,今日(2023-12-31统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量TypeScript项目3Swift项目1Java项目1HTML项目1Astro项目1Python项目1C项目1Dart项目1Jupyter Notebook项目1C项…...
管程-第三十三天
目录 为什么要引入管程 管程的定义和基本特征 用管程解决生产者消费者问题 结论 本节思维导图 为什么要引入管程 原因:在解决进程的同步与互斥问题时,信号量机制存在编写困难和易出错的问题 能不能设计一种机制,让程序员写程序时不再需…...
嵌入式中断理解
一、概念 中断: 在主程序运行过程中,出现了特定的中断触发条件(中断源),使得CPU暂停当前正在运行的程序,转而去处理中断程序,处理完成后又返回原来被暂停的位置继续运行。 中断优先级&#x…...
React16源码: Hooks源码实现
Hooks 1 )概述 Hooks 在 React16.7版本出现的新功能Hooks 改变了整体应用开发的模式,同时开发体验会和以前会变得不一样Hooks 让函数组件具有类组件的能力 在 function component 里面没有this无法保存 state通过 Hooks可以让 function component 代替…...
华为端口隔离高级用法经典案例
最终效果: pc4不能ping通pc5,pc5能ping通pc4 pc1不能和pc2、pc3通,但pc2和pc3能互通 vlan batch 2 interface Vlanif1 ip address 10.0.0.254 255.255.255.0 interface Vlanif2 ip address 192.168.2.1 255.255.255.0 interface MEth0/0/1 i…...
java项目启动jar包启动参数设置端口号
默认启动 java -jar myapp.jar 指定配置文件 java -jar myapp.jar --spring.profiles.activedev 指定端口号 java -jar myapp.jar --server.port8080 后台启动 nohup java -jar myapp.jar --server.port8080 >outlog.log 2>&1 &...
【数据结构和算法】寻找数组的中心下标
其他系列文章导航 Java基础合集数据结构与算法合集 设计模式合集 多线程合集 分布式合集 ES合集 文章目录 其他系列文章导航 文章目录 前言 一、题目描述 二、题解 2.1 前缀和的解题模板 2.1.1 最长递增子序列长度 2.1.2 寻找数组中第 k 大的元素 2.1.3 最长公共子序列…...
多粒度在研究中的应用
FontDiffuser: One-Shot Font Generation via Denoising Diffusion with Multi-Scale Content Aggregation and Style Contrastive Learning 存在的问题 现有的字体生成方法虽然取得了令人满意的性能,但在处理复杂字和风格变化较大的字符(尤其是中文字符)时&#x…...
Docker命令---查看容器日志
介绍 使用docker命令查看容器输出的日志 示例 docker logs 容器ID...
多模态2025:技术路线“神仙打架”,视频生成冲上云霄
文|魏琳华 编|王一粟 一场大会,聚集了中国多模态大模型的“半壁江山”。 智源大会2025为期两天的论坛中,汇集了学界、创业公司和大厂等三方的热门选手,关于多模态的集中讨论达到了前所未有的热度。其中,…...
Spark 之 入门讲解详细版(1)
1、简介 1.1 Spark简介 Spark是加州大学伯克利分校AMP实验室(Algorithms, Machines, and People Lab)开发通用内存并行计算框架。Spark在2013年6月进入Apache成为孵化项目,8个月后成为Apache顶级项目,速度之快足见过人之处&…...
MongoDB学习和应用(高效的非关系型数据库)
一丶 MongoDB简介 对于社交类软件的功能,我们需要对它的功能特点进行分析: 数据量会随着用户数增大而增大读多写少价值较低非好友看不到其动态信息地理位置的查询… 针对以上特点进行分析各大存储工具: mysql:关系型数据库&am…...
8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂
蛋白质结合剂(如抗体、抑制肽)在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上,高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术,但这类方法普遍面临资源消耗巨大、研发周期冗长…...
java 实现excel文件转pdf | 无水印 | 无限制
文章目录 目录 文章目录 前言 1.项目远程仓库配置 2.pom文件引入相关依赖 3.代码破解 二、Excel转PDF 1.代码实现 2.Aspose.License.xml 授权文件 总结 前言 java处理excel转pdf一直没找到什么好用的免费jar包工具,自己手写的难度,恐怕高级程序员花费一年的事件,也…...
【git】把本地更改提交远程新分支feature_g
创建并切换新分支 git checkout -b feature_g 添加并提交更改 git add . git commit -m “实现图片上传功能” 推送到远程 git push -u origin feature_g...
学习STC51单片机32(芯片为STC89C52RCRC)OLED显示屏2
每日一言 今天的每一份坚持,都是在为未来积攒底气。 案例:OLED显示一个A 这边观察到一个点,怎么雪花了就是都是乱七八糟的占满了屏幕。。 解释 : 如果代码里信号切换太快(比如 SDA 刚变,SCL 立刻变&#…...
【数据分析】R版IntelliGenes用于生物标志物发现的可解释机器学习
禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍流程步骤1. 输入数据2. 特征选择3. 模型训练4. I-Genes 评分计算5. 输出结果 IntelliGenesR 安装包1. 特征选择2. 模型训练和评估3. I-Genes 评分计…...
Redis的发布订阅模式与专业的 MQ(如 Kafka, RabbitMQ)相比,优缺点是什么?适用于哪些场景?
Redis 的发布订阅(Pub/Sub)模式与专业的 MQ(Message Queue)如 Kafka、RabbitMQ 进行比较,核心的权衡点在于:简单与速度 vs. 可靠与功能。 下面我们详细展开对比。 Redis Pub/Sub 的核心特点 它是一个发后…...
服务器--宝塔命令
一、宝塔面板安装命令 ⚠️ 必须使用 root 用户 或 sudo 权限执行! sudo su - 1. CentOS 系统: yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh && sh install.sh2. Ubuntu / Debian 系统…...
