1-1应用UI卡顿常见原因
我们在使用App时会发现有些界面启动卡顿、动画不流畅、列表等滑动时也会卡顿,究其原因,很多都是丢帧导致的;通过上面卡顿原理的简单说明我们从应用开发的角度往回推理可以得出常见卡顿原因,如下:
1、人为在UI线程中做轻微耗时操作,导致UI线程卡顿;
2、布局Layout过于复杂,无法在16ms内完成渲染;
3、同一时间动画执行的次数过多,导致CPU或GPU负载过重;
4、View过度绘制,导致某些像素在同一帧时间内被绘制多次,从而使CPU或GPU负载过重;
5、View频繁的触发measure、layout,导致measure、layout累计耗时过多及整个View频繁的重新渲染;
6、内存频繁触发GC过多(同一帧中频繁创建内存),导致暂时阻塞渲染操作;
7、冗余资源及逻辑等导致加载和执行缓慢;
8、ANR;
1-2 应用UI卡顿分析解决方法
1、布局优化;尽量使用include、merge、ViewStub标签,尽量不存在冗余嵌套及过于复杂布局(譬如10层就会直接异常),尽量使用GONE替换INVISIBLE,使用weight后尽量将width和heigh设置为0dp减少运算,Item存在非常复杂的嵌套时考虑使用自定义Item View来取代,减少measure与layout次数等。
2、列表及Adapter优化;尽量复用getView方法中的相关View,不重复获取实例导致卡顿,列表尽量在滑动过程中不进行UI元素刷新等。
3、背景和图片等内存分配优化;尽量减少不必要的背景设置,图片尽量压缩处理显示,尽量避免频繁内存抖动等问题出现。
4、自定义View等绘图与布局优化;尽量避免在draw、measure、layout中做过于耗时及耗内存操作,尤其是draw方法中,尽量减少draw、measure、layout等执行次数。
5、避免ANR,不要在UI线程中做耗时操作,遵守ANR规避守则,譬如多次数据库操作等。
1-2-1 使用Lint进行资源及冗余UI布局等优化
上面说了,冗余资源及逻辑等也可能会导致加载和执行缓慢,所以我们就来看看Lint这个工具是如何发现优化这些问题的(当然了,Lint实际的功能是非常强大的,我们开发中也是经常使用它来发现一些问题的,这里主要有点针对UI性能的说明了,其他的雷同)。
在Android Studio 1.4版本中使用Lint最简单的办法就是将鼠标放在代码区点击右键->Analyze->Inspect Code–>界面选择你要检测的模块->点击确认开始检测,等待一下后会发现如下结果:
可以看见,Lint检测完后给了我们很多建议的,我们重点看一个关于UI性能的检测结果;上图中高亮的那一行明确说明了存在冗余的UI层级嵌套,所以我们是可以点击跳进去进行优化处理掉的。
2-1应用开发Memory内存性能分析优化
Android系统中框架会定义如下几类进程、在系统内存达到规定的不同level阈值时触发清空不同level的进程类型。
2-1-1Android应用内存泄露
众所周知,在Java中有些对象的生命周期是有限的,当它们完成了特定的逻辑后将会被垃圾回收;但是,如果在对象的生命周期本来该被垃圾回收时这个对象还被别的对象所持有引用,那就会导致内存泄漏;这样的后果就是随着我们的应用被长时间使用,他所占用的内存越来越大。如下就是一个最常见简单的泄露例子(其它的泄露不再一一列举了):
public final classMainActivityextendsActivity{ private DbManager mDbManager;
@Override protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//DbManager是一个单例模式类,这样就持有了MainActivity引用,导致泄露 mDbManager = DbManager.getInstance(this);
}
}
可以看见,上面例子中我们让一个单例模式的对象持有了当前Activity的强引用,那在当前Acvitivy执行完onDestroy()后,这个Activity就无法得到垃圾回收,也就造成了内存泄露。
内存泄露可以引发很多的问题,常见的内存泄露导致问题如下:
1、应用卡顿,响应速度慢(内存占用高时JVM虚拟机会频繁触发GC);
2、应用被从后台进程干为空进程(上面系统内存原理有介绍,也就是超过了阈值);
3、应用莫名的崩溃(上面应用内存原理有介绍,也就是超过了阈值OOM);
造成内存泄露泄露的最核心原理就是一个对象持有了超过自己生命周期以外的对象强引用导致该对象无法被正常垃圾回收;可以发现,应用内存泄露是个相当棘手重要的问题,我们必须重视。
2-1-2Android应用内存泄露察觉手段
知道了内存泄露的概念之后肯定就是想办法来确认自己的项目是否存在内存泄露了,那该如何察觉自己项目是否存在内存泄露呢?如下提供了几种常用的方式:
1、AS的Memory窗口平时用来直观了解自己应用的全局内存情况,大的泄露才能有感知。
2、DDMS-Heap内存监测工具同上,大的泄露才能有感知。
3、dumpsys meminfo命令常用方式,可以很直观的察觉一些泄露,但不全面且常规足够用。
4、leakcanary神器比较强大,可以感知泄露且定位泄露;实质是MAT原理,只是更加自动化了,当现有代码量已经庞大成型,且无法很快察觉掌控全局代码时极力推荐;或者是偶现泄露的情况下极力推荐。
2-1-3 Android应用开发规避内存泄露建议
有了上面的原理及案例处理其实还不够,因为上面这些处理办法是补救的措施,我们正确的做法应该是在开发过程中就养成良好的习惯和敏锐的嗅觉才对,所以下面给出一些应用开发中常见的规避内存泄露建议:
1、Context使用不当造成内存泄露;不要对一个Activity Context保持长生命周期的引用(譬如上面概念部分给出的示例)。尽量在一切可以使用应用ApplicationContext代替Context的地方进行替换。
2、非静态内部类的静态实例容易造成内存泄漏;即一个类中如果你不能够控制它其中内部类的生命周期(譬如Activity中的一些特殊Handler等),则尽量使用静态类和弱引用来处理(譬如ViewRoot的实现)。
3、警惕线程未终止造成的内存泄露;譬如在Activity中关联了一个生命周期超过Activity的Thread,在退出Activity时切记结束线程。一个典型的例子就是HandlerThread的run方法是一个死循环,它不会自己结束,线程的生命周期超过了Activity生命周期,我们必须手动在Activity的销毁方法中中调运thread.getLooper().quit();才不会泄露。
4、对象的注册与反注册没有成对出现造成的内存泄露;譬如注册广播接收器、注册观察者(典型的譬如数据库的监听)等。
5、创建与关闭没有成对出现造成的泄露;譬如Cursor资源必须手动关闭,WebView必须手动销毁,流等对象必须手动关闭等。
6、不要在执行频率很高的方法或者循环中创建对象,可以使用HashTable等创建一组对象容器从容器中取那些对象,而不用每次new与释放。
7、避免代码设计模式的错误造成内存泄露。
2-2Android内存溢出OOM性能分析
上面谈论了Android应用开发的内存泄露,下面谈谈内存溢出(OOM);其实可以认为内存溢出与内存泄露是交集关系,具体如下图:
2-2-1Android应用内存溢出OOM概念
上面我们探讨了Android内存管理和应用开发中的内存泄露问题,可以知道内存泄露一般影响就是导致应用卡顿,但是极端的影响是使应用挂掉。前面也提到过应用的内存分配是有一个阈值的,超过阈值就会出问题,这里我们就来看看这个问题—–内存溢出(OOM–OutOfMemoryError)。
内存溢出的主要导致原因有如下几类:
应用代码存在内存泄露,长时间积累无法释放导致OOM;
应用的某些逻辑操作疯狂的消耗掉大量内存(譬如加载一张不经过处理的超大超高清图片等)导致超过阈值OOM;
可以发现,无论哪种类型,导致内存溢出(OutOfMemoryError)的核心原因就是应用的内存超过阈值了。
2-2-2Android应用规避内存溢出OOM建议
还是那句话,等待OOM发生是为时已晚的事,我们应该将其扼杀于萌芽之中,至于如何在开发中规避OOM,如下给出一些我们应用开发中的常用的策略建议:
1、时刻记得不要加载过大的Bitmap对象;譬如对于类似图片加载我们要通过BitmapFactory.Options设置图片的一些采样比率和复用等,,不过过我们一般都用fresco或Glide开源库进行加载。
2、优化界面交互过程中频繁的内存使用;譬如在列表等操作中只加载可见区域的Bitmap、滑动时不加载、停止滑动后再开始加载。
3、有些地方避免使用强引用,替换为弱引用等操作。
4、避免各种内存泄露的存在导致OOM。
5、对批量加载等操作进行缓存设计,譬如列表图片显示,Adapter的convertView缓存等。
6、尽可能的复用资源;譬如系统本身有很多字符串、颜色、图片、动画、样式以及简单布局等资源可供我们直接使用,我们自己也要尽量复用style等资源达到节约内存。
7、对于有缓存等存在的应用尽量实现onLowMemory()和onTrimMemory()方法。
8、尽量使用线程池替代多线程操作,这样可以节约内存及CPU占用率。
9、尽量管理好自己的Service、Thread等后台的生命周期,不要浪费内存占用。
10、尽可能的不要使用依赖注入,中看不中用。
11、尽量在做一些大内存分配等可疑内存操作时进行try catch操作,避免不必要的应用闪退。
12、尽量的优化自己的代码,减少冗余,进行编译打包等优化对齐处理,避免类加载时浪费内存。
可以发现,上面只是列出了我们开发中常见的导致OOM异常的一些规避原则,还有很多相信还没有列出来,大家可以自行追加参考即可。
3-1Android应用耗电量优化建议
优化电量使用情况我们不仅可以使用系统提供的一些API去处理,还可以在平时编写代码时就养成好的习惯。具体的一些建议如下:
1、在需要网络的应用中,执行某些操作前尽量先进行网络状态判断。
2、在网络应用传输中使用高效率的数据格式和解析方法,譬如JSON等。
3、在传输用户反馈或者下载OTA升级包等不是十分紧急的操作时尽量采用压缩数据进行传输且延迟到设备充电和WIFI状态时进行。
4、在有必要的情况下尽量通过PowerManager.WakeLock和JobScheduler来控制一些逻辑操作达到省电优化。
5、对定位要求不太高的场景尽量使用网络定位,而不是GPS定位。
6、对于定时任务尽量使用AlarmManager,而不是sleep或者Timer进行管理。
7、尽可能的减少网络请求次数和减小网络请求时间间隔。
8、后台任务要尽可能少的唤醒CPU,譬如IM通信的长连接心跳时间间隔、一些应用的后台定时唤醒时间间隔等要设计合理。
9、特殊耗电业务情况可以进行弹窗等友好的交互设计提醒用户该操作会耗用过多电量。