好文推荐——系列文:
1. 背景:Android App优化, 要怎么做?
2. Android App优化之性能分析工具
3. Android App优化之提升你的App启动速度之理论基础
4. Android App优化之提升你的App启动速度之实例挑战
5. Android App优化之Layout怎么摆
6. Android App优化之ANR详解
7. Android App优化之消除卡顿
8. Android App优化之内存优化
9. Android App优化之持久电量
10. Android App优化之如何高效网络请求
1 简介
1.1 官方工具
一般来说, 学习一门新的技术, 最应该做的就是阅读其官方文档, 那是最权威的。Android本身给我们提供了很多App性能测试和分析工具, 而且大部分都集成到Android Studio或DDMS中, 非常方便使用。
1.1.1 StrictMode
· 说明
顾名思义, "严格模式", 主要用来限制应用做一些不符合性能规范的事情. 一般用来检测主线程中的耗 时操作和阻塞. 开启StrictMode后, 如果线程中做一些诸如读写文件, 网络访问等操作, 将会在Log console输出一些警告, 警告信息包含Stack Trace来显示哪个地方出了问题.
· 文档
o https://developer.android.com/reference/android/os/StrictMode.html
· 作用
o 主要用来做主线程优化分析
1.1.2 Systrace
· 说明
Systrace是一个收集和检测时间信息的工具,它能显示CPU和时间被消耗在哪儿了,每个进程和线程都在其CPU时间片内做了什么事儿.而且会指示哪个地方出了问题,以及给出Fix建议.
其以trace文件(html)的方式记录.可以直接用Chrome浏览器打开查看.界面如下:
Systrace
· 文档
o https://developer.android.com/studio/profile/systrace.html
o https://developer.android.com/studio/profile/systrace-walkthru.html
o https://developer.android.com/studio/profile/systrace-commandline.html?hl=fy
· 作用
o 作用很多,个人主要用来分析UI的绘制时间,结合Hierarchy Viewer来提升UI性能.
o 也可以用来发现耗时操作.
1.1.3 HierarchyViewer
· 说明
Hierarchy Viewer提供了一个可视化的界面来观测布局的层级,让我们可以优化布局层级,删除多余的不必要的View层级,提升布局速度.
Hierarchy Viewer
有必要说明下的是:
上图红框标出的三个点是关键分析数据. 左起依次代表View的Measure, Layout和Draw的性能. 另外颜色表示该View的该项时间指数, 分为:
* 绿色, 表示该View的此项性能比该View Tree中超过50%的View都要快.
* 黄色, 表示该View的此项性能比该View Tree中超过50%的View都要慢.
* 红色, 表示该View的此项性能是View Tree中最慢的.
· 文档
o https://developer.android.com/studio/profile/hierarchy-viewer.html
o https://developer.android.com/studio/profile/hierarchy-viewer-walkthru.html
o https://developer.android.com/studio/profile/hierarchy-viewer-setup.html
o https://developer.android.com/studio/profile/optimize-ui.html#HierarchyViewer
· 作用
o 用来做View层级分析,可以分析出View Tree中的性能阻塞点,以便对症下药,提升布局性能.
Hierarchy Viewer需要Root的机器(产品机没有开启ViewServer)才可以执行。可以使用第三方的开源的ViewServer来协助我们在未Root的机器上使用Hierarchy Viewer分析.
1.1.4 TraceView——方法耗时分析
· 文档
o https://developer.android.com/studio/profile/traceview.html
o https://developer.android.com/studio/profile/traceview-walkthru.html
· 作用
o 分析方法调用栈以及其执行时间,优化方法执行.
1.1.5 MemoryMonitor——内存监控
· 说明
内存使用检测器,可以实时检测当前Application的内存使用和释放等信息,并以图形化界面展示。
Memory Monitor
· 文档
o https://developer.android.com/studio/profile/am-memory.html
o https://developer.android.com/studio/profile/heap-viewer-walkthru.html
o https://developer.android.com/studio/profile/allocation-tracker-walkthru.html
· 作用
o 用来做内存分析,内存泄露排查的不二之选.可以结合heap viewer, allocation tracker来分析.
o 可以导出hprof文件结合第三方的MAT工具分析泄露点.
1.1.6 OtherMonitor
· 说明
Android Studio的Monitor还提供了其他三个Motinor --- CPU, GPU,Network.
· 文档
o https://developer.android.com/studio/profile/am-cpu.html
o https://developer.android.com/studio/profile/am-gpu.html
o https://developer.android.com/studio/profile/am-network.html
· 作用
o 分别用来跟踪监测CPU,GPU和Network的使用极其变化,可以作为网络优化,流量优化和渲染优化等的一个指导. (个人并不常用到~)
1.1.7 其他
Android的开发者模式中也提供了较多的用来监测性能的选项, 可以用下:
Developer options
1.2 第三方工具
以下工具全部开源
1.2.1 Google的Battery Historian
· 说明
Google出品, 通过Android系统的bug report文件来做电量使用分析的工具.
· 文档
o https://github.com/google/battery-historian
· 作用
o 用来做电量使用分析.
1.2.2 网易的Emmagee
· 说明
针对Android App的CPU, 内存, 网络, 电量等多项综合的测试分析.
· 文档
o https://github.com/NetEase/Emmagee
· 作用
o 比官方工具更适合国人使用来做App的整体性能分析.
1.2.3 Square的leakcanary
· 说明
Square出品, 必属精品.类似于App探针的内存泄露监测工具.
· 文档
o https://github.com/square/leakcanary
· 作用
o 集成到App中,用来做内存问题预防最好不过了.
1.2.4 AndroidDevMetrics
· 说明
一个library, 用来检测Activity生命周期执行性能, Dagger2注入性能以及帧率性能的工具.
· 文档
o https://github.com/frogermcs/AndroidDevMetrics
· 作用
o 如果你的应用使用的Dagger2,这个就比较必要了.
2 MemoryMonitor
(转自)Android Studio Memory Monitor
http://blog.csdn.net/flyworkspace/article/details/54019812
2.1 使用方法
2.1.1 内存镜像
生成内存镜像,当从图中发现内存使用较高时,点击小卡车图标,可以主动触发GC。当应用内存使用很高,并且存在很严重的内存泄露时,点击该按钮后的效果并不明显,内存并没有降低或者降低的很少。点击"Dump Java Heap"图标稍等片刻,会生成该应用的内存快照。如图
• Total Count:内存中该类对象个数。
• Heap Count:堆内存中该类对象个数。
• Sizeof:每个对象的大小(如果为0,则大小不固定)。
• Shallow size:对象本身占有内存大小。
• Retained Size:释放该对象后,节省的内存大小。
其中,Retained Size之所以比Shallowsize大的原因是,该对象释放后,会引起其他对象的回收。 例如,某个对象被回收后: 该对象引用的其他对象也会被回收, 该对象A被另一对象B强引用后,之前对象B因为强引用该对象A而没有被回收,现在该对象A被回收后,若对象B强引用的其他对象都已被回收,则对象B也会被回收。
优化内存:
点击Class Name中的类名,查看其所有实例(Instance),分析实例中参数所占用的内存。根据Retaine Size排序,查找Instance中Depth较小的实例或者参数,在代码中找到相应的位置,查看内存占用是否合理。
判断内存泄露的步骤:
从Total Count入手。该类的对象数量不对。
• 很多对象只可能存在1个,若存在多个,则可能存在泄露。例如MainActivity的数量为2。又或者由于单例的使用不规范而导致创建多个“单例”对象。
• 某个对象已经不再使用,而其还在内存中显示。例如LoginActivity已经退出了,其数量为1。
Memory Monitor只提供了内存信息,如需详细信息,可以通过android studio的Captures(View—–Tool window—–Captures)栏,右键点击快照文件,Export to standard .hprof将堆快照(Heap Snapshot)转换成通用的hprof文件,之后可以通过其他内存分析工具打开,例如MAT。
2.1.2 跟踪内存分配(Allocation tracker)
点击“Start Allocation Tracking”开始监听内存的分配情况,再次点击,监听完成,生成报告。该报告显示这段时间内,内存的分配情况。
2.1.3 小结
2.1是从内存的静态信息中分析,是某一个点的内存使用情况。2.2是跟踪某一段时间内内存的分配情况,是个过程跟踪。分析内存可以相结合,例如,再进行某个操作前,执行2.1导出静态内存信息,在开启2.2开始跟踪内存的分配。当执行完操作的时候,关闭内存分配的跟踪,再次执行2.1的,导出操作某个流程后的静态信息。将2.1的两个静态表结合2.2的内存分配动态过程一起分析。
2.2 分析样例
2.2.1 占用内存分析样例
打开某应用后,切换几个页面,内存飞速上涨。这只是一个极端例子,有很多app,随着用的时间越长,内存也是一直在升高。
根据2.1的方法,生成内存镜像。
查找可疑的对象,这个过程是逐个分析的过程,例如byte[]其实是其他对象的某个参数,很多本质上都是byte[],例如Bitmap中mBuffer也是byte[],当内存中有很多Bitmap的时候,byte[]也会很高。所以byte[]不是我们重点关注对象,如果真的是因为Bitmap(或者其他类)多而造成的byte[]高,那么下面肯定会有该类也会占用很高内存。
继续分析下一个HashMap$HashMapEntry。点击它后,在Instance栏中,看到其实例很多,先从占用大的实例入手。
例如99=这个,点击它后ReferenceTree显示如下:
得知这个HashMap是ReuseThumbnail…类中ReuseThumbnailManager的REUSE_BITMAPS。在代码中查看其大小是否合理。本例中REUSE_BITMAPS参数是static参数,其类型是HashMap,查看逻辑,看其是否正常。
再看其他参数,逐个分析其内存占用是否正常。
分析内存是个逐步的过程,一个问题解决后,再次循环这些步骤。有时候虽然列表中显示很多对象占用内存很高,有可能是同一个参数导致的,所以一个问题解决后,有可能有一系列参数占用高的情况会消失。就比如如果高内存bitmap的优化后,byte[]也很降低很多,其他参数也有可能会降低很多。
2.2.2 跟踪内存分配分析样例
3.1是从静态内存信息中分析内存的使用,现在按照2.2从动态过程中跟踪内存的分配。
生成报告如下:
查看Size最大的一个Thread.
可以看到调用过程,从NewDisplayRunnale(执行了636次)调用了BitmapDecoder的decode方法(执行了135次),从代码中分析过程是否合理。
2.2.3 内存泄露分析样例
对于android的内存泄露,一般监测Activity的泄露居多,例如LeakCanary默认也是监测Activity是否泄露。
现写一个demo,故意造成内存泄露作为分析样例。
public class MyActivity extends Activity{
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
new TestThread().start();
}
class TestThread extends Thread{
@Override
public voidrun() {
super.run();
try{
Thread.sleep(10 * 60 * 1000);
}catch(InterruptedException e) {
e.printStackTrace();
}
}
}
}
当进入MyActivity点击回退,重复多次。按照2.1方法获取内存镜像。
看到MyActivity的实例数量为17个。在右边的Analyzer Tasks栏中:
一般引起Activity泄露是由于其Context被强引用导致的。
MyActivity的context参数被TestThread引用了。所以在activity的销毁的时候,由于TestThread还引用着MyActivity,所以阻止了MyActivity被释放,因而导致内存泄露。
2.3 性能数据采集
3 DDMS
Android Studio中怎么使用DDMS工具?
http://www.cnblogs.com/gaobig/p/5029381.html
Android Studio开发工具中,打开DDMS,具体的方式如图:
打开之后的窗口如图:
查看进程中的线程:
查看内存信息:
3.1 Traceview
Android学习之Android studio TraceView和lint工具的使用详解
http://blog.csdn.net/qq_16131393/article/details/51172488
3.1.1 使用方法
打开AndroidDevice Monitor,这个大家都知道:
1. 选择你要调试的进程。
2. 点击start mothod profiling,待图标变黑。
3. 选择sample base profiling
这里需要解释一下:
Trace base profiling
整体监听,项目中所有方法都会监听,资源消耗比较大。
sample base profiling
抽样监听,以指定的频率进行抽样调查,一般不要超过5s,需要较长时间获取准确的样本数据。
再次点击start mothod profiling,就会生成检测样本。
效果如下:
上部分为时间轴,x轴表示时间,黑色区域可放大,每个区域代表每个方法的执行时间。y轴表示每一个独立线程。
下部分Name为你所选择的颜色区块所代表的性能分析。不同的颜色,代表不同的方法,颜色长度代表占用时间。
属性介绍:
Incl cpu time:某方法占用cpu时间(父 子)
Excl cpu time:某方法本身占用cpu时间(父)
Incl Real time:某方法真正执行时间(父 子)
Excl Real time:某方法自身执行时间(父)
当然还有相应所占百分比,不过多介绍。
还有Calls RecurCall调用次数 递归调用次数
还有比较重要的:
cpu time/call:平均每次调用占用cpu时间。
real time/call:平均每次调用所执行的时间。
我觉得这个参数很具有参考性。
打开每个方法,会显示Paents和children(即父方法和子方法),以及分别所占用时间。
· 说明
一个图形化的工具,用来展示和分析方法的执行时间.
TraceView
3.1.2 数据采集
3.2 Heap Viewer
3.2.1 HeapViewer面板
按上图的标记顺序按下,我们就能看到内存的具体数据,右边面板中数值会在每次GC时发生改变,包括App自动触发或者你来手动触发。
3.2.2 详情
下面是每一个对象都有的列名含义:
当我们点击某一行时,可以看到如下的柱状图:
横坐标是对象的内存大小,这些值随着不同对象是不同的,纵坐标是在某个内存大小上的对象的数量。
3.2.3 HeapViewer的使用
我们说HeapViewer适合发现内存泄漏的问题,那你知道何为内存泄漏么?
内存泄漏
英文名:Memory Leaks
标准解释:无用的单纯,但是还是没GC ROOT引用的内存
通俗解释:该死不死的内存
检测
那么如何检测呢?Heap Viewer中的数值会自动在每次发生GC时会自动更新,那么我们是等着他自己GC么?小弟不才,刚开始我就是这么一直等啊等,由于GC的时机是系统把握的,所以很不好把握,既然我们是来看内存泄漏,那么我们在需要检测内存泄漏的用例执行过后,手动GC下,然后观察data object一栏的total size(也可以观察HeapSize/Allocated内存的情况),看看内存是不是会回到一个稳定值,多次操作后,只要内存是稳定在某个值,那么说明没有内存溢出的,如果发现内存在每次GC后,都在增长,不管是慢增长还是快速增长,都说明有内存泄漏的可能性。
实例
先来看3个图:
1.刚打开首页,手动GC一下:
2.首页到详情页10遍,最后回到首页,手动GC一下,直到数值不再变化:
3.首页到详情页10遍,最后回到首页,手动GC一下:
从data object一栏看到该类型的数值会在不断增长,可能发生了内存泄漏,而我们也可以从上面三个图的标红部分来看,Allocated分别增加了2.418M和1.084M,而且你继续这么操作下去,内存依然是增长的趋势。
3.2.4 数据采集
4 Emmagee
4.1 使用说明
1、Emmagee是网易杭州研究院QA团队开发的一个简单易上手的Android性能监测小工具,主要用于监控单个App的CPU,内存,流量,启动耗时,电量,电流等性能状态的变化,且用户可自定义配置监控的频率以及性能的实时显示,并最终生成一份性能统计文件。
2、操作完成后,从系统任务列表中选择Emmagee,并停止测试,在”storagesdcard0”下找到命名类似”Emmagee_TestResult_20140403210532.csv”的文件,打卡即为监控的得到的数据。
3、将csv数据拷贝到excel中生成图表,即可清晰看到整个操作过程中cpu、内存等关键数据的变化。
Android性能测试小工具Emmagee - 敌测漏师
http://blog.csdn.net/anlegor/article/details/22895993
APP性能测试工具Emmagee的使用总结
http://blog.csdn.net/chenrushui/article/details/51589995
Android性能测试工具Emmagee
http://www.cnblogs.com/jytian/p/6516170.html
4.2 性能数据采集
5 leakcanary
· 说明
Square出品, 必属精品,类似于App探针的内存泄露监测工具.
· 文档
o https://github.com/square/leakcanary
· 作用
o 集成到App中,用来做内存问题预防最好不过了.
5.1 使用说明
LeakCanary使用指南(1)
http://blog.csdn.net/hmh0512/article/details/57053265?utm_source=tuicool&utm_medium=referral
快速集成
第一步:在build.gradle中添加如下依赖:
dependencies {
debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5'
releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5'
testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5'
}
第二步:在自己的Application(假设名为ExampleApplication)中添加如下代码:
public class ExampleApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
if (LeakCanary.isInAnalyzerProcess(this)) {
// This process is dedicated to LeakCanary for heap analysis.
// You should not init your app in this process.
return;
}
LeakCanary.install(this);
// Normal app init code...
}
}
到这里其实可以检测到Activity的内存泄露了,原理后面再说。以Debug模式运行你的App,你可以看到,你App的图标后面跟着一个Leaks图标,如下图;而如果你以Release模式运行,则没有这个图标。
测试使用
假装你是测试人员,你开始各种点击App,进行测试。然后你有幸看到这样一个弹框,如下图。
你很好奇,然后点击了弹框中间那个图标,于是手机屏幕的左上角出现了你App的图标,再下拉点击那个图标,或者从桌面上LeakCanary图标(跟在你App的图标屁股后面那个)点进去,你看到下图。点击 号可以展开,点击-号收起。
内存泄露往往发生在,生命周期较长的对象,直接或间接持有了生命周期较短的对象的强引用,导致了生命周期较短的对象不能及时释放。
上图已经够傻瓜式了,第一行表示生命周期较长的那个对象,图中是AliPayModel这个类;第二行表示生命周期长的那个持有了一个什么样的引用,图中是mActivity;第三行表示生命周期较短的那个对象,图中是SelectPayTypeActivity。
回去查看源码,发现AliPayModel是个单例,在SelectPayTypeActivity中以AliPayModel.getInstance(this).XXX()的方式调用单例中的XXX()方法。于是AliPayModel通过mActivity持有了SelectPayTypeActivity.this的引用。SelectPayTypeActivity本来应该在用户退出这个页面和进入其他Activity(尤其是其他Activity层级较深时)时释放掉,但是单例的生命周期贯穿整个App,AliPayModel一直引用着SelectPayTypeActivity,导致SelectPayTypeActivity不能及时释放,引发内存泄露。
public class AliPayModel extends BasePayModel {
private Activity mActivity;
private AliPayModel() {}
private static AliPayModel instance = new AliPayModel();
public static AliPayModel getInstance(Activity tag) {
instance.mActivity = tag;
return instance;
}
}
找到了原因,解决方法也呼之欲出。要么AliPayModel这个业务类不要定义成单例,要么mActivity由强引用改成软引用或者弱引用。Java的强、软、弱、虚四种引用的区别不在本文的讨论范围。
发现开源组件中的内存泄露
用上述方法,可以检测出各种各样的内存泄露,包括:WebView导致的内存泄露、资源未关闭导致的内存泄露、非静态匿名内部类导致的内存泄露、Handler导致的内存泄露等等。
请看下图,每次选择图片、上传头像时都会引发0.96M的内存泄露!
再按图索骥,发现罪魁祸首是将一个Activity定义为static。表示不是很能理解这种神代码。最让人心中万马奔腾的是,它竟然有2600多个star!在这个项目的Issues中很多人反映内存占用大、容易OOM、卡顿等,但是没有人从技术层面去查找和分析原因,更遑论去阅读源码,都是直接拿来就用!
6 参考链接
Android App优化之性能分析工具
http://www.jianshu.com/p/da2a4bfcba68
Android开发调试必备-使用DDMS
http://blog.csdn.net/stzy00/article/details/46554529
Android内存分析工具(二):DDMS
http://blog.csdn.net/berber78/article/details/47784007
Android性能专项测试之Heap Viewer工具
http://blog.csdn.net/itfootball/article/details/48734553
正确使用Android性能分析工具——TraceView
http://android.jobbole.com/78995/
Android性能专项测试之MemoryMonitor工具
http://blog.csdn.net/itfootball/article/details/48712595
Android性能优化第(二)篇---Memory Monitor检测内存泄露
http://www.cnblogs.com/ldq2016/p/6628311.html
Android内存与性能
http://blog.kamidox.com/android-memory-guide.html
Android系统性能调优工具介绍
http://blog.csdn.net/innost/article/details/9008691/
Android开发——Android多进程以及使用场景介绍
http://blog.csdn.net/seu_calvin/article/details/53932171
理解Android进程创建流程
http://gityuan.com/2016/03/26/app-process-create/
LeakCanary使用指南(1)
http://blog.csdn.net/hmh0512/article/details/57053265?utm_source=tuicool&utm_medium=referral
利用LeakCanary来检查Android内存泄漏
http://www.jianshu.com/p/0049e9b344b0
LeakCanary中文使用说明
https://www.liaohuqiu.net/cn/posts/leak-canary-read-me/