1 布局优化
2 启动优化
3 内存分析工具
4 常见内存泄漏场景
5 优化内存空间
6 稳定性优化
7 线程优化
8 响应时间优化
9 内存优化-(cpu 耗电)
Android系统的 性能优化目的 及一些案例 ?
1 布局优化
关于布局优化的思想很简单,就是尽量减少布局文件的层级。减少测量和绘制时间、提高复用性 这个道理很浅显,布局中的层级少了,就意味着Android绘制时的工作量少了,那么程序的性能自然就提高了。
减少层级:合理使用RelativeLayout和LinerLayout,合理使用Merge。
提高显示速度:使用ViewStub,它是一个看不见的、不占布局位置、占用资源非常小的视图对象。
布局复用:可以通过标签(include)来提高复用。
尽可能少用 wrap_content:wrap_content 会增加布局measure时计算成本,在已知宽高为固定值时,不用wrap_content。
其他:删除控件中无用的属性。其次有选择地使用性能比较低的ViewGroup。
如何进行布局优化?
关于有选择地使用性能比较低的ViewGroup,这就需要我们开发就实际灵活选择了。
例如:如果布局中既可以使用LinearLayout也可以使用RelativeLayout,那么就采用LinearLayout,这是因为RelativeLayout的功能比较复杂,它的布局过程需要花费更多的CPU时间。FrameLayout和LinearLayout一样都是一种简单高效的ViewGroup,因此可以考虑使用它们,但是很多时候单纯通过一个LinearLayout或者FrameLayout无法实现产品效果,需要通过嵌套的方式来完成。这种情况下还是建议采用RelativeLayout,因为ViewGroup的嵌套就相当于增加了布局的层级,同样会降低程序的性能。
(1)避免过度绘制
过度绘制是指在屏幕上的某个像素在同一帧的时间内被绘制了多次。在多层次重叠的UI结构中,如果不可见的UI也在做绘制的操作,就会导致某些像素区域被绘制了多次,从而浪费了多余的CPU以及GPU源。
如何避免过度绘制呢,如下:
布局上的优化:移除XML中非必须的背景,移除Window默认的背景、按需显示占位背景图片。
自定义View优化:使用 canvas.clipRect() 来帮助系统识别那些可见的区域,只有在这个区域内才会被绘制。
绘制优化
绘制优化是指View的onDraw方法要避免执行大量的操作,这主要体现在两个方面:
①onDraw中不要创建新的局部对象。
因为onDraw方法可能会被频繁调用,这样就会在一瞬间产生大量的临时对象,这不仅占用了过多的内存而且还会导致系统更加频繁gc,降低了程序的执行效率。
②onDraw方法中不要做耗时的任务,也不能执行成千上万次的循环操作,尽管每次循环都很轻量级,但是大量的循环仍然十分抢占CPU的时间片,这会造成View的绘制过程不流畅。
2 启动优化
通过对启动速度的监控,发现影响启动速度的问题所在,优化启动逻辑,提高应用的启动速度。启动主要完
成件事:UI布局、绘制和数据准备。因此启动速度优化就是需要优化这三个过程:
UI布局:应用一般都有闪屏页,优化闪屏页的UI布局,可以通过 Profile GPU Rendering 检测丢帧情况。
启动加载逻辑优化:可以采用分布加载、异步加载、延期加载策略来提高应用启动速度。
数据准备:数据初始化分析,加载数据可以考虑用线程初始化等策略。
3 内存分析工具
做内存优化前,需要了解当前应用的内存使用现状,通过现状去分析哪些数据类型有问题,各种类型的分布情况如何,以及在发现问题后如何发现是哪些具体对象导致的,这就需要相关工具来帮助我们。
(1)Memory Monitor
Memory Monitor是一款使用非常简单的图形化工具,可以很好地监控系统或应用的内存使用情况,主要有以下功能:
- 显示可用和已用内存,并且以时间为维度实时反应内存分配和回收情况。
- 快速判断应用程序的运行缓慢是否由于过度的内存回收导致。
- 快速判断应用是否由于内存不足导致程序崩溃。
4 常见内存泄漏场景
- 常见内存泄漏场景:(也属于稳定性)
- 资源性对象未关闭。比如Cursor、File文件等,往往都用了一些缓冲,在不使用时,应该及时关闭它们。
- 注册对象未注销。比如事件注册后未注销,会导致观察者,列表中、维持着对象的引用。
- 类的静态变量持有大数据对象。
- 非静态内部类的静态实例。
- Handler临时性内存泄漏。如果Handler是非静态的,容易导致 Activity 或 Service 不会被回收。
- 容器中的对象没清理造成的内存泄漏。
- WebView。WebView 存在着内存泄漏的问题,在应用中只要使用一次 WebView,内存就不会被释放掉。
****除此之外,内存泄漏可监控,常见的就是用LeakCanary第三方库,这是一个检测内存泄漏的开源库,使用非常简单,可以在发生内存泄漏时告警,并且生成 leak(显示包路径) 分析泄漏位置,同时可以提供 Dump 文件进行分析。
5 优化内存空间
没有内存泄漏,并不意味着内存就不需要优化,在移动设备上,由于物理设备的存储空间有限,Android 系统对每个应用进程也都分配了有限的堆内存,因此使用最小内存对象或者资源可以减小内存开销,同时让GC 能更高效地回收不再需要使用的对象,让应用堆内存保持充足的可用内存,使应用更稳定高效地运行。常见做法如下:
- 对象引用:强引用、软引用、弱引用、虚引用四种引用类型,根据业务需求合理使用不同,选择不同的引用类型。
- 减少不必要的内存开销:注意自动装箱,增加内存复用,比如有效利用系统自带的资源、视图复用、对象池、Bitmap对象的复用。
- 使用最优的数据类型:比如针对数据类容器结构,可以使用ArrayMap数据结构,避免使用枚举类型,使用缓存Lrucache等等。
- 图片内存优化:可以设置位图规格,根据采样因子做压缩,用一些图片缓存方式对图片进行管理等等。
(1)避免创建过多的对象
(2)不要过多的使用枚举类型,枚举占用的内存空间比整型要大
(3)常量请使用static final来修饰
(4)适当的使用软引用和弱引用
(5)采用内存缓存和磁盘缓存
(6)尽量采用静态内部类,这样可以避免潜在的由于内部类而导致的内存泄漏的问题
性能分析工具:
Profile GPU Rendering GPU
在手机开发者模式下,有一个卡顿检测工具叫做:Profile GPU Rendering
一个图形监测工具,能实时反应当前绘制的耗时
6 稳定性优化:
Android 应用的稳定性定义很宽泛,影响稳定性的原因很多,比如内存使用不合理、代码异常场景考虑不周全、代码逻辑不合理等,都会对应用的稳定性造成影响。其中最常见的两个场景是:Crash 和 ANR,这两个错误将会使得程序无法使用,比较常用的解决方式如下:
- 提高代码质量。比如开发期间的代码审核,看些代码设计逻辑,业务合理性等。
- 代码静态扫描工具。常见工具有Android Lint、Check style、PMD等等。
- Crash监控。把一些崩溃的信息,异常信息及时地记录下来,以便后续分析解决。
- Crash上传机制。在Crash后,尽量先保存日志到本地,然后等下一次网络正常时再上传日志信息。
7 线程优化:
线程优化的思想是采用线程池:
1 避免程序中存在大量的Thread。线程池可以重用内部的线程,从而避免了线程的创建和销毁带来的性能开销,
2 线程池可以有效的控制线程的最大并发数,避免了大量线程因互相抢占系统资源而导致阻塞现象的发生。
3 因此在实际开发中应尽量采用线程池,而不是每次都要创建一个Thread对象。
8 响应时间优化
响应速度包括启动速度——点击APP按钮到APP首页完全打开的过程尽可能快;页面响应速度——用户执行点击、滑动等操作后,页面能快速响应。APP不能产生卡顿、更不能出现ANR。
具体优化措施有:
- 耗时操作应放入子线程进行处理,不能阻塞主线程。
- SDK等资源应采用懒加载方式,需要时才进行加载,不需要时可不必加载。
- 线上环境避免打印大量的日志。
- 使用(发 可 特 瑞) BitmapFactory.Option 的inBitmap变量,来复用旧的Bitmap,避免为新Bitmap多次分配内存以及销毁旧Bitmap(如果该Bitmap使用频率高的话)
- 优化view视图渲染时间:
①若view视图比较复杂,可考虑使用ConstraintLayout约束布局,减少视图渲染的层级。
②若view视图比较简单,优化考虑使用LinearLayout水平布局(因为LinearLayout的渲染时间比ConstraintLayout、 RelativeLayout都要短)。
③避免过度渲染,如果有多个view的背景重叠在一起,可以考虑去掉底层被覆盖的view;主题 theme可以设置为NoBackground模式。
④若view视图在需要时才被创建,使用ViewStub控件。
recyclerview列表控件优化:
①item 的view视图优化,同第4点。
②增加recyclerView的item缓存数量,将网络请求的数据缓存,避免二次请求网络。
③在 onBindViewHolder 避免执行耗时操作,因为onBindViewHolder是在主线程执行,onBindViewHolder加耗时操作会影响滑动流畅度。
④如果不需要RecyclerView的默认动画,删除。(如刷新时闪烁的动画效果)
⑤RecyclerView 刷新时尽量使用局部刷新,避免全局刷新。
备注:查看view是否过度渲染可在手机开发者模式开启以下设置:
9 内存优化-(cpu 耗电)
减少内存的使用,主要是避免创建过多对象占用过多内存、避免内存抖动以及避免内存泄漏。
内存抖动:即频繁地创建和销毁内存,在这个过程中,垃圾回收器也会频繁工作,对内存性能造成影响。
内存泄漏:即应该被GC回收的内存,由于还在被其他对象引用,导致无法被回收。内存泄漏是比较严重的问题,过多的内存泄漏会导致内存溢出,产生OOM的系统错误。
(1)造成内存泄漏的原因主要有:
- 单例类引用Context造成内存泄漏。
- 非静态内部类引用外部类造成内存泄漏。
- handler引用activity造成内存泄漏。
- 属性动画没有取消,导致view一直被引用造成内存泄漏。
- 监听器没有取消、回调没有反注册。
内存优化的措施有:
- 使用线程池复用线程,因为线程本身会占用相对比较大的内存,复用就可以省下部分内存。
- 在onDraw方法内避免创建对象。因为onDraw会被频繁调用,导致其内部的对象也会被频繁创建,占用过多内存。
- 尽量使用StringBuilder或StringBuffer拼接字符串,减少String的使用。(因为拼接字符串时,String会创建新的对象,而StringBuilder、StringBuffer是在原字符串基础上拼接)
- 视图资源不可见时进行清除,避免占用内存。如Bitmap执行.Recycle方法进行清除、对图片和(楼 特)lottie资源进行销毁。
针对内存泄漏的问题进行优化:
①单例类应引用Application的Context,因为Application的Context的生命周期是和APP一致的,不会造成单例类引用某个activity的context以致该activity无法被回收的问题。
②将非静态内部类改为静态内部类,这样就不会引用外部类。
③handler:a.handler使用结束时调用remove Callbacks And Messages(null)清除队列;
b.静态内部类+弱引用方式可避免内存泄漏。
④属性动画、监听器使用结束应及时取消,广播或其他一些外部库的回调应该及时反注册。
(2)CPU优化
CPU的作用是计算处理信息、运行程序,因此优化的方向就是减少CPU计算的工作,提升CPU的计算效率。
具体的优化措施有:
- 避免主线程执行耗时任务,耗时任务在子线程异步执行。
- 避免在onDraw方法里执行大量耗时操作。
- 暂时不需要用到的信息进行懒加载、延迟初始化。
(3)耗电量优化
优化的措施有:
- 避免频繁进行网络请求。
- 避免任务被频繁执行,可以等任务形成一定数量时,再一起执行。
- 避免应用频繁唤醒屏幕。(频繁唤醒屏幕会导致系统无法进入休眠,耗电量大)