Android比较实用的性能优化

  Android设备作为一种移动设备,无论是内存还是CPU的性能都受到了很大的限制,这导致Android程序的性能问题异常突出,随着产品的不断更新迭代,对于性能优化提出了更高的要求。本篇文章从稳定性、流畅性、耗损、安装包大小四个方面对Android开发提供了一些容易上手、切实有效的性能优化方法,为Android开发中有关性能优化方面的学习提供一个参考。

1.稳定性(解决内存溢出、崩溃等问题),内存优化

了解内存如何分配和回收机制,对内存优化会有一定的认识和掌握。可以借助内存分析工具,方便定位需要内存优化的代码。

(1)Memory Monitor 工具:

  Android Studio自带的一个内存监视工具,它可以很好地帮助我们进行内存实时分析。通过点击Android Studio右下角的Memory Monitor标签,打开工具可以看见较浅蓝色代表free的内存,而深色的部分代表使用的内存从内存变换的走势图变换,可以判断关于内存的使用状态,例如当内存持续增高时,可能发生内存泄漏;当内存突然减少时,可能发生GC等

(2)Memory Analyzer 工具:

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

(a)屏幕多次翻转,出现内存持续增高时。点击 Dump java Heap就会生成运行内存快照hprof文件。

(b)然后将APP完全退出,重新启动,打开Android Monitor 再次点击Dump java Heap 生成一份还没操作(旋转屏幕)前的内存快照hprof文件。现在就已经生成好了2份hprof文件, 一份是没有旋转过屏幕的 ,一份是旋转过屏幕多次的。

(c)然后选中Android Studio 最左边的Captures 进行将hprof文件导出。导出的时候需要选择保存的目录以及文件名。

(d)打开MAT ,导入我们的2个hprof文件 Open File-->选择文件-->Leak Suspects Report-->Finish

可以通过检索包名,查看某个类的实例个数和所在内存数据,还可以查看被引用的内存数据。

  Objects:实例个数
  Shallow Heap:所占内存大小
  Retained Heap:释放后能回收多少内存

(3)LeakCanary工具:

  简单,傻瓜式操作。可以在GitHup官网查阅https://github.com/square/leakcanary,我们可以在Gradle文件里添加依赖。这个工具是Square公司在Github开源的。行业内不是有一句话嘛,Square出品必属精品,主流的库像okhttp、Picasso、retrofit、Dagger等都出自Square之手。说到这不得不让我联想到一位在Android开发领域神一般存在的人物,他就是大名鼎鼎的Jake Wharton(杰克.沃顿),ButterKnife的创造者,也参与贡献了Retrofit, okhttp等。

 

(4)Android Lint 工具:

 

    Android Lint Tool 是Android Sutido种集成的一个Android代码提示工具,它可以给你布局、代码提供非常强大的帮助。
硬编码会提示以级别警告,例如:在布局文件中写了三层冗余的LinearLayout布局、直接在TextView中写要显示的文字、字体大小使用dp而不是sp为单位,就会在编辑器右边看到提示。使用Android Studio的lint可以清除无用的资源文件(点击菜单栏的Analyze -> Run Inspection by Name, 输入unused resource)。
    当然以上都是一个简单的举例,Lint的功能非常强大,大家应该养成写完代码查看Lint的习惯,这不仅让你及时发现代码种隐藏的一些问题,更能让你养成良好的代码风格,要知道,这些Lint提示可都是Google大牛们汗水合智慧的结晶。

小结

    影响稳定性的原因很多,比如内存使用不合理、代码异常场景考虑不周全、代码逻辑不合理等,都会对应用的稳定性造成影响。其中最常见的两个场景是:Crash 和 ANR,这两个错误将会使得程序无法使用。所以做好Crash全局监控,处理闪退同时把崩溃信息、异常信息收集记录起来,以便后续分析;合理使用主线程处理业务,不要在主线程中做耗时操作,防止ANR程序无响应发生。

 

2.流畅性(卡顿优化)

卡顿的场景通常是发生在用户交互体验最直接的方面。影响卡顿的两大因素,分别是界面绘制和数据处理。
  • 界面绘制:主要原因是绘制的层级深、页面复杂、刷新不合理,由于这些原因导致卡顿的场景更多出现在 UI 和启动后的初始界面以及跳转到页面的绘制上。
  • 数据处理:导致这种卡顿场景的原因是数据处理量太大,一般分为三种情况,一是数据在处理 UI 线程,二是数据处理占用 CPU 高,导致主线程拿不到时间片,三是内存增加导致 GC 频繁,从而引起卡顿。
卡顿场景大概分为四个方面,如下图所示。
 

分析UI卡顿:

    我们知道Android的绘制步骤是:Measure、Layout、Draw,所以布局的层级越深、元素越多、耗时也就越长。还有就是Android操作系统每隔 16ms 发出 VSYNC 信号,触发对 UI 进行渲染,如果每次渲染都成功,这样就能够达到流畅的画面所需的 60FPS。如果某个操作花费的时间是 24ms ,系统在得到 VSYNC 信号时就无法正常进行正常渲染,这样就发生了丢帧现象。那么用户在 32ms 内看到的会是同一帧画面,无法在 16ms 完成渲染,最终引起刷新不及时。

    总结以上,两个根本原因:1、绘制任务太重,绘制一帧内容耗时太长;2、主线程太忙,根据系统传递过来的 VSYNC 信号来时,还没准备好数据,导致丢帧。

(1)布局优化

    在Android种系统对View进行测量、布局和绘制时,都是通过对View数的遍历来进行操作的。如果一个View数的高度太高就会严重影响测量、布局和绘制的速度。Google也在其API文档中建议View高度不宜哦过10层。现在版本种Google使用RelativeLayout替代LineraLayout作为默认根布局,目的就是降低LineraLayout嵌套产生布局树的高度,从而提高UI渲染的效率。
  • 布局复用,使用<include>标签重用layout;
  • 提高显示速度,使用<ViewStub>延迟View加载;
  • 减少层级,使用<merge>标签替换父级布局;
  • 注意使用wrap_content,会增加measure计算成本;

  • 删除控件中无用属性;

(2)绘制优化

    过度绘制是指在屏幕上的某个像素在同一帧的时间内被绘制了多次。在多层次重叠的 UI 结构中,如果不可见的 UI 也在做绘制的操作,就会导致某些像素区域被绘制了多次,从而浪费了多余的 CPU 以及 GPU 资源。如何避免过度绘制?
  • 布局上的优化。移除 XML 中非必须的背景,移除 Window 默认的背景、按需显示占位背景图片

  • 自定义View优化。使用 canvas.clipRect() 帮助系统识别那些可见的区域,只有在这个区域内才会被绘制。

(3)启动优化

    应用一般都有闪屏页SplashActivity,优化闪屏页的 UI 布局,可以通过 Profile GPU Rendering 检测丢帧情况。

    (另外,还可以IDE自带的一款UI绘制检测图形化数据分析工具 Systrace ,4.0版本以上版本可以使用。)

 闪屏页的存在可以说就是启动优化,通常在闪屏页延迟2秒跳转到主界面但是在进入首页的时候,首页复杂的View渲染以及必须在UI线程执行的业务逻辑,必然拖慢了启动速度。启动闪屏页虽然简单执行快,首页却复杂执行慢,应用启动前轻后重。

    所以要启动加载逻辑优化。可以采用分布加载、异步加载、延期加载策略来提高应用启动速。例如,把SplashActivity改成SplashFragment,应用程序的入口变成MainActivity,在MainActivity中先展示SplashFragment,显示完毕后再移除SplashFragment。这样,在SplashFragment的2S的友好时间内进行数据准备的同时,首页的View就能够被加载,首页的业务逻辑就能够被执行。在闪屏页窗口加载完毕后,我们加载activity_main的布局,考虑到这个布局有可能比较复杂,耽误View的解析时间,可以采用ViewStub的形式进行懒加载。

(4)刷新优化

  1. 减少刷新次数;
  2. 缩小刷新区域;

(5)动画优化

      需要实现动画效果时,需要根据不同场景选择合适的动画框架来实现。有些情况下,可以用硬件加速方式来提供流畅度降低动画卡顿。

3.耗损(耗电、流量消耗)

 3.1耗电优化

在移动设备中,电池的重要性自然不言而喻,如果手机没电了应用功能技术实现再怎么牛逼,用户也什么都干不成。对于Android操作系统和设备各大开发商来说,对手机耗电的优化从没有停止过,不断地追求更长的待机时间。而对于开发一款应用来说,绝不可以忽略电量耗损的问题,被归为“电池杀手”的应用,最终的结果无疑是走向被用户卸载的道路。比如,有些应用为了保持应用进程长期在后台存活,使用各种不合理进程保活方案,破坏操作系统“生态平衡”,导致用户电量严重耗损,虽然这种流氓开发行为并不违法吧,但是也属于不道德的行为,也同样会被同行所被鄙视的行为。

 

    在 Android5.0 以前,关于应用电量消耗的测试即麻烦又不准确,而5.0 之后Google专门引入了一个获取设备上电量消耗信息的API—— Battery Historian。Battery Historian 是一款由 Google 提供的 Android 系统电量分析工具,直观地展示出手机的电量消耗过程,通过输入电量分析文件,显示消耗情况。

 

    最后提供一些可供参考耗电优化的方法:

(1)计算优化。算法for循环优化Switch..case替代if..else、避开浮点运算。

        浮点运算:计算机里整数和小数形式就是按普通格式进行存储,例如1024、3.1415926等等,这个没什么特点,但是这样的数精度不高,表达也不够全面,为了能够有一种数的通用表示法,就发明了浮点数。浮点数的表示形式有点像科学计数法(*.*****×10^***),它的表示形式是0.*****×10^***,在计算机中的形式为 .***** e ±***),其中前面的星号代表定点小数,也就是整数部分为0的纯小数,后面的指数部分是定点整数。利用这样的形式就能表示出任意一个整数和小数,例如1024就能表示成0.1024×10^4,也就是 .1024e+004,3.1415926就能表示成0.31415926×10^1,也就是 .31415926e+001,这就是浮点数。浮点数进行的运算就是浮点运算。浮点运算比常规运算更复杂,因此计算机进行浮点运算速度要比进行常规运算慢得多。

(2)避免 Wake Lock 使用不当。

    Wake Lock是一种锁的机制,主要是相对系统的休眠而言的,,只要有人拿着这个锁,系统就无法进入休眠意思就是我的程序给CPU加了这个锁那系统就不会休眠了,这样做的目的是为了全力配合我们程序的运行。有的情况如果不这么做就会出现一些问题,比如微信等及时通讯的心跳包会在熄屏不久后停止网络访问等问题。所以微信里面是有大量使用到了Wake_Lock锁。系统为了节省电量,CPU在没有任务忙的时候就会自动进入休眠。有任务需要唤醒CPU高效执行的时候,就会给CPU加Wake_Lock锁。大家经常犯的错误,我们很容易去唤醒CPU来工作,但是很容易忘记释放Wake_Lock。
 

(3)使用 Job Scheduler 管理后台任务。

   在Android 5.0 API 21 中,google提供了一个叫做JobScheduler API的组件,来处理当某个时间点或者当满足某个特定的条件时执行一个任务的场景,例如当用户在夜间休息时或设备接通电源适配器连接WiFi启动下载更新的任务。这样可以在减少资源消耗的同时提升应用的效率。

 3.2流量优化

3.2.1 对通讯录式、个人信息式的数据进行数据库的存储;
3.2.2 对浏览类的数据进行三级缓存

4.安装包(缩小apk大小)

 详见本人的另一篇文章——Android性能优化之apk终极瘦身策略

Android比较实用的性能优化

全文结束