android开发(性能篇)

 

今天想说的重点是Android APP性能优化,也就是在开发应用程序时应该注意的点有哪些,如何更好地提高用户体验。一个好的应用,除了要有吸引人的功能和交互之外,在性能上也应该有高的要求,即时应用非常具有特色,在产品前期可能吸引了部分用户,但是用户体验不好的话,也会给产品带来不好的口碑。那么一个好的应用应该如何定义呢?主要有以下三方面:

  • 业务/功能

  • 符合逻辑的交互

  • 优秀的性能

众所周知,Android系统作为以移动设备为主的操作系统,硬件配置是有一定的限制的,虽然配置现在越来越高级,但仍然无法与PC相比,在CPU和内存上使用不合理或者耗费资源多时,就会碰到内存不足导致的稳定性问题、CPU 消耗太多导致的卡顿问题等。

面对问题时,大家想到的都是联系用户,然后查看日志,但殊不知有关性能类问题的反馈,原因也非常难找,日志大多用处不大,为何呢?因为性能问题大部分是非必现的问题,问题定位很难复现,而又没有关键的日志,当然就无法找到原因了。这些问题非常影响用户体验和功能使用,所以了解一些性能优化的一些解决方案就显得很重要了,并在实际的项目中优化我们的应用,进而提高用户体验。

 

四个方面

可以把用户体验的性能问题主要总结为4个类别:

  • 流畅

  • 稳定

  • 省电、省流量

  • 安装包小

性能问题的主要原因是什么,原因有相同的,也有不同的,但归根到底,不外乎内存使用、代码效率、合适的策略逻辑、代码质量、安装包体积这一类问题,整理归类如下:

从图中可以看到,打造一个高质量的应用应该以4个方向为目标:快、稳、省、小。

  • 快:使用时避免出现卡顿,响应速度快,减少用户等待的时间,满足用户期望。

  • 稳:减低 crash 率和 ANR 率,不要在用户使用过程中崩溃和无响应。

  • 省:节省流量和耗电,减少用户使用成本,避免使用时导致手机发烫。

  • 小:安装包小可以降低用户的安装成本。

 

要想达到这4个目标,具体实现是在右边框里的问题:卡顿、内存使用不合理、代码质量差、代码逻辑乱、安装包过大,这些问题也是在开发过程中碰到最多的问题,在实现业务需求同时,也需要考虑到这点,多花时间去思考,如何避免功能完成后再来做优化,不然的话等功能实现后带来的维护成本会增加。

 

卡顿优化

Android 应用启动慢,使用时经常卡顿,是非常影响用户体验的,应该尽量避免出现。卡顿的场景有很多,按场景可以分为4类:UI 绘制、应用启动、页面跳转、事件响应,如图:

这4种卡顿场景的根本原因可以分为两大类:

  • 界面绘制。主要原因是绘制的层级深、页面复杂、刷新不合理,由于这些原因导致卡顿的场景更多出现在UI和启动后的初始界面以及跳转到页面的绘制上。

  • 数据处理。导致这种卡顿场景的原因是数据处理量太大,一般分为三种情况,一是数据在处理UI线程,二是数据处理占用CPU高,导致主线程拿不到时间片,三是内存增加导致GC频繁,从而引起卡顿。

引起卡顿的原因很多,但不管怎么样的原因和场景,最终都是通过设备屏幕上显示来达到用户,归根到底就是显示有问题,所以,要解决卡顿,就要先了解Android系统的显示原理。

Android系统显示原理

Android显示过程可以简单概括为:Android应用程序把经过测量、布局、绘制后的Surface缓存数据,通过SurfaceFlinger把数据渲染到显示屏幕上, 通过Android的刷新机制来刷新数据。也就是说应用层负责绘制,系统层负责渲染,通过进程间通信把应用层需要绘制的数据传递到系统层服务,系统层服务通过刷新机制把数据更新到屏幕上。

我们都知道在Android的每个View绘制中有三个核心步骤:Measure、Layout、Draw。具体实现是从 ViewRootImp类的performTraversals() 方法开始执行,Measure和Layout都是通过递归来获取View的大小和位置,并且以深度作为优先级,可以看出层级越深、元素越多、耗时也就越长。

真正把需要显示的数据渲染到屏幕上,是通过系统级进程中的SurfaceFlinger服务来实现的,那么这个SurfaceFlinger服务主要做了哪些工作呢?如下:

  • 响应客户端事件,创建Layer与客户端的Surface建立连接。

  • 接收客户端数据及属性,修改Layer属性,如尺寸、颜色、透明度等。

  • 将创建的Layer内容刷新到屏幕上。

  • 维持Layer的序列,并对Layer最终输出做出裁剪计算。

既然是两个不同的进程,那么肯定是需要一个跨进程的通信机制来实现数据传递,在Android显示系统中,使用了Android的匿名共享内存:SharedClient,每一个应用和SurfaceFlinger之间都会创建一个SharedClient ,然后在每个SharedClient中,最多可以创建31个 SharedBufferStack,每个Surface都对应一个SharedBufferStack,也就是一个Window。

一个SharedClient对应一个Android应用程序,而一个Android应用程序可能包含多个窗口,即Surface。也就是说SharedClient包含的是SharedBufferStack的集合,其中在显示刷新机制中用到了双缓冲和三重缓冲技术。最后总结起来显示整体流程分为三个模块:应用层绘制到缓存区,SurfaceFlinger把缓存区数据渲染到屏幕,由于是不同的进程,所以使用Android的匿名共享内存SharedClient缓存需要显示的数据来达到目的。

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

卡顿根本原因

根据Android系统显示原理可以看到,影响绘制的根本原因有以下两个方面:

  • 绘制任务太重,绘制一帧内容耗时太长。

  • 主线程太忙,根据系统传递过来的VSYNC信号来时还没准备好数据导致丢帧。

绘制耗时太长,有一些工具可以帮助我们定位问题。主线程太忙则需要注意了,主线程关键职责是处理用户交互,在屏幕上绘制像素,并进行加载显示相关的数据,所以特别需要避免任何主线程的事情,这样应用程序才能保持对用户操作的即时响应。总结起来,主线程主要做以下几个方面工作:

  • UI生命周期控制

  • 系统事件处理

  • 消息处理

  • 界面布局

  • 界面绘制

  • 界面刷新

除此之外,应该尽量避免将其他处理放在主线程中,特别复杂的数据计算和网络请求等。

性能分析工具

性能问题并不容易复现,也不好定位,但是真的碰到问题还是需要去解决的,那么分析问题和确认问题是否解决,就需要借助相应的的调试工具,比如查看Layout层次的Hierarchy View、Android系统上带的GPU Profile工具和静态代码检查工具Lint等,这些工具对性能优化起到非常重要的作用,所以要熟悉,知道在什么场景用什么工具来分析。

1,Profile GPU Rendering

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

它的功能特点如下:

  • 一个图形监测工具,能实时反应当前绘制的耗时

  • 横轴表示时间,纵轴表示每一帧的耗时

  • 随着时间推移,从左到右的刷新呈现

  • 提供一个标准的耗时,如果高于标准耗时,就表示当前这一帧丢失

2,TraceView

TraceView是Android SDK自带的工具,用来分析函数调用过程,可以对Android的应用程序以及Framework层的代码进行性能分析。它是一个图形化的工具,最终会产生一个图表,用于对性能分析进行说明,可以分析到每一个方法的执行时间,其中可以统计出该方法调用次数和递归次数,实际时长等参数维度,使用非常直观,分析性能非常方便。

3,Systrace UI 性能分析

Systrace是Android 4.1及以上版本提供的性能数据采样和分析工具,它是通过系统的角度来返回一些信息。它可以帮助开发者收集Android关键子系统,如Surfaceflinger、WindowManagerService等Framework部分关键模块、服务、View系统等运行信息,从而帮助开发者更直观地分析系统瓶颈,改进性能。Systrace的功能包括跟踪系统的I/O操作、内核工作队列、CPU负载等,在UI显示性能分析上提供很好的数据,特别是在动画播放不流畅、渲染卡等问题上。

优化建议

1,布局优化

布局是否合理主要影响的是页面测量时间的多少,我们知道一个页面的显示测量和绘制过程都是通过递归来完成的,多叉树遍历的时间与树的高度h有关,其时间复杂度O(h),如果层级太深,每增加一层则会增加更多的页面显示时间,所以布局的合理性就显得很重要。

那布局优化有哪些方法呢,主要通过减少层级、减少测量和绘制时间、提高复用性三个方面入手。总结如下:

  • 减少层级。合理使用RelativeLayout和LinerLayout,合理使用Merge。

  • 提高显示速度。使用ViewStub,它是一个看不见的、不占布局位置、占用资源非常小的视图对象。

  • 布局复用。可以通过

    标签来提高复用。

  • 尽可能少用wrap_content。wrap_content 会增加布局measure时计算成本,在已知宽高为固定值时,不用wrap_content 。

  • 删除控件中无用的属性。

2,避免过度绘制

过度绘制是指在屏幕上的某个像素在同一帧的时间内被绘制了多次。在多层次重叠的UI结构中,如果不可见的UI也在做绘制的操作,就会导致某些像素区域被绘制了多次,从而浪费了多余的CPU以及GPU源。

如何避免过度绘制呢,如下:

  • 布局上的优化。移除XML中非必须的背景,移除Window默认的背景、按需显示占位背景图片

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

3,启动优化

通过对启动速度的监控,发现影响启动速度的问题所在,优化启动逻辑,提高应用的启动速度。启动主要完成三件事:UI布局、绘制和数据准备。因此启动速度优化就是需要优化这三个过程:

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

  • 启动加载逻辑优化。可以采用分布加载、异步加载、延期加载策略来提高应用启动速度。

  • 数据准备。数据初始化分析,加载数据可以考虑用线程初始化等策略。

4,合理的刷新机制

在应用开发过程中,因为数据的变化,需要刷新页面来展示新的数据,但频繁刷新会增加资源开销,并且可能导致卡顿发生,因此,需要一个合理的刷新机制来提高整体的UI流畅度。合理的刷新需要注意以下几点:

  • 尽量减少刷新次数。

  • 尽量避免后台有高的CPU线程运行。

  • 缩小刷新区域。

5,其他

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

 

内存优化

在Android系统中有个垃圾内存回收机制,在虚拟机层自动分配和释放内存,因此不需要在代码中分配和释放某一块内存,从应用层面上不容易出现内存泄漏和内存溢出等问题,但是需要内存管理。Android系统在内存管理上有一个Generational Heap Memory模型,内存回收的大部分压力不需要应用层关心,Generational Heap Memory有自己一套管理机制,当内存达到一个阈值时,系统会根据不同的规则自动释放系统认为可以释放的内存,也正是因为Android程序把内存控制的权力交给了Generational Heap Memory,一旦出现内存泄漏和溢出方面的问题,排查错误将会成为一项异常艰难的工作。除此之外,部分Android应用开发人员在开发过程中并没有特别关注内存的合理使用,也

  • 2
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值