android性能优化

APP启动优化

https://androidperformance.com/2019/11/18/Android-App-Lunch-Optimize/

1.冷启动和热启动优化

减少application和首页activity生命周期函数执行时间

2.线程优化

减少 CPU 调度带来的波动,让启动时间更稳定。如果启动过程中有太多的线程一起启动,会给 CPU 带来非常大的压力,尤其是比较低端的机器。过多的线程同时跑会让主线程的 Sleep 和 Runnable 状态变多, 增加了应用的启动速度,优化的过程中要注意:

控制线程数量 – 线程池

检查线程间的锁 ,防止依赖等待

3.系统调度优化

应用启动的时候,如果主线程的工作过多,也会造成主线程过于繁忙,下面几个系统调度相关的点需要注意:

启动过程中减少系统调用,避免与 AMS、WMS 竞争锁。启动过程中本身 AMS 和 WMS 的工作就很多,且 AMS 和 WMS 很多操作都是带锁的,如果此时 App 再有过多的 Binder 调用与 AMS、WMS 通信,SystemServer 就会出现大量的锁等待,阻塞关键操作

启动过程中不要启动子进程,如果好几个进程同时启动,系统负担则会加倍,SystemServer 也会更繁忙

启动过程中除了 Activity 之外的组件启动要谨慎,因为四大组件的启动都是在主线程的,如果组件启动慢,占用了 Message 通道,也会影响应用的启动速度

Application 和主 Activity 的 onCreate 中异步初始化某些代码

4.GC 优化

启动过程中减少 GC 的次数

避免进行大量的字符串操作,特别是序列化和反序列化

频繁创建的对象需要考虑复用

5.IO 优化

IO 分网络 IO 和磁盘 IO 

启动过程中负载比较高,有许多系统 IO 都在此时发生,这时候 IO 的性能下降会比较快,此时 App 中的 IO 操作会比平时更慢一些,尤其是在性能比较差的机器上。

6.主页面布局优化

通过减少冗余或者嵌套布局来降低视图层次结构

用 ViewStub 替代在启动过程中不需要显示的 UI 控件

使用自定义 View 替代复杂的 View 叠加

7.闲时调用

IdleHandler:当 Handler 空闲的时候才会被调用,如果返回 true, 则会一直执行,如果返回 false,执行完一次后就会被移除消息队列。比如,我们可以将从服务器获取推送 Token 的任务放在延迟 IdleHandler 中执行,或者把一些不重要的 View 的加载放到 IdleHandler 中执行

8.选择合适的启动框架

把任务按优先级分阶段执行

9.预加载和缓存数据

Activity 打开之前就预加载数据,在 Activity 的 UI 布局初始化完成后显示预加载的数据,大大缩短启动时间

10.进程保活

11.类重排

类重排的实现通过 ReDex 的 Interdex 调整类在 Dex 中的排列顺序。Interdex 优化不需要去分析类引用,它只需要调整 Dex 中类的顺序,把启动时需要加载的类按顺序放到主 dex 里,这个工作我们完全可以在编译过程中实现,而且这个优化可以提升启动速度

12.手机外部环境也会影响app启动速度

13.厂商白名单优化

启动加速:App 启动的时候,系统会对要启动的应用做绝对的资源倾斜,比如 CPU、IO、GPU 等,这一点大家抓个 Systrace 看一下即可,不管是频率还是调度算法,正在启动的 App 绝对是当时的系统 VIP 客户

主线程、渲染线程加速:部分厂家会对启动过程 App 的主线程和渲染线程做特殊对待,比如让他们直接跑到大核上,将其他不重要的线程移到小核

启动预测:部分场景会针对用户的使用习惯进行学习,比如在什么时间、什么场合、什么交通工具打开手机,系统会预测你要启动的 App,并在后台进行启动,这样你点击这个 App 的时候,就已经是热启动了

后台保活:系统也会对一些应用进行特殊处理,以提升用户体验:包括但不限于 进程\线程优先级调整、查杀白名单、用户常用应用记录等,进行适当的后台保活,下次启动的时候就是热启动了

后台重启:系统会对一些应用进行特殊处理,比如这个 App 比较重要但是不能杀掉,那么有的厂商会在这种应用退到后台之后,进行无感重启:比如说某个应用内存超标或者持续 Crash ,后台重启可以很好地解决这个问题,这样重启后的 App 是用户点击启动的时候就是热启动

内存优化:部分应用启动的时候,需要大量的内存,比如现在的相机启动,这时候如果没有足够的内存,那么系统必须要通过杀掉很多应用、释放 Cache 等操作来给这个 App 让路,这个过程会使得这些大内存的 App 在启动的时候频繁进行内存操作,导致启动速度变慢


卡顿问题

卡顿原理

通常在帧率60的手机上,连续丢帧用户就会感觉到卡顿。

App卡顿问题引起的原因

卡顿分app自身原因和系统外部原因

系统外部原因引起的卡顿

https://www.androidperformance.com/2019/09/05/Android-Jank-Due-To-System/

https://time.geekbang.org/column/article/71982

系统当前cpu使用过高,io操作频繁,内存占用过大,后台活跃进程过多等原因都会导致app卡顿。

app自身原因引起的卡顿

https://www.androidperformance.com/2019/09/05/Android-Jank-Due-To-App/

1.主线程不要做耗时操作,主线程执行 Input \ Animation \ Measure \ Layout \ Draw \ decodeBitmap 等操作超时都会导致卡顿

2.冷启动时候application的生命周期函数中不要做过多耗时操作,可以用延迟加载,异步加载

3.热启动时的首页activity的生命周期函数不要做过多耗时操作,可以用延迟加载,异步加载

4.使用viewpager+fragment时候同时加载多个复杂fragment也会引起卡顿,可以做懒加载

5.线程过多,频繁进行cpu时间片切换,导致主线程执行时间变短

6.主线程 IO 操作,主线程操作数据库,使用 SharedPerforence 的 Commit 而不是 Apply

7.手机使用过长,外部环境也会引起app卡顿

8.过多的创建对象和垃圾回收也会引起卡顿

9.WebView 性能不足,应用里面涉及到 WebView 的时候, 如果页面比较复杂, WebView 的性能就会比较差, 从而造成卡顿

10.帧率与刷新率不匹配,如果屏幕帧率和系统的 fps 不相符 , 那么有可能会导致画面不是那么顺畅. 比如使用 90 Hz 的屏幕搭配 60 fps 的动画

11.应用性能跟不上高帧率屏幕和系统,部分应用由于设计比较复杂, 每一帧绘制的耗时都比较长 , 这么做的话在 60 fps 的机器上可能没有问题 , 但是在 90 fps 的机器上就会很卡, 因为从 60 -> 90 , 每帧留给应用的绘制时间从 16.6 ms 变成了 11.1 ms , 如果没有在 11.1 ms 内完成, 就会出现掉帧的情况.


卡顿线上如何监控

卡顿的产生也是依赖很多因素,比如用户的系统版本、CPU 负载、网络环境、应用数据等。

高版本的系统没有权限读取系统的 ANR 日志。

很多监控方案都是基于消息队列实现,通过主线程handler定时发延迟消息。

线上埋点统计方法耗时。

按线上线下如下:

线下

1.StrictMode:查找代码不规范的地方,主线程中是否进行了磁盘IO,网络请求等。

2.TraceView:追踪方法调用栈来查看耗时方法。

线上

数据打点收集

1.帧率统计

2.dispatchMessage耗时监控。

3.BlockCanary:动态检测消息执行耗时。基于消息队列实现,通过主线程handler定时发延迟消息,监控dispatcher到finish之间的操作耗时。

4.单点问题监控:setContentView耗时统计,四大组件生命周期函数耗时统计等。通过Aop实现非侵入式打点。


卡顿问题分析

https://www.jianshu.com/p/03dd61816051

分能复现问题和不能复现问题:通过线上监控日志分析app内各函数的执行时间(主要是监控activity生命周期和主线程消息队列各消息执行耗时),读取cpu、内存、io,线程调度等各种数据进行分析。


ANR问题

http://www.jishudog.com/14983/html

在Android开发中,当程序发生异常时会抛出异常信息,先说下三种常见类型:

列表内容KeyDispatchTimeout(谷歌default 5s,MTK平台上是8s) –主要类型按键或触摸事件在特定时间内无响应

BroadcastTimeout(10s),BroadcastReceiver在特定时间内无法处理完成

ServiceTimeout(20s) –小概率类型,Service在特定的时间内无法处理完成

一些典型的ANR 问题场景

1)最常见错误,UI线程等待其它线程释放某个锁,导致UI线程无法处理用户输入;

2)游戏中每帧动画都进行了比较耗时的大量计算,导致CPU忙不过来;

3)Web应用中,网络状态不稳定,而界面在等待网络数据;

4)UI线程中进行了一些磁盘IO(包括数据库、SD卡等等)的操作,在个别设备上因为硬件损坏等原因阻塞住了;

5)手机被其他App占用着CPU,自己获取不到足够的CPU 时间片,纯属误伤。

排查分析的思路

1、通过ANR 日志定位问题,Logcat和traces文件(目录/data/anr/)的相关信息输出去定位问题。

2、通过分析trace文件得到ANR信息(真机导出,模拟机在DDMS下查看),如果ANR发生,对应的应用会收到SIGQUIT异常终止信号,dalvik虚拟机就会自动在/data/anr/目录下生成trace.txt文件,将异常信息写入到traces文件中,系统会记录异常的位置、CPU和内存当时的使用情况,通过查看日志基本就能判断问题所在。

3、在源代码中插入ANR检测工具(BlockCanary、StrictMode)内存泄漏。

4、使用第三方SDK输出Crach信息到后台服务器。如腾讯bugly 和umeng。


内存泄漏

常用内存泄漏工具

Android Studio 自带的 Profile 工具、MAT(Memory Analyzer Tool)、以及LeakCanary

leakcanary原理

监听 Activity 的生命周期

在 onDestroy 的时候,创建相应的 Refrence 和 RefrenceQueue,并启动后台进程去检测

一段时间之后,从 RefrenceQueue 读取,若读取不到相应 activity 的 Refrence,有可能发生泄露了,这个时候,再触发 gc,一段时间之后,再去读取,若在从 RefrenceQueue 还是读取不到相应 activity 的 refrence,可以断定是发生内存泄露了

发生内存泄露之后,dump,分析 hprof 文件,找到泄露路径(使用 haha 库分析)


进程保活

注:8.0以前有很多方法可以做到进程保活,8.0以上不太好做,加入了服务分组,电量优化等一系列内容。不要花太多时间去挑战rom系统的意见清理,目前发现只有部分手机可能清理不赶紧,大部分手机手机清理非常彻底。白名单最可靠,微信、QQ等都在厂商白名单中,app进入后台,进程优先级也不会降低,因此不会被回收。

8.0系统前常见的保活方法:

1.1像素activity,手Q以前使用过该方法,低版本系统有效。

2.后台service播放无声音乐

3.双进程守护,相互之间唤起

4.监听系统网络切换、锁屏等广播,进行进程重启

5.第三方sdk帮助重启进程

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 卡顿是非常直观的用户体验,它的特点是:产生原因错综复杂,线上问题难以复现。基于这个特点,卡顿优化主要是三方面工作:...
    Stan_Z阅读 17,049评论 6 30
  • 性能的优化是一个老生常谈的点,也是一个比较重要的点。做过一点性能优化的工作,现在对工作中的优化点做一个总结。如有错...
    Xander_Wang阅读 505评论 0 1
  • Android的性能优化,主要是从以下几个方面进行优化的: 稳定(内存溢出、崩溃) 流畅(卡顿) 耗损(耗电、流量...
    Android_冯星阅读 12,264评论 4 101
  • 启动优化 冷启动 冷启动指的是应用程序从头开始:系统的进程没有,直到此开始,创建了应用程序的进程。 在应用程序自设...
    小夫哥阅读 507评论 0 0
  • 内存作为计算机程序运行最重要的资源之一,需要运行过程中做到合理的资源分配与回收,不合理的内存占用轻则使得用户应用程...
    Java李太白阅读 571评论 0 1

友情链接更多精彩内容