Android 性能优化

作者: 因为我的心 | 来源:发表于2019-08-25 17:03 被阅读0次

    一、前言:

    用android的都知道,新买的手机用过一段时间后,手机变得越来越卡了;装了一些APP后,电量用得飞快,一天基本要一充;有些APP打开半天加载不出来;有些APP进入某些页面突然闪退;还有用了一些APP,流量用得飞快,几百M的流量用了几天就没有了等等;

    1. 这是什么原因呢?
    • android系统源码是开放的,可以对源码的更改,像国内的几大手机厂商,都是对系统进入定制开发,这样就会引发一系列问题,比如说著名的系统碎片化问题;
    • 各大厂商定制的系统,兼容性肯定不好,开发人员要对各个系统做各种适配,还有开发人员的水平参次不齐,开发出来的APP就会出现这样那样的问题等等;

    2. 那我们又得如何处理这样的问题呢?

    那就是今天我们要说的APP性能优化,开发人员开发出来的app要性能好,用户体验好,性能好的APP总结一下,有如下几点:

    • 流畅 (APP不卡顿);
    • 耗损低 (省电、省流量);
    • 安装包(APP包尽量要小);
    • 稳定(不闪退、内存溢出、ANR);

    二、流畅-卡顿优化

    1. Android 应用启动慢,使用时经常卡顿,是非常影响用户体验的,应该尽量避免出现。总的来说造成卡顿的原因有如下几种:

    • UI的绘制。主要原因是绘制的层级深、页面复杂、刷新不合理,由于这些原因导致卡顿的场景更多出现在 UI 和启动后的初始界面以及跳转到页面的绘制上。
    • 数据处理上。导致这种卡顿场景的原因是数据处理量太大,一般分为三种情况,一是数据在主线程处理,这个是初级工程师会犯的错误,二是数据处理占用 CPU 高,导致主线程拿不到时间片,三是内存增加导致 GC 频繁,从而引起卡顿。引起卡顿的原因很多,但不管怎么样的原因和场景,最终都是通过设备屏幕上显示来达到用户,归根到底就是显示有问题,所以,要解决卡顿,就要先了解 Android 系统的渲染机制。
    • UI的过度绘制,绘制的页面有几层View,底层View都是隐藏的,这种的还绘制的话就会造成过度绘制

    2. andriod的渲染机制

    要在屏幕上显示,其实要经过一系列的过程,Android 应用程序把经过测量、布局、绘制后的 surface 缓存数据,通过 SurfaceFlinger 把数据渲染到显示屏幕上, 通过 Android 的刷新机制来刷新数据。也就是说应用层负责绘制,系统层负责渲染,通过进程间通信把应用层需要绘制的数据传递到系统层服务,系统层服务通过刷新机制把数据更新到屏幕上。

    这里我们先介绍一个名词:FPS。FPS 表示每秒传递的帧数。在理想情况下,60 FPS 就感觉不到卡,这意味着每个绘制时长应该在16 ms 以内。但是 Android 系统很有可能无法及时完成那些复杂的页面渲染操作。Android 系统每隔 16ms 发出 VSYNC 信号,触发对 UI 进行渲染,如果每次渲染都成功,这样就能够达到流畅的画面所需的 60FPS。如果某个操作花费的时间是 24ms ,系统在得到 VSYNC 信号时就无法正常进行正常渲染,这样就发生了丢帧现象。那么用户在 32ms 内看到的会是同一帧画面,这种现象在执行动画或滑动列表比较常见,还有可能是你的 Layout 太过复杂,层叠太多的绘制单元,无法在 16ms 完成渲染,最终引起刷新不及时。

    android的View的绘制流程大家应该都知道,都是要经过三大核心步骤:Measure、Layout、Draw。具体是如何实现的建议看一下View的源码,这里我就不多说了;如果绘制的层级深,页面复杂,在Measure、Layout这二个步骤要花费大量的时间;这样也会造卡顿现象;

    3. andriod卡顿优化方案

    • 不要在主线程进行网络访问/大文件的IO操作
    • 绘制UI时,尽量减少绘制UI层次;减少不必要的view嵌套,可以用Hierarchy Viewer工具来检测,后面会详细讲;
    • 当我们的布局是用的FrameLayout的时候,我们可以把它改成merge,可以避免自己的帧布局和系统的ContentFrameLayout帧布局重叠造成重复计算(measure和layout)
    • 提高显示速度,使用ViewStub:当加载的时候才会占用。不加载的时候就是隐藏的,仅仅占用位置。
    • 在view层级相同的情况下,尽量使用 LinerLayout而不是RelativeLayout;因为RelativeLayout在测量的时候会测量二次,而LinerLayout测量一次,可以看下它们的源码;
    • 删除控件中无用的属性;
    • 布局复用.比如listView 布局复用
    • 尽量避免过度绘制(overdraw),比如:背景经常容易造成过度绘制。由于我们布局设置了背景,同时用到的MaterialDesign的主题会默认给一个背景。这时应该把主题添加的背景去掉;还有移除 XML 中非必须的背景
    • 自定义View优化。使用 canvas.clipRect()来帮助系统识别那些可见的区域,只有在这个区域内才会被绘制。也是避免过度绘制.
    • 启动优化,启动速度的监控,发现影响启动速度的问题所在,优化启动逻辑,提高应用的启动速度。比如闪屏页面,合理优化布局,加载逻辑优化,数据准备等。
    • 合理的刷新机制,尽量减少刷新次数,尽量避免后台有高的 CPU 线程运行,缩小刷新区域。

    4. andriod卡顿优化所用到的工具

    性能问题并不容易复现,也不好定位,但是真的碰到问题就需要借助相应的的调试工具,下面介绍比较常用的调试工具。

    • Profile GPU Rendering
      在手机开发者模式下,有一个卡顿检测工具叫做:Profile GPU Rendering,看图:

    • Debug GPU overDraw过度绘制检测
      在手机开发者模式下,有一个过度绘制检测工具叫做:Debug GPU overDraw(调式 GPU 过度绘制):

      图片.png
    • 原色:没有过度绘制

    • 蓝色:1 次过度绘制

    • 绿色:2 次过度绘制

    • 粉色:3 次过度绘制

    • 红色:4 次及以上过度绘制

    三、耗损低 -耗电优化

    在移动设备中,电池的重要性不言而喻,没有电什么都干不成。

    1. 优化方案:

    • 合理的使用wack_lock锁,wake_lock锁主要是相对系统的休眠(这里就是为了省电,才做休)而言的,意思就是我的程序给CPU加了这个锁那系统就不会休眠了,这样做的目的是为了全力配合我们程序的运行。有的情况如果不这么做就会出现一些问题,比如微信等及时通讯的心跳包会在熄屏不久后停止网络访问等问题。所以微信里面是有大量使用到了wake_lock锁。这里有一篇关于wake_lock的使用,请查阅

    • 使用jobScheduler2,集中处理一些网络请求,有些不用很及时的处理可以放在充电的时候处理,比如,图片的处理,APP下载更新等等,这里有一篇关于jobScheduler的使用,请查阅

    • 计算优化,避开浮点运算等。

    • 数据在网络上传输时,尽量压缩数据后再传输,建议用FlatBuffer序列化技术,这个比json效率高很多倍,不了解FlatBuffer,建议找资料学习一下,后面有时间的话,也会专门写关于FlatBuffer的文章.

    2. andriod耗电分析所用到的工具

    在 Android5.0 以前,在应用中测试电量消耗比较麻烦,也不准确,5.0 之后专门引入了一个获取设备上电量消耗信息的 API:Battery Historian。Battery Historian 是一款由 Google 提供的 Android 系统电量分析工具,是一款图形化数据分析工具,直观地展示出手机的电量消耗过程,通过输入电量分析文件,显示消耗情况,最后提供一些可供参考电量优化的方法。

    Battery Historian耗电分析工具
    [图片上传失败...(image-4ecfd5-1566722662631)]

    Battery Historian耗电分析工具的开源地址;

    四、安装包-优化

    随着功能不断增加,APP的包肯定不会断的变大,但应用的安装包越大,用户下载的门槛越高,特别是在移动网络情况下,用户在下载应用时,对安装包大小的要求更高,因此,减小安装包大小可以让更多用户愿意下载和体验产品。所以,我们还是要想办法去如何去优化,尽量减小app的安排包。

    1. res资源优化

    (1)只使用一套图片,使用高分辨率的图片。
    (2)UI设计在ps安装TinyPNG插件,对图片进行无损压缩。
    (3)svg图片:一些图片的描述,牺牲CPU的计算能力的,节省空间。使用的原则:简单的图标。
    (4)图片使用WebP(https://developers.google.com/speed/webp/)的格式(Facebook、腾讯、淘宝在用。)缺点:加载相比于PNG要慢很多。 但是配置比较高。工具:http://isparta.github.io/
    (5)使用tintcolor(android - Change drawable color programmatically)实现按钮反选效果。

    2. 代码优化

    (1)实现功能模块的逻辑简化
    (2)Lint工具检查无用文件将无用的资源列在“UnusedResources: Unused resources”,删除。
    (3)移除无用的依赖库。

    3. lib资源优化

    (1)动态下载的资源。
    (2)一些模块的插件化动态添加。
    (3)so文件的剪裁和压缩。

    4. assets资源优化

    (1)音频文件最好使用有损压缩的格式,比如采用opus、mp3等格式,但是最好不要使用无损压缩的音乐格式
    (2)对ttf字体文件压缩,可以采用FontCreator工具只提取出你需要的文字。比如在做日期显示时,其实只需要数字字体,但是使用原有的字体库可能需要10MB大小,如果只是把你需要的字体提取出来生成的字体文件只有10KB

    5. 代码混淆。

    使用proGuard 代码混淆器工具,它包括压缩、优化、混淆等功能。

    6. 7z极限压缩

    具体请参考微信的安接包压缩,实现实现原理,有时间再分析;

    五、稳定-内存优化

    在 Android 系统中有个垃圾内存回收机制,在虚拟机层自动分配和释放内存,因此不需要在代码中分配和释放某一块内存,从应用层面上不容易出现内存泄漏和内存溢出等问题,但是需要内存管理。

    除此之外,部分 Android 应用开发人员在开发过程中并没有特别关注内存的合理使用,也没有在内存方面做太多的优化,当应用程序同时运行越来越多的任务,加上越来越复杂的业务需求时,完全依赖 Android 的内存管理机制就会导致一系列性能问题逐渐呈现,对应用的稳定性和性能带来不可忽视的影响,因此,解决内存问题和合理优化内存是非常有必要的。

    在开发的过程,如果方法不当的话,很容易造成内存泄漏,接下来,来说一下哪些情景容易出现内存泄漏。

    1. 内存泄漏出现的情景

    • 单例中引用的上下文Context,引用了Activity中的Context, 这样会造成内存泄漏,要引用Application中的Context;
    • 资源性对象未关闭。比如Cursor、File文件等,往往都用了一些缓冲,在不使用时,应该及时关闭它们。
    • 注册对象未注销。比如事件注册后未注销,会导致观察者列表中维持着对象的引用。
    • 类的静态变量持有大数据对象。
    • 非静态内部类的静态实例。
    • Handler临时性内存泄漏。如果Handler是非静态的,容易导致 Activity 或 Service 不会被回收。
    • 容器中的对象没清理造成的内存泄漏。
    • WebView。WebView 存在着内存泄漏的问题,在应用中只要使用一次 WebView,内存就不会被释放掉。

    2. 内存优化的方案

    • 对象引用。强引用、软引用、弱引用、虚引用四种引用类型,根据业务需求合理使用不同,选择不同的引用类型。
    • 减少不必要的内存开销。注意自动装箱,增加内存复用,比如有效利用系统自带的资源、视图复用、对象池、Bitmap对象的复用。
    • 使用最优的数据类型。比如针对数据类容器结构,可以使用ArrayMap数据结构,避免使用枚举类型,使用缓存Lrucache等等。
    • 图片内存优化。可以设置位图规格,根据采样因子做压缩,用一些图片缓存方式对图片进行管理等等。图片的压缩几种方案;
    • 大量使用线程时,采用线程池。
    • 避免创建过多的对象。
    • 不要过多使用枚举,枚举占用的内存空间要比整型大。
    • 常量请使用 static final 来修饰。
    • 采用内存缓存和磁盘缓存。
    • 适当使用软引用和弱引用。
    • 尽量采用静态内部类,这样可以避免潜在的由于内部类而导致的内存泄漏。

    3. 内存分析工具

    做内存优化前,需要了解当前应用的内存使用现状,通过现状去分析哪些数据类型有问题,各种类型的分布情况如何,以及在发现问题后如何发现是哪些具体对象导致的,这就需要相关工具来帮助我们。以下介绍几种内存分析工具

    • Memory Monitor
      Memory Monitor 是一款使用非常简单的图形化工具,可以很好地监控系统或应用的内存使用情况.
      主要有以下功能:

    (1) 显示可用和已用内存,并且以时间为维度实时反应内存分配和回收情况。
    (2) 快速判断应用程序的运行缓慢是否由于过度的内存回收导致。
    (3) 快速判断应用是否由于内存不足导致程序崩溃。

    • Heap Viewer
      Heap Viewer 的主要功能是查看不同数据类型在内存中的使用情况,可以看到当前进程中的 Heap Size 的情况,分别有哪些类型的数据,以及各种类型数据占比情况。通过分析这些数据来找到大的内存对象,再进一步分析这些大对象,进而通过优化减少内存开销,也可以通过数据的变化发现内存泄漏。
      主要有以下功能:
      (1) 实时查看App分配的内存大小和空闲内存大小
      (2) 发现Memory Leaks
      Heap Viewer不光可以用来检测是否有内存泄漏,对于内存抖动,我们也可以用该工具检测,因为内存抖动的时候,会频繁发生GC,这个时候我们只需要开启Heap Viewer,观察数据的变化,如果发生内存抖动,会观察到数据在段时间内频繁更新。
    • Allocation Tracker
      Memory Monitor 和 Heap Viewer 都可以很直观且实时地监控内存使用情况,还能发现内存问题,但发现内存问题后不能再进一步找到原因,或者发现一块异常内存,但不能区别是否正常,同时在发现问题后,也不能定位到具体的类和方法。这时就需要使用另一个内存分析工具 Allocation Tracker,进行更详细的分析, Allocation Tracker 可以分配跟踪记录应用程序的内存分配,并列出了它们的调用堆栈,可以查看所有对象内存分配的周期。

    • Memory Analyzer Tool(MAT)
      MAT 是一个快速,功能丰富的 Java Heap 分析工具,通过分析 Java 进程的内存快照 HPROF 分析,从众多的对象中分析,快速计算出在内存中对象占用的大小,查看哪些对象不能被垃圾收集器回收,并可以通过视图直观地查看可能造成这种结果的对象。

    4. 稳定性优化

    Android 应用的稳定性定义很宽泛,影响稳定性的原因很多,比如内存使用不合理、代码异常场景考虑不周全、代码逻辑不合理等,都会对应用的稳定性造成影响。其中最常见的两个场景是:Crash 和 ANR,这两个错误将会使得程序无法使用,比较常用的解决方式如下:

    • 提高代码质量。比如开发期间的代码审核,看些代码设计逻辑,业务合理性等。
    • 代码静态扫描工具。常见工具有Android Lint、Findbugs、Checkstyle、PMD等等。
    • Crash监控。把一些崩溃的信息,异常信息及时地记录下来,以便后续分析解决。
    • Crash上传机制。在Crash后,尽量先保存日志到本地,然后等下一次网络正常时再上传日志信息。

    六、总结:

    其实app性能优化,不是一二天可以完成的,主要是要开发的过程,不断的提前代码的质量,开发人员提高自己的开发水平,发现了问题,就要及时的解决。


    参考作者:android的那点事
    链接:https://www.jianshu.com/p/d71b51a0e29f

    相关文章

      网友评论

        本文标题:Android 性能优化

        本文链接:https://www.haomeiwen.com/subject/amahectx.html