Android性能优化(总结优化点)

概述

总结性的内容,总结了一些优化可以注意的点。

APP启动优化

APP内存优化

  • 内存检测 (查找内存泄漏和对象数量分配)
  • Android Profile (直观,新版本可以直接查看最近一次GC Route路线,对查找内存泄漏问题很好用)
  • Memory Analyzer(MAT):查找引用和内存泄漏比较方便
  • LeakCanary:直观分析GC路径,可以带到线上(但是得优化,因为dump的文件比较大)
  • android Studio最新版本的内存检测可以实时查看内存对象变化情况,这个非常好!!!类似于以前Java监控对象变化的功能
  • 图片优化
  • 图片大小计算方式:图片: 宽 x 高 x 一个像素占用内存 x(如果放在drawable下的压缩比率)
  • 获取图片大小使用 options.inJustDecodeBounds = true 可以不加在图片到内存
  • 图片压缩:使用 BitmapFactory.Options() 的 inSampleSize 属性,建议采用和加载图片的 view 大小动态计算 inSampleSize 大小,防止图片失真。
  • 采用不用的图片格式 比如:PNG是无损格式占用内存较大,JPEG不支持透明通道为有损格式,WEBP 支持有损和 无损压缩 支持透明通道 支持多帧动画 — 首选(替代PNG)。还有 GIF格式等。可以根据不用需要选择
  • 对图片进行 Options 的 inPreferredConfig 压缩 ,比如 ARGB_8888 改为 ARGB_565,取消透过名通道,并且 ARGB_8888 每个像素占用4字节 ARGB_565占用2字节。其他还有:ARGB_4444 ,ALPHA_8 等。根据需要进行选择。
    超大图片加载:在压缩之外,如果需求允许,可以加载部分图片,根据手势或者其他控制展示其他部分。可以通过 BitmapRegionDecoder 加载指定区域图片。
  • 加载 Bitmap 时,可以通过 InBitmap 优化,复用上一次创建Bitmap的内存空间,可以防止重新分配空间。限制:需要新bitmap需要的空间 < 复用空间。
  • 优化图片加载:
    1:可以通过自定义ImageVIew onDraw的时候动态加载 (侵入性强 但是兼容性好)
    2:可以在 onDrawListener 回调里面处理 (侵入性强 但是兼容性好
    3:ARTHook方式优化 (因为兼容性不好 所以 可以通过某些版本开启hook 某些版本不开启hook) (EPIC框架 最小力度为Java方法 支持Android4.0-9.0)
    (感觉如果配合Kotlin 可以做成扩展函数 让开发人员选择调用)
  • Bitmap.recycle 新版API可以不用主动调用。因为图片位信息等存储位置改变了。
  • Low Memory Kller

内存不足时回掉 onTrimMemory ;基本上此方法回掉以后就快oom了。可以自己处理是主动关闭重新打开,还是释放资源。

  • 使用Android优化过的一些容器比如:android.util.Sparserray 等
  • 优点:
    避免了基本数据类型的装箱操作
    不需要额外的结构体,单个元素的存储成本更低
    数据量小的情况下,随机访问的效率更高
  • 缺点:
    插入操作需要复制数组,增删效率降低
    数据量巨大时,复制数组成本巨大,gc()成本也巨大
    数据量巨大时,查询效率也会明显下降

每个容器都有各自的特点,开发时需要根据情况选择是增删快还是查找快等特性的容器。

  • SharedPreference 优化(用不好严重影响性能甚至 ANR)
  • 避免大量频繁读取SharedPreference,占用内存,IO操作耗时。
  • 使用SharedPreference时,可以合理利用多次添加一次写入等。合理利用 apply与commit
  • 可以使用 MMKV替代(mmap原理,少一次copy操作,效率大大提高)
  • 初始化可以利用启动时空闲CPU时间片进行初始化操作。

APP布局优化

  • 通过分析布局源码分析一些可以考虑优化性能的地方。
  • 文件解析,IO加载XML,
  • 反射创建view,
  • 层级越深,循环测量次数增长越大。
  • 可以通过 LayoutInflate.Factory 和 Factory2 做一些事情

Factory实际上是创建 View 的一个Hook。可以做一些自己的操作,比如更换全部TextView。

  • Activity加载xml优化
  • AsyncLayoutInflate 异步加载view ,如果 xml特别复杂比较耗时,可酌情采用

有一些使用限制,并且也会占用主线程looper

  • 由于加载xml是IO操作,为了避免IO操作,可以使用java代码创建布局,但是Java代码难维护。可以采用 X2C 框架编译期编译 xml 文件

  • X2C 框架 (XML文件是 IO操作 还会通过反射创建View 效率低)原理:编译时注解:编辑期换成java代码创建布局 避免了反射和IO操作。 缺点:部分属性不支持 失去了系统兼容AppCompat。

  • GPU过度绘制 (根据开发者模式中的绘制检测,通过颜色判断是否过度绘制。白色最好 蓝色可以接受,红色应该修改…等)

  • 自定义view 只绘制指定区域

  • viewStub 延迟初始化(高效占位)

  • 优化层级;避免过度绘制

  • 检测工具

  • Systrace (检测具体耗时)
    关注 Frames;正常:绿色圆点;丢帧,黄色或红色
    Alert 栏:自动分析标注出的一些问题条目。
  • Layout Inspector (AndroidStudio自带工具,检测布局层级)
    通过分析层级,优化布局结构,减少布局深度。
  • Choreographer

获取 FPS,可以在线上使用,具备实时性。(> API 16)

  • 获取加载布局耗时
  • 普通记录…打点统计
  • 通过 aop方式记录,低侵入(AspectJ等aop框架;代理等)
  • 可以通过 LayoutInflate.Factory 获取每个view加载耗时。
  • 监控:通过aop方式,监控执行时间
  • 监控指标:FPS,加载时间,布局层级…

APP卡顿优化

  • 产生的原因复杂:代码问题、IO、内存、绘制
  • 不容易复现;可能和线上场景有关
  • 工具
  • CPU Profiler :图形的形式展示执行时间和调用栈等。信息全面,包含了所有线层。不过运行时容易卡死,开销严重。
  • Systrace:(AndroidSdk tools下的monitor )监控和跟踪Api调用,线程运行情况,生成HTML报告;API18以上,建议使用TraceCompat:特点:轻量级,开销小。直观反应CPU利用率,给我建议。
  • StrickMode :严苛模式,Android提供的一种运行时检测机制,方便强大;包含:线程策略和虚拟机策略检测。(线程策略:自定义的耗时调用;磁盘读取操作,网络请求等。。。)(虚拟机策略:Activity泄漏,Sqlite对象泄漏,检测实例数量。)
  • 自动化检测方案

方案原理

  • 消息处理机制,一个线程只有一个Looper
  • mLogging 对象在每一个 message 处理前后被调用。
  • 主线程发生卡顿,是在 dispatchMessage 执行耗时操作。
    可以通过 Looper.getMainLooper.setMessageLogging();设置自己的Log
    匹配>>> Dispatching,阀值时间后执行任务(获取堆栈等信息)
    匹配 <<< Finished ,任务启动之前取消掉。
  • AndroidPerformanceMonitor
  • 非侵入式性能监控组件,通知形式弹出卡片信息。
  • 原理就是上面方案原理
  • 存在问题:可能抓取的堆栈不一定是卡顿的堆栈;解决:需要高频抓取这段时间多个堆栈信息。上报到后台。
  • ANR 分析

发生条件:

  • KeyDispatchTimeout :5s
  • BroadcastTimeout:前台10s,后台60s。
  • Service:前台20s,后台200s。

ANR解决套路

  • 线下可以导出ANR文件进行分析(CPU,IO,锁等…)导出ANR文件跳转链接
  • 线上:通过FileOberver,监控文件变化(高版本权限问题)
  • ANR-WatchDog:非侵入式ANR监控组件(非侵入式,弥补高版本无权限问题。)
    对比 AndroidPerformanceMonitor 和 ANR-WatchDog;前者更适合监控卡顿,后者补充ANR监控。
  • 单点卡顿检测方案

例:IPC检测方案

  • 常规检测方案:IPC前后加埋点。
  • 不优雅容易忘记,维护成本大。
  • 技巧:线下可以通过 adb 命令检测 adb shell am trace-ipc start
  • 优雅的方案:ARTHook(可以检测系统方法) 和 AspectJ (不可监控系统方法,因为没法修改代码。)

监控维度:IPC,IO,DB,View绘制

注: AOP轻量级框架 Lancet

APP线程优化

  • 线程调度

需要了解的知识点

  • 线程优先级
  • 线程切换对CPU的损耗
  • Android线程调度中的:
    nice值(Process中设置)值越大优先级越低 ;
    cgroup:更严格的前后台优先级策略,保证前台线程获取更多CPU(如果只按照单独线程优先级,如果后台线程过多,也会影响到前台线程的调度)
  • 线程的继承性
  • 异步执行方式
  • 直接创建Thread
  • HandlerThread:自带消息循环,串行执行,适合长时间执行,不断的从队列中获取任务。
  • Intent Service:Service内部创建的 HandlerThread(因为是Service优先级比较高不容易被kill)
  • AsyncTask:内部是线程池原理;不用自己处理线程切换
  • 线程池

  • RxJava

  • 线程使用

  • 严禁使用 new Thread
  • 提供基础线程池供各个任务使用
  • 根据任务类型选择合适的线程池;
  • 可以使用的时候设置当前线程的名字,方便查找错误
  • 重视设置线程优先级,可以设置多次
  • 收敛项目线程数量
  • 创建线程时获取堆栈
  • 无论什么框架都会走到 new Thread
  • 通过 Hook (通过一些hook框架)方式查找 Hook点,记录创建线程信息,获取堆栈信息
  • 线程池库设计注意点
  • 区分任务类型(IO 密集型,CPU密集型)
  • IO密集型:不消耗CPU,线程吃可以很大
  • CPU密集型:核心线程大小和CPU核数相关

APP网络优化

  • 网络优化维度

流量消耗

  • 一定时间流量消耗精准度量,网络的类型(移动网络、WI-FI等),前台后台
  • 监控相关:用户流量消耗均值,异常率(消耗多,次数多)
  • 可以通过request 和 response 进行拦截,主动上报。

网络请求质量

  • 用户体验:请求速度,成功率。
  • 监控:请求时长,业务成功率,失败率,Top失败接口。

其他

  • 成本:贷款,服务器数,CDN
  • 耗电
  • 工具选择
  • Network Profiler:AndroidStudio自带的工具,显示了实时网络活动,发送,接受数据以及连接数。需要启用高级分析。只支持HttpUrlConnection 和 OkHttp两个网络库
  • 抓包工具
  • Charles (Mac用的比较多):断点功能,MapLocal,弱网环境模拟
  • Fiddler(window用的比较多)
  • Wireshark
  • TcpDump
  • Stetho:强大的应用调试桥,连接Android和Chrome ;网络监控,试图查看,数据库查看,命令行扩展等。(一般不会用它作为抓包工具等)
  • 精准获取流量消耗
  • 绝对值不能一定展现流量高低
  • 对比竞品,进行对比
  • 异常监控超过指标

线上流量获取方案

  • TrafficStats:API18 获取流量(仅获取重起以后的流量)可以设置获取指定UID的流量,可以获取总发送流量。(无法获取某一个时间段内的流量消耗)
  • NetworkStatsManager:API23以后的流量统计(可获取指定时间内的流量信息。可获取不同网络下的流量消耗)

前台后台流量获取

  • 后台开启定时任务-> 获取一定时间内的流量消耗-> 记录是前台还是后台消耗 -> 分别计算,上报。
  • 使用网络的场景

数据API;资源包(RN H5 热更新等);配置信息等
图片:上传下载
监控:需要网络上报信息。单点问题等

  • 数据缓存

服务器请求的数据做数据缓存一段时间,避免重复获取。能提高访问速度,节约流量。okhttp volley等都有缓存策略。

  • 增量数据更新

只传输有变化的数据;比如:省市区的更新

  • 数据压缩
  • Post 请求使用 GZIP压缩
  • 请求头压缩
  • 图片上传压缩(这个点对流量比较明显)
  • 数据可以根据需求粒度压缩(我在项目中就对经纬度进行过采样压缩由于数据过大,由于计算量过大请求速度变慢,流量消耗也大。可以采用采样的方式,比如隔几个点取一个经纬度降低密度。)
  • 合并网络请求,减少请求次数
  • 一些特定信息等WI-FI再上传(比如性能日志等Log)、
  • 质量指标

网络请求成功 && 网络请求速度

  • 网络请求过程:请求达到运营商通过DNS解析成对应的IP,创建连接根据IP找到服务器,发起请求,服务器找到资源返回。所以对 DNS解析进行优化可以提高网络访问体验。使用HttpDNS 绕过解析过程。可以降低平均访问时长,提高连接成功率。(项目中可以通过 Okhttp + HttpDNS进行优化)
  • 升级协议

比如Http协议 从开始到现在升级了 持久链接,网络复用等。

  • 网络请求监控
  • 请求接口耗时,成功率,失败率(业务失败和请求失败),错误吗
  • 图片加载每一步的耗时
  • 客户端监控
  • 接口每一步的信息(DNS、链接、请求等)
  • 请求次数、网络包大小、失败原因
  • 图片监控
  • 其他优化点
  • 可以采用复用连接池
  • 如果都是同一个域名的请求 采用 keep-alive 多路复用提高效率(okhttp默认开启)
  • 线下测试
  • 最好关闭其他应用的网络权限,只开启单独app,测试流量性能更准确
  • 侧重点:请求有误,多余请求,网络切换,弱网,无网测试

APP电量优化

电量优化往往被忽略,而且线上不好量化和监测电量问题。

  • 通过广播获取电池电量充电状态电池状态等(针对手机,不能针对特定单个 app)

ACTION_BATTERY_CHANGED 广播

  • 可以通过 BatteryManager 查看具体可以获取到哪些参数
  • Battery Historian : google 推出的Android系统电量分析工具 支持 API21以上版本
  • 测试功能强大,推荐使用
  • 可视化指标:耗电执行时间,次数等
  • 适合线下测试使用
  • Energy Profiler
  • 监控CPU、网络和GPS使用情况
  • 可以直接定位到代码位置
  • 可以终点检测一下 heavy 级别的耗电的位置
  • 测试场景
  • 因为测试很难拿到精准的耗电量,所以测试要针对一些特殊模块,比如视频播放,导航,大运算等。
  • 注意使用时长,耗电量,发热情况等
  • 后台静默测试:切换到后台查看耗电情况
  • 相关优化

cpu

  • CPU频率越高会提高电量的消耗;所以多关注CPU消耗的时间片;减少CPU高负荷运作时间等。可以通过一些检测CPU的工具进行查看;如CPU profile等
  • 减少切换到后台后,减少APP的操作。切换到前台以后再唤起等。包括动画UI等

网络

  • 控制网络请求的时机和次数(比如一起请求,可以减少网络发起次数)
  • 数据压缩,减少网络请求时间
  • 网络尽量不使用轮询的方式做业务,可能会导致网络一直处于被激活的状态

定位

  • 根据场景选择定位模式,高精度,低精度模式;网络定位替代GPS定位。使用后及时关闭GPS,减少更新频率。
    其他
  • JobScheduler 根据特定场景发起任务

APP apk瘦身优化

  • 影响APK下载转化率

  • 可以通过反编译,或 AndroidStudio 查看APK哪部分占比比较大。可以考虑是否有优化空间

  • 通过代码混淆:移除冗余代码,缩短元素名称。增加反编译难度。可以缩小包大小

  • 三方库统一,选择更小的库

  • 仅引入需要的代码,去掉不需要的文件。

  • 移除无用代码

  • 可以通过AS RemoveUnusedResource 移除(注意:如果是通过文件名称去匹配的资源文件不是通过R文件等方式。也会被移除)

  • 图片压缩

  • SO文件移除:一般根据CPU架构来使用so库,一般选择armeabi就可以了,但是毕竟最匹配的性能最好,比如armeabi-v7 家在armeabi 就不如直接加载V7效率。所以这样会降低一些性能。

  • so更优方案:将需要的so文件全部放在armeabi下,通过代码去判断是加载armeabi-v7的还是armeabi的so文件

APP稳定性优化

  • 大型的趋于稳定周期的app才会重视稳定性。稳定性是用户留存很重要的指标。
  • Crash是最高优先级
  • 稳定性维度
  • Crash纬度:降低Crash率
  • 性能纬度:提高性能
  • 业务高可用
  • 重在预防,监控必不可少
  • Crash相关指标
  • UV (方便用户影响范围)、PV(Crash影响严重程度)Crash 率
  • Java Native Crash率
  • 启动重点流程Crash率
  • 增量存量Crash率
  • 尽可能还原崩溃现场
  • 堆栈、设备、os版本、进程、线程、logcat
  • 前后台、使用时长、APP版本、小版本、升级渠道
  • CPU架构、内存信息、线程数、资源包信息、行为日志

在这里插入图片描述

  • 业务高可用方案 & 容灾方案
  • 有时候项目并没有Crash,但是一样会给公司带来很大损失,比如点击了支付但是没有成功等。有时候需要把主流程打上点,看用户进行了哪些操作,出现了哪些异常情况,如走到了catch中或者走到了else中终断了操作。
  • 如果发版本后不稳定,兜底方案可以用后台配置功能开关的方式,禁用功能。或者 A
    B版本切换新旧老版本。
  • 可以通过类似于组件化路由框架中,根据线上问题动态更改路由跳转地址
  • 热修复
  • 根据Crash次数,如果次数过多,清除APP全部信息,防止脏数据导致用户崩溃
  • 多次请求服务失败,可以主动拒绝请求服务,也给服务器降低压力

其他优化

  • StringBuilder 代替 String拼接

  • 存储优化

  • SharePreperences:加载慢,初始化需要加载整个文件;全量写入,单次改动都需要整体写入。卡顿,虽然apply切换到子线程,但是也会造成主线程等待结果。不适合存储大量数据,不支持跨进程通信。如果发生Crash不能保证数据完整。
  • 使用MMKV替代SharePreperences,通过MMap和文件锁;增量写入,使用Protocol Buffer,支持从SharePreperences迁移。
  • 项目日志存储优化:如果每次写磁盘会影响性能,如果写入缓存区最后直接写磁盘有可能会导致数据丢失。也可以用MMap方式,它是一种内存映射的方法,高性能不丢失。
  • 常用数据缓存,避免多次读取。
  • 采用适当的缓冲区Buffer大小 4-8k
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值