头痛的apk卡顿,我该从哪些方面进行优化?带你了解常见方案

Android性能优化七种方式

1.Android性能优化之交互优化

交互是用户体验最直接的方面,交互场景大致可以分为四个部分: ui 呈现、应用程序启动、页面跳转和事件响应。对于以上四个方面,可以从以下两个方面进行优化:

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

数据处理:导致这种卡顿场景的原因是数据处理量太大,一般分为三种情况,

一是数据处理在UI线程(这种应该避免),

二是数据处理占用CPU高,导致主线程拿不到时间片,

三是内存增加导致GC频繁,从而引起卡顿。

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

棉白杨现象有两个原因:

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

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

根据问题的原因,我们可以通过以下方式进行优化:

布局优化

在Android种系统对View进行测量、布局和绘制时,都是通过对View数的遍历来进行操作的。如果视图的高度过高,会严重影响测量、布局和渲染的速度。Google也在API文档中建议View树的高度不宜超过10层。使用RelativeLayout替代LineraLayout作为默认根布局降低LineraLayout嵌套产生布局树的高度从而提高UI渲染的效率。

在布局优化方面,我们可以通过以下方式进行优化:

布局复用,使用标签重用layout;

提高显示速度,使用延迟View加载;

减少层级,使用标签替换父级布局;

注意使用wrap_content,会增加measure计算成本;

删除控件中无用属性;

3.Android性能优化之渲染优化

透支是指屏幕上的一个像素在同一帧中被绘制多次。在多层次重叠的UI结构中如果不可见的UI也在做绘制的操作就会导致某些像素区域被绘制了多次从而浪费了多余的CPU以及GPU资源。我们可以通过开启手机的透支功能来检查页面是否透支。

为了避免过度绘制,我们可以从以下几个方面进行优化:

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

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

4.Android性能优化之启动优化

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

你也可以通过启动加载逻辑来优化。可以使用分布式加载、异步加载和延迟加载策略来提高应用程序的启动速度。

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

5.Android性能优化之刷新优化

Android开发中,通常是异步操作页面的,因此需要可以从刷新优化上来优化应用,主要有两个原则:

减少刷新次数;

缩小刷新区域;

6.Android性能优化之动画优化

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

7.Android性能优化之耗电优化

在移动设备中,电池的重要性不言而喻,没有电什么也做不了。对于操作系统和设备开发者来说,功耗的优化并没有停下来追求更长的待机时间,但是对于一个应用来说,功耗的问题是不容忽视的,尤其是那些被归类为“电池杀手”的应用,最终被卸载。因此,应用程序开发人员在实现需求时需要最小化功耗。

内存优化

1.1 内存泄漏

Java虚拟机中的对象

内存泄漏

1.2 内存溢出(OOM Out Of Memory)

内存泄漏一般导致应用卡顿,极端情况会导致OOM,OOM的原因是因为超过内存的阈值。原因主要有两方面:

  • 内存泄漏,导致无法及时释放导致OOM;
  • 一些逻辑消耗了大量内存,无法及时释放或者超过导致OOM;

能够消耗大量内存的,绝大多数是因为图片加载。这是OOM出现最频繁的地方。图片加载,一个是控制每次加载的数量,二是保证每次滑动的时候不进行加载,滑动完进行加载。一般情况使用先进后出,而不是先进先出。不过一般我们图片加载都是使用fresco或者Glide等开源库。

下面是导致内存溢出的两种情况:

通过命令行查看内存消耗情况(adb shell dumpsys meminfo 包名 -d):

UI优化

UI优化主要包括布局优化以及View的绘制优化。有时候我们打开某个软件,会出现卡顿的情况,这就是UI的问题。什么情况会导致卡顿呢?一般是如下几种情况:

  • 在UI线程中做耗时操作,导致UI线程卡顿;
  • 布局Layout过于复杂,无法在16ms内完成渲染;
  • 同一时间动画执行的次数过多,导致CPU或GPU负载过重;
  • View过度绘制,导致某些像素在同一帧时间内被绘制多次,从而使CPU或GPU负载过重;
  • View频繁的触发measure、layout,导致measure、layout累计耗时过多及整个View频繁的重新渲染;
  • 内存频繁触发GC过多(同一帧中频繁创建内存),导致暂时阻塞渲染操作; 冗余资源及逻辑等导致加载和执行缓慢;
  • ANR;

可以看见,上面这些导致卡顿的原因都是我们平时开发中非常常见的。有些运行起来还可以的程序,一旦进行一些瞬时测试或压力测试,就会发现很多性能问题。

2.1 布局优化

2.1.1 GPU绘制

对于UI性能的优化还可以通过开发者选项中的GPU过度绘制工具来进行分析。在设置->开发者选项->调试GPU过度绘制(不同设备可能位置或者叫法不同)中打开调试后可以看见如下图(对settings当前界面过度绘制进行分析):

以下说明:

蓝色(1x过度绘制),淡绿(2x过度绘制),淡红(3x过度绘制),深红(4x过度绘制)代表了4种不同程度的Overdraw情况,我们的目标就是尽量减少红色Overdraw,看到更多的蓝色区域。

Overdraw有时候是因为UI布局存在大量重叠的部分,还有的时候是因为非必须的重叠背景。例如某个Activity有一个背景,然后里面的Layout又有自己的背景,同时子View又分别有自己的背景。仅仅是通过移除非必须的背景图片,这就能够减少大量的红色Overdraw区域,增加蓝色区域的占比。这一措施能够显著提升程序性能。

如果布局中既能采用RealtiveLayout和LinearLayout,那么直接使用LinearLayout,因为Relativelayout的布局比较复杂,绘制的时候需要花费更多的CPU时间。如果需要多个LinearLayout或者Framelayout嵌套,那么可采用Relativelayout。因为多层嵌套导致布局的绘制有大部分是重复的,这会减少程序的性能。

2.1.2 GPU呈现模式分析

设置–>开发者选项–>GPU呈现模式分析–>在屏幕上显示为条形图,如图所示:

随着界面的刷新,界面上会以实时柱状图来显示每帧的渲染时间,柱状图越高表示渲染时间越长,每个柱状图偏上都有一根代表16ms基准的绿色横线,每一条竖着的柱状线都包含三部分(蓝色代表测量绘制Display List的时间,红色代表OpenGL渲染Display List所需要的时间,黄色代表CPU等待GPU处理的时间),只要我们每一帧的总时间低于基准线就不会发生UI卡顿问题(个别超出基准线其实也不算什么问题的)。

2.2 绘制优化

绘制优化主要是指View.onDraw方法需要避免执行大量的操作:

  • onDraw方法不需要创建新的局部对象,这是因为onDraw方法是实时执行的,产生大量的临时对象,导致占用了更多内存,并且使系统不断的GC,降低了执行效率;
  • onDraw方法不需要执行耗时操作,在onDraw方法里少使用循环,因为循环会占用CPU的时间。导致绘制不流畅,卡顿等等。 Google官方指出,View的绘制帧率稳定在60dps,这要求每帧的绘制时间不超过16ms(1000/60)。虽然很难保证,但我们需要尽可能的降低;

60dps是目前最合适的图像显示速度,也是绝大部分Android设备设置的调试频率,如果在16ms内顺利完成界面刷新操作可以展示出流畅的画面,而由于任何原因导致接收到VSYNC信号的时候无法完成本次刷新操作,就会产生掉帧的现象,刷新帧率自然也就跟着下降(假定刷新帧率由正常的60fps降到30fps,用户就会明显感知到卡顿)。

速度优化

3.1 ANR问题(application not responding)

Android官方规定:Activity如果在5s内无响应事件(屏幕触摸事件或者键盘输入事件);BroadcastReceiver如果在10s内无法处理完成;Service如果20s内无法处理完成。这三种情况会导致ANR。

上面说的三种导致ANR的情况,绝大多数就是因为线程阻塞导致的。 那么应该如何处理呢?Android系统为我们提供了若干组工具类来解决此问题:

  • Asynctask:为UI线程与工作线程之间进行快速处理的切换提供一种简单便捷的机制。适用于当下立即需要启动,但是异步执行的生命周期短暂的场景;
  • HandlerThread:为某些回调方法或者等待某些执行任务的执行设置一个专属的线程,并提供线程任务的调度机制;
  • ThreadPool:把任务分解成不同的单元,分发到各个不同的线程上,并发处理;
  • IntentService:适合执行由UI触发的后台任务。并可以把这些任务执行的情况通过一定的机制反馈给UI;

线程池

3.2 图片处理

  • 使用WebP格式:同样的照片,采用WebP格式可大幅节省流量,相对于JPG格式的图片,流量能节省将近25%到35 %;相对于 PNG格式的图片,流量可以节省将近80%。最重要的是使用WebP之后图片质量也没有改变;
  • 使用缩略图;

3.3 网络请求处理

  • 对服务端返回数据进行缓存,设定有效时间,在有效时间之内不走网络请求,减少流量消耗;
  • 在某些情况,尽量少使用GPS定位,如果条件允许,尽可能使用网络定位; 下载、上传,尽可能使用断点续传;

启动优化

Android应用的启动方式分为三种:冷启动、暖启动、热启动,不同的启动方式决定了应用UI对用户可见所需要花费的时间长短,冷启动消耗的时间最长。基于冷启动方式的优化工作也是最考验产品用户体验的地方。

4.1 冷启动

冷启动是在启动应用前,系统没有获取到当前APP的Activity、Service等等。例如,第一次启动APP,又或者说杀死进程后第一次启动。那么对比其他两种方式。冷启动自然是耗时最久的。

应用发生冷启动时,系统一定会执行下面的三个任务:

  • 开始加载并启动应用;
  • 应用启动后,显示一个空白的启动窗口(启动闪屏页);
  • 创建应用信息;

那么创建应用信息,系统就需要做:

  • Application的初始化
  • 启动UI线程
  • 创建Activity
  • 导入视图(inflate view)
  • 计算视图大小(onMesure view)
  • 得到视图排版(onLayout view)
  • 绘制视图(onDraw view)

这其中有两个creation 工作,分别为Application和Activity creation。它们均在View绘制展示之前。所以,在应用自定义的 Application类和第一个Activity类中,onCreate()方法做的事情越多,冷启动消耗的时间越长。

4.2 暖启动

当应用中的Activities被销毁,但在内存中常驻时,应用的启动方式就会变为暖启动。相比冷启动,暖启动过程减少了对象初始化、布局加载等工作,启动时间更短。但启动时,系统依然会展示闪屏页,直到第一个Activity的内容呈现为止。

4.3 热启动

相比暖启动,热启动时应用做的工作更少,启动时间更短。热启动产生的场景很多,比如:用户使用返回键退出应用,然后马上又重新启动应用。

4.4 如何优化

先对比一下下三种启动的时间:

可以看到三者的明显的差距。

为了提升用户体验,可以把闪屏页当作一个Fragment嵌套在MainActivity中,这样就可以在进入闪屏时直接预加载主页的View。闪屏页结束后直接remove掉就可以了。

对于启动的优化就是减少耗时操作,总结如下:

  • 主线程中涉及到Shareperference能否在非UI线程执行;
  • Application的创建过程中尽量少的进行耗时操作;
  • 减少布局的层次,并且生命周期回调的方法中尽量减少耗时的操作;

或者使用指令:adb shell am start -S -W [packageName]/[packageName.MainActivity],-S是重新启动应用,也可以查看启动时间:

电量优化

  • 音乐和视频是耗电量最大的。对于电量优化:
  • 需要进行网络请求时,我们需先判断网络当前的状态;
  • 在多网络请求的情况下,最好进行批量处理,尽量避免频繁的间隔网络请求;
  • 在同时有wifi和移动数据的情况下,我们应该直接屏蔽移动数据的网络请求,只有当wifi断开时在调用,因为,wifi请求的耗电量远比移动数据的耗电量低的低;
  • 后台任务要尽可能少的唤醒CPU,比如说,锁屏时,QQ的消息提示行就是唤醒了CPU,但是它的提示只有在你打开锁屏或者进行充电时才会进行提示;

本文主要做对Android中常见的性能优化浅析;如需深入了解性能优化这块,大家可以参考《Android性能优化》这个技术文档,里面记录了大部分的性能优化的方案。

总结

内存优化问题是面试和平时开发中经常可能都会遇到的问题,也比较复杂,本文就常见问题做了简单的整理。Android性能优化有多种方式,在多个方面都有体现:

  • 布局优化:布局优化的思想就是减少布局的层级,使用, ,来优化布局。
  • 绘制优化:绘制优化是指在View的onDraw()方法内避免执行大量的操作。不要在在onDraw()方法内创建大量的局部对象,这不仅会占用过多内存还会频繁gc,降低程序的效率;不要在onDraw()方法内执行耗时操作,绘制时间要保持在16ms以内,防止界面卡顿。
  • 内存泄漏优化:内存泄漏,是指一段内存在该释放的时候却得不到释放,导致App内内存越用越少。一,静态变量导致的内存泄漏,静态变量的生命周期是整个程序的生命周期,所以不要让静态变量引用生命周期比程序生命周期短的对象;二,单例模式导致的内存泄漏,单例模式对象引用局部对象导致内存泄漏;三,属性动画或线程导致的内存泄漏,属性动画和线程不去主动停止,会一直执行下去的,要在适当的时候停止属性动画和线程。
  • 响应速度优化和ANR:响应速度优化是指避免在主线程做耗时操作,因为操作都是在主线程响应的 ;解决ANR问题,如果不能在代码中找到导致ANR的原因,则可以分析traces.txt找到原因,traces.txt文件会记录导致ANR的原因的。
  • ListView和Bitmap优化:ListView采用convertView+ViewHolder缓存策略,来避免在getView()中执行耗时操作,三级缓存,异步加载,不要在滑动的时候去加载图片,开启硬件加速,来加速渲染;Bitmap的优化则是加载压缩后的Bitmap,采样率。
  • 线程优化:不要开启大量线程,采用线程池。

猜你喜欢

转载自blog.csdn.net/m0_71524094/article/details/130048674
今日推荐