如何开启该功能:AndroidStudio中,Tools — Android — Android Devices Monitor
该工具的缺点:使用起来麻烦。
- 看看项目中遇到的问题(MainTabAvtivity)。
1、merge标签的使用
未使用merge,例如:XLTabLayout.java
使用merge,例如:账号信息页的条目UserAccountItem
2、include标签的使用导致的问题
3、避免创建不必要的层级(MainTabActivity)
4、ViewStub的使用
这个标签最大的优点是当你需要时才会加载,使用他并不会影响UI初始化时的性能。
通常情况下我们需要在某个条件下使用某个布局的时候会通过gone或者invisible来隐藏,其实这样的方式虽然隐藏了布局,但是当显示该界面的时候还是将该布局实例化的。使用ViewStub可以避免内存的浪费,加快渲染速度。
其实ViewStub就是一个宽高都为0的一个View,它默认是不可见的,只有通过调用setVisibility函数或者Inflate函数才会将其要装载的目标布局给加载出来,从而达到延迟加载的效果,这个要被加载的布局通过android:layout属性来设置。
当准备inflate ViewStub时,调用inflate()方法即可。还可以设定ViewStub的Visibility为VISIBLE或INVISIBLE,也会触发inflate。注意的是,使用inflate()方法能返回布局文件的根View。
((ViewStub) findViewById(R.id.stub_import)).setVisibility(View.VISIBLE);
// or
View importPanel = ((ViewStub) findViewById(R.id.stub_import)).inflate();
setVisibility的时候会触发了inflate
注意:使用ViewStub加载的布局中不能使用merge标签。
- 看看Space标签(不常用)
space标签可以只在布局文件中占位,不绘制,Space标签有对应的java类Space.java,
通过阅读源码可以发现,它继承至View.java,并且复写了draw方法,
该方法为空,既没有调用父类的draw方法,也没有执行自己的代码,表示该类是没有绘制操作的,
但onMeasure方法正常调用,说明是有宽高的。
主要功能用来设置间距,这个标签不常用,常使用margin或padding。
[](()3、介绍一下查看渲染性能的工具
- GPU呈现模式分析(大致定位问题)
开发者选项 — GPU呈现模式分析 — 选择“在屏幕上显示为条形图”
Android开发者选项——Gpu呈现模式分析,参考:http://www.voidcn.com/blog/gjy211/article/p-6210447.html
自动播放的视频停止的时候会有两条很长的柱线,下个视频播放的时候还会有一条。这里有一个明显的卡顿。
播放器操作(DefaultPlayerView.java - doPlay,player_auto_control_layout.xml):
上图的E total time = 68 是播放器停止播放的时候耗费的时间。
total time = 29 是播放器开始播放的时候耗费的时间。
其中,大部分时间耗费在了 5total time = 18 上面,这个是inflate播放器界面的时候耗费的时间。
是不是所有的inflate都很耗费时间,看一下账号信息页:
- GPU Monitor
- 启用严格模式(不止渲染性能)
应用在主线程上执行长时间操作时会闪烁屏幕。
通过代码进行严格模式(StrictMode)调试,参考:http://www.tuicool.com/articles/ueeM7b6
[](()二、内存问题
[](()1、内存浪费
程序内存的管理是否合理高效对应用的性能有着很大的影响。
推荐阅读Android性能优化典范-第3季,参考:http://hukai.me/android-performance-patterns-season-3/
- ArrayMap(我们项目中没有用到,Android源码中很多使用)
Android为移动操作系统特意编写了一些更加高效的容器,例如ArrayMap、SparseArray。
为了解决HashMap更占内存的弊端,Android提供了内存效率更高的ArrayMap。
1、先来看看HashMap的原理
HashMap的整体结构如下:
存储位置的确定流程:
2、再看来看看ArrayMap是如何优化内存的
它内部使用两个数组进行工作,其中一个数组记录key hash过后的顺序列表,
另外一个数组按key的顺序记录Key-Value值,如下图所示:
当你想获取某个value的时候,ArrayMap会计算输入key转换过后的hash值,然后对hash数组使用二分查找法寻找到对应的index,
然后我们可以通过这个index在另外一个数组中直接访问到需要的键值对。
既然ArrayMap中的内存占用是连续不间断的,那么它是如何处理插入与删除操作的呢?它跟HashMap有什么区别?
二者之间的删除插入效率有什么差异?请看下图所示,演示了Array的特性:
HashMap与ArrayMap之间的内存占用效率对比图如下:
与HashMap相比,ArrayMap在循环遍历的时候更加高效。
什么时候使用ArrayMap呢?
1、对象个数的数量级最好是千以内,没有频繁的插入删除操作
2、数据组织形式包含Map结构
- Autoboxing(避免自动装箱)
Autoboxing的行为还经常发生在类似HashMap这样的容器里面,对HashMap的增删改查操作都会发生了大量的autoboxing的行为。
当key是int类型的时候,HashMap和ArrayMap都有Autoboxing行为。
- SparseArray(项目中用到较多 – 后面再说如何利用工具查找该用SparseArray而没有用到的地方)
为了避免Autoboxing行为Android提供了SparseArray,此容器使用于key为int类型。
SparseBooleanMap,SparseIntMap,SparseLongMap等容器,是key为int,value类型相应为boolean、int、long等。
- Enum(枚举,项目中较多使用,应尽量避免)
Enums often require more than twice as much memory as static constants. You should strictly avoid using enums on Android.
Android官方强烈建议不要在Android程序里面使用到enum。
关于enum的效率,请看下面的讨论。假设我们有这样一份代码,编译之后的dex大小是2556 bytes,
在此基础之上,添加一些如下代码,这些代码使用普通static常量相关作为判断值:
增加上面那段代码之后,编译成dex的大小是2680 bytes,相比起之前的2556 bytes只增加124 bytes。假如换做使用enum,情况如下:
使用enum之后的dex大小是4188 bytes,相比起2556增加了1632 bytes,增长量是使用static int的13倍。
不仅仅如此,使用enum,运行时还会产生额外的内存占用,如下图所示:
推荐一些文章:
- HashMap,ArrayMap,SparseArray源码分析及性能对比,参考:
http://www.jianshu.com/p/7b9a1b386265#
-
Android性能优化–小心自动装箱:http://blog.csdn.net/lgz_ei/article/details/69208784
-
Android性能优化篇:Android中如何避免创建不必要的对象:
http://blog.csdn.net/jia635/article/details/52525243
- HashMap、ArrayMap、SparseArray分析比较:
http://blog.csdn.net/chen_lifeng/article/details/52057427
-
Android性能优化之String篇:http://www.androidchina.net/5940.html
-
SharedPreferences的commit和apply分析:
http://blog.csdn.net/u010198148/article/details/51706483
[](()2、内存泄漏
- 什么是内存泄漏?
一些不用的对象被长期持有,导致内存无法被释放。
- 可能发生内存泄漏的地方有哪些?
1、内部类引用导致Activity的泄漏
在Java中,非静态(匿名)内部类会默认隐性引用外部类对象。而静态内部类不会引用外部类对象。
最典型的场景是Handler导致的Activity泄漏,如果Handler中有延迟的任务或者是等待执行的任务队列过长,
都有可能因为Handler继续执行而导致Activity发生泄漏。
为了解决这个问题,可以在UI退出之前,执行remove Handler消息队列中的消息与runnable对象。
或者是使用Static + WeakReference的方式来达到断开Handler与Activity之间存在引用关系的目的。
举例,MainTabActivity - MainTabHandler:
如何修复?
Android Weak Handler:可以避免内存泄漏的Handler库,参考:http://www.jcodecraeer.com/a/anzhuokaifa/androidkaifa/2014/1123/2047.html
2、Activity Context被传递到其他实例中,这可能导致自身被引用而发生泄漏。
考虑使用Application Context而不是Activity Context。
例如:全局Dialog或者Context被单例持有。
3、静态造成的内存泄漏
4、还有静态变量持有View,例如:
private static View view;
void setStaticView() {
view = findViewById(R.id.sv_button);
}
5、注意监听器的注销(稍后利用工具分析一个例子)
regist就要unregist
6、注意Cursor对象是否及时关闭(项目中也存在,不再列举)
7、WebView的引起的泄漏(暂时没有研究)
- 使用工具分析定位解决内存泄漏
1、Memory monitor
通过MemoryMonitor可以看到,启动手雷进入手雷的内存情况如下(为什么没有做任何操作内存一直在增加?):
通过实例分析一处内存泄漏,操作步骤如下:
启动手雷 - 进入首页 - 切换底部tab到我的tab - 点击登录弹窗弹窗 - 登录成功返回首页
2、MAT(Memory Analyzer Tool)
需要下载MAT独立版,可到这里下载解压使用:192.168.8.188上传2ltsoft
分析刚才的操作内存情况
进入首页并没有进行任何活动操作,为什么会有那么多bitmap对象呢?
3、LeakCanary
LeakCanary 中文使用说明,参考:https://www.liaohuqiu.net/cn/posts/leak-canary-read-me/
LeakCanary:让内存泄露无所遁形,参考:https://www.liaohuqiu.net/cn/posts/leak-canary/
4、TraceView(不做详细分析)
5、GT(应该更适合测试同学测试APP性能)
利用GT,仅凭一部手机,无需连接电脑,您即可对APP进行快速的性能测试(CPU、内存、流量、电量、帧率/流畅度等等)、
开发日志的查看、Crash日志查看、网络数据包的抓取、APP内部参数的调试、真机代码耗时统计等。
GT官网:http://gt.qq.com/index.html
- 内存使用策略优化
1、看看下载一个视频加上浏览一下精选页,然后将应用切到后台,内存使用情况
2、有什么优化内存的策略
A、onLowMemory():Android系统提供了一些回调来通知当前应用的内存使用情况,通常来说,当所有的background应用都被kill掉的时候,forground应用会收到onLowMemory()的回调。在这种情况下,需要尽快释放当前应用的非必须的内存资源,从而确保系统能够继续稳定运行。
B、onTrimMemory(int):Android系统从4.0开始还提供了onTrimMemory()的回调,当系统内存达到某些条件的时候,所有正在运行的应用都会收到这个回调,同时在这个回调里面会传递参数,代表不同的内存使用情况,收到onTrimMemory()回调的时候,需要根据传递的参数类型进行判断,合理的选择释放自身的一些内存占用,一方面可以提高系统的整体运行流畅度,另外也可以避免自己被系统判断为优先需要杀掉的应用。
- 文章推荐:
1、Android内存优化之OOM:http://hukai.me/android-performance-oom/
2、内存泄露从入门到精通三部曲之基础知识篇:http://bugly.qq.com/bbs/forum.php?mod=viewthread&tid=21&extra=page%3D4
3、内存泄露从入门到精通三部曲之排查方法篇:http://bugly.qq.com/bbs/forum.php?mod=viewthread&tid=62&extra=page%3D5
4、内存泄露从入门到精通三部曲之常见原因与用户实践:http://bugly.qq.com/bbs/forum.php?mod=viewthread&tid=125&highlight=%E5%86%85%E5%AD%98%E6%B3%84%E9%9C%B2



