资讯详情

Android 应用和系统优化V1.2

一年多来,我为公司同事撰写了一个简单的软件优化教程。现在不应该过时。在过去的一年里,在国家脱虚向实体的倡导下,高科技企业如雨后春笋般涌现,对软件优化重构的需求也越来越大。早期的 android 开发者曾经羡慕C文开发者,C的调试工具是如此之多和丰富,内存,堆栈,CPU,GPU,断点,现在android还有相当多的系统分析和优化工具,借助系统自带的开发者工具,android日益成熟和完善的系统。无论是功耗(耗电)还是CPU,还是GPU,还是内存,还是UI显示有相应的分析工具进行定量分析,我的软件似乎不会更快,似乎节能,但肉眼看不到结果。

Android 应用和系统优化V1.2作者:贾治国目录1应用UI卡顿的常见原因2.常用工具3.工具用法Android应用开发性能优化完全分析 给 App 提速:Android 性能优化总结Android介绍系统性能调优工具正确使用Android性能分析工具——TraceView为什么微博?app在iPhone比Android上流畅被忽略的UI检视利器:Hierarchy Viewer 使用Systrace分析UI性能正确使用Android性能分析工具——TraceView内存分析工具 MAT 的使用 使用Android studio分析内存泄漏Android 编程下的 TraceView 简介及其案例实战 Google 发布 Android 性能优化模型Android MemInfo 各项意义(转) Android 内存分析工具 - LogCat GCAndroid使用procrank和dumpsysmeminfo分析内存占用情况LeakCanary——直白的展现Android内存泄漏 LeakCanary 中文说明 如何使用eclipse单独调试android系统的appAndroid上oprofile使用说明谁动了我的cpu——oprofile使用札记Android(上)性能优化案例研究 Android(下)性能优化案例研究 Android优化代码性能 应用UI卡顿的常见原因我们在使用App当你发现一些界面启动、动画不流畅、列表和其他滑动时,它们也会被卡住。其中许多原因是由帧丢失引起的;通过上述卡原理的简要解释,我们可以从应用程序开发的角度得出常见的卡原因,如下:人为在UI在线程中做轻微耗时的操作,导致UI线程卡顿;(traceview)布局Layout太复杂了,不能在16岁ms渲染在内部完成;(HierarchyViewer)同时,动画执行次数过多,导致动画执行次数过多CPU或GPU负载过重;(traceview)View过度绘制导致某些像素在同一帧时间内多次绘制,从而导致CPU或GPU负荷过重;使用GPU过度绘制分析UI性能(开发者模式)View频繁的触发measure、layout,导致measure、layout累积时间过多,整体View频繁的再渲染;(HierarchyViewer)频繁触发内存GC过多(内存在同一帧中频繁创建)会暂时阻塞渲染操作;(Memory监测)冗余资源和逻辑导致加载和执行缓慢; (HierarchyViewer)臭名昭着的ANR; (adb logcat | grep AndroidRuntime)影响动画、时间曲线、帧率、触摸屏响应的因素(systrace)大量的数学操作和矩阵变化阻碍了动画线程,优化了数学方法,减少了浮点(代码分析)大量滤镜特效处理图像导致动画堵塞(代码分析)常用的分析工具使用GPU过度绘制分析UI性能(开发者模式,布局优化)加速硬件,打开 “Show hardware layers updates” 选项使用GPU呈现模式图和FPS考核UI性能(开发者模式优化超过16m秒)一些android自带命令行工具procrank,meminfo分析系统和应用内存使用Lint资源和冗余UI布局等优化(android studio OR eclipse工具对代码优化)android studio device monitor或者DDMS使用Memory监测及GC打印与Allocation Tracker进行UI卡顿分析(android studio device monitor OR eclipse ddms分析)使用HierarchyViewer(层次观察器)分析UI性能(命令行,android studio device monitor OR eclipse ddms启动)使用Traceview和dmtracedump分析优化系统函数耗时(android studio device monitor OR eclipse ddms )使用Systrace分析优化,动画帧超时分析,提供性能警告信息分析(android studio device monitor OR eclipse ddms)使用traces.txt文件进行ANR分析优化(分析阻塞)LeakCanary(需要注入概率内存泄漏的搜索APP测试)Oprofile,linux在下一个使用中,可以分析和优化系统底部的文件和函数MAT(查找内存泄漏)Adb logcat | findstr GC 或者 adb logcat grep GC常用工具及用法(标题前网站为原地址)http://blog.csdn.net/yanbober/article/details/48394201Android应用开发性能优化完全分析 1 背景其实有点不想写这篇文章,但又想写,有些矛盾。不想写的原因是随便上网一搜一堆关于性能的建议,感觉大家你一总结、我一总结的都说到了很多优化 注意事项,但是看完这些文章,大部分都有一个问题,就是只给出什么不能用,什么应该用等。,但是很少有更系统的真实性能案例分析,大部分都是嘴上喊。 喊或死记住规则(当然,我自己听起来有点刺耳,对不起,其实网上还是有很多关于性能优化的优质博文的,比如Google官方已经推了 对于优化主题,我只是在这里总结我的感受。如果我冒犯了,欢迎拍砖头,我愿意被打,因为我以前工作的一半时间负责性能优化)。当然,这篇文章不会编辑这样一次,因为技术正在发展,工具正在强大着Android Studio 1.4版本已经推出),我自己的经验也在增加,所以本文自然不会涵盖所有的性能优化和分析;解决方案是文章将长期维护和更新,并欢迎您在评论区进行性能 讨论优化想法。Android事实上,应用程序的性能问题可以分为几个大模块,并且都有相对较好的优化调试技能。接下来,我们将根据项目常规开发的大类型进行一些分析和解释。PS:我以前在一家初创医疗互联网公司工作过,更不用说性能优化了。在建立了一个新项目后,老板要求在一个月内看到在线成品。在这种压力下,谈论性能优化纯粹是胡说八道,所以我选择在不到三个月的时间内退出。后来,当我询问这一现象时,我发现许多初创公司都非常严重,他们都想快速成功,但忽略了经验。PPPS:这篇文章只是为了达到吸引玉石的作用,很多事情都值得深入研究,加上性能优化是一个需要综合考虑的任务,并不是说这篇文章可以做性能分析,需要一切才能有效地定位问题的原因。2 应用UI性能问题分析UI可以说是一张应用的脸,所以在开发阶段,我们的互动、视觉和动画工程师都拼命想让它自然、大方、美丽,但现实总是不令人满意,动画和交流 互相总是觉得开发的应用程序感觉不自然,没有达到自然流畅的细节;在这种情况下,更不用说发布给终端用户了,如果用户能感觉到,就少了 会影响心情,多卸载应用;所以一个应用UI开发人员必须注意显示性能问题。2-1 应用UI卡顿原理人脑和眼睛对画面的连贯性感知实际上有一个界限。例如,当我们看电影时,我们会觉得画面自然连贯(帧率为24fps),当然,使用手机也需要感知屏幕操作的连贯性(特别是过度动画),因此Android简单地将这种流畅的帧率规定为60fps。以上背景,我们开发App帧率性能的目标是保持在60fps,也就是说,我们在做App心中要有以下标准:换算关系:60帧/秒-16ms/帧;标准:尽量保证每次16ms所有的内部处理CPU与GPU计算、绘制、渲染等操作,否则会造成帧丢失堵塞。123从上面可以看出,所谓的卡顿实际上是可以量化的,每次都能成功渲染是一个非常重要的问题,16ms完成一次操作是否直接决定了性能问题。当然,针对Android我们还需要了解系统设计的另一个常识;虚拟机正在执行中GC垃圾回收操作中的所有线程(包括所有线程)UI当线程)需要暂停时GC垃圾 所有线程只有在回收完成后才能继续执行(详细介绍以下细节)。也就是说当在16ms如果内部渲染和其他操作碰巧遇到很多GC操作会导致渲染 间隙明显不足,导致帧丢失卡顿。有了以上两个简单的理论基础,我们下面就讨论一些UI卡顿的原因分析及解决方案。2-2 应用UI卡顿的常见原因我们在使用App当你发现一些界面启动、动画不流畅、列表和其他滑动时,它们也会被卡住。其中许多原因是由帧丢失引起的;通过上述卡原理的简要解释,我们可以从应用程序开发的角度得出常见的卡原因,如下:人为在UI在线程中做轻微耗时的操作,导致UI线程卡顿;布局Layout太复杂了,不能在16岁ms渲染在内部完成;同时,动画执行次数过多,导致动画执行次数过多CPU或GPU负载过重;View过度绘制导致某些像素在同一帧时间内多次绘制,从而导致CPU或GPU负载过重;View频繁的触发measure、layout,导致measure、layout累计耗时过多及整个View频繁的再渲染;频繁触发内存GC过多(内存在同一帧中频繁创建)会暂时阻塞渲染操作;冗余资源和逻辑导致加载和执行缓慢; 臭名昭着的ANR; 可以看出,上述原因在我们的日常开发中非常常见。有些人可能会觉得他们的应用程序相当好OK事实上,这是因为你没有进行一些瞬时测试和压力测试,一旦你在这种环境下运行App你会发现很多性能问题。2-3 应用UI卡顿分析解决方案分析UI我们通常使用工具,通过工具可以直观地分析问题的原因,从而反向寻求优化解决方案,具体说明各种强大的工具。2-3-1 使用HierachyViewer分析UI性能我们可以通过SDK提供的工具HierarchyViewer来进行UI布局复杂程度及冗余等分析,如下:xxx@ThinkPad:~$ hierarchyviewer   //通过命令启动HierarchyViewer1选中一个Window界面item,然后点击右上方Hierarchy window或者Pixel Perfect window(这里不介绍,主要用来检查像素属性的)即可操作。先看下Hierarchy window,如下: 一个Activity的View树,通过这个树可以分析出View嵌套的冗余层级,左下角可以输入View的id直接自动跳转到中间显示;Save as PNG用来把左侧树保存为一张图片;Capture Layers用来保存psd的PhotoShop分层素材;右侧剧中显示选中View的当前属性状态;右下角显示当前View在Activity中的位置 等;左下角三个进行切换;Load View Hierarchy用来手动刷新变化(不会自动刷新的)。当我们选择一个View后会如下图所示: 类似上图可以很方便的查看到当前View的许多信息;上图最底那三个彩色原点代表了当前View的性能指标,从左到右依次代表测量、布局、绘制的渲 染时间,红色和黄色的点代表速度渲染较慢的View(当然了,有些时候较慢不代表有问题,譬如ViewGroup子节点越多、结构越复杂,性能就越差)。当然了,在自定义View的性能调试时,HierarchyViewer上面的invalidate Layout和requestLayout按钮的功能更加强大,它可以帮助我们debug自定义View执行invalidate()和 requestLayout()过程,我们只需要在代码的相关地方打上断点就行了,接下来通过它观察绘制即可。可以发现,有了HierarchyViewer调试工具,我们的UI性能分析变得十分容易,这个工具也是我们开发中调试UI的利器,在平时写代码时会时常伴随我们左右。2-3-2 使用GPU过度绘制分析UI性能我们对于UI性能的优化还可以通过开发者选项中的GPU过度绘制工具来进行分析。在设置->开发者选项->调试GPU过度绘制(不同设备可能位置或者叫法不同)中打开调试后可以看见如下图(对settings当前界面过度绘制进行分析): 可以发现,开启后在我们想要调试的应用界面中可以看到各种颜色的区域,具体含义如下: 颜色    含义       无色    WebView等的渲染区域       蓝色    1x过度绘制       绿色    2x过度绘制       淡红色    3x过度绘制       红色    4x(+)过度绘制     由于过度绘制指在屏幕的一个像素上绘制多次(譬如一个设置了背景色的TextView就会被绘制两次,一次背景一次文本;这里需要强调的是 Activity设置的Theme主题的背景不被算在过度绘制层级中),所以最理想的就是绘制一次,也就是蓝色(当然这在很多绚丽的界面是不现实的,所以 大家有个度即可,我们的开发性能优化标准要求最极端界面下红色区域不能长期持续超过屏幕三分之一,可见还是比较宽松的规定),因此我们需要依据此颜色分布 进行代码优化,譬如优化布局层级、减少没必要的背景、暂时不显示的View设置为GONE而不是INVISIBLE、自定义View的onDraw方法设 置canvas.clipRect()指定绘制区域或通过canvas.quickreject()减少绘制区域等。2-3-3 使用GPU呈现模式图及FPS考核UI性能Android界面流畅度除过视觉感知以外是可以考核的(测试妹子专用),常见的方法就是通过GPU呈现模式图或者实时FPS显示进行考核,这里我 们主要针对GPU呈现模式图进行下说明,因为FPS考核测试方法有很多(譬如自己写代码实现、第三方App测试、固件支持等),所以不做统一说明。通过开发者选项中GPU呈现模式图工具来进行流畅度考量的流程是(注意:如果是在开启应用后才开启此功能,记得先把应用结束后重新启动)在设置 ->开发者选项->GPU呈现模式(不同设备可能位置或者叫法不同)中打开调试后可以看见如下图(对settings当前界面上下滑动列表后 的图表): 当然,也可以在执行完UI滑动操作后在命令行输入如下命令查看命令行打印的GPU渲染数据(分析依据:Draw + Process + Execute = 完整的显示一帧时间 < 16ms):adb shell dumpsys gfxinfo [应用包名]1打开上图可视化工具后,我们可以在手机画面上看到丰富的GPU绘制图形信息,分别展示了StatusBar、NavgationBar、 Activity区域等的GPU渲染时间信息,随着界面的刷新,界面上会以实时柱状图来显示每帧的渲染时间,柱状图越高表示渲染时间越长,每个柱状图偏上 都有一根代表16ms基准的绿色横线,每一条竖着的柱状线都包含三部分(蓝色代表测量绘制Display List的时间,红色代表OpenGL渲染Display List所需要的时间,黄色代表CPU等待GPU处理的时间),只要我们每一帧的总时间低于基准线就不会发生UI卡顿问题(个别超出基准线其实也不算啥问 题的)。可以发现,这个工具是有局限性的,他虽然能够看出来有帧耗时超过基准线导致了丢帧卡顿,但却分析不到造成丢帧的具体原因。所以说为了配合解决分析UI丢帧卡顿问题我们还需要借助traceview和systrace来进行原因追踪,下面我们会介绍这两种工具的。2-3-4 使用Lint进行资源及冗余UI布局等优化上面说了,冗余资源及逻辑等也可能会导致加载和执行缓慢,所以我们就来看看Lint这个工具是如何发现优化这些问题的(当然了,Lint实际的功能是非常强大的,我们开发中也是经常使用它来发现一些问题的,这里主要有点针对UI性能的说明了,其他的雷同)。在Android Studio 1.4版本中使用Lint最简单的办法就是将鼠标放在代码区点击右键->Analyze->Inspect Code–>界面选择你要检测的模块->点击确认开始检测,等待一下后会发现如下结果: 可以看见,Lint检测完后给了我们很多建议的,我们重点看一个关于UI性能的检测结果;上图中高亮的那一行明确说明了存在冗余的UI层级嵌套,所以我们是可以点击跳进去进行优化处理掉的。当然了,Lint还有很多功能,大家可以自行探索发挥,这里只是达到抛砖引玉的作用。2-3-5 使用Memory监测及GC打印与Allocation Tracker进行UI卡顿分析关于Android的内存管理机制下面的一节会详细介绍,这里我们主要针对GC导致的UI卡顿问题进行详细说明。Android系统会依据内存中不同的内存数据类型分别执行不同的GC操作,常见应用开发中导致GC频繁执行的原因主要可能是因为短时间内有大量频 繁的对象创建与释放操作,也就是俗称的内存抖动现象,或者短时间内已经存在大量内存暂用介于阈值边缘,接着每当有新对象创建时都会导致超越阈值触发GC操 作。如下是我工作中一个项目的一次经历(我将代码回退特意抓取的),出现这个问题的场景是一次压力测试导致整个系统卡顿,瞬间杀掉应用就OK了,究其原 因最终查到是一个API的调运位置写错了方式,导致一直被狂调,当普通使用时不会有问题,压力测试必现卡顿。具体内存参考图如下:  与此抖动图对应的LogCat抓取如下://截取其中比较密集一段LogCat,与上图Memory检测到的抖动图对应,其中xxx为应用包名......10-06 00:59:45.619 xxx I/art: Explicit concurrent mark sweep GC freed 72515(3MB) AllocSpace objects, 65(2028KB) LOS objects, 80% free, 17MB/89MB, paused 3.505ms total 60.958ms10-06 00:59:45.749 xxx I/art: Explicit concurrent mark sweep GC freed 5396(193KB) AllocSpace objects, 0(0B) LOS objects, 75% free, 23MB/95MB, paused 2.079ms total 100.522ms......10-06 00:59:48.059 xxx I/art: Explicit concurrent mark sweep GC freed 4693(172KB) AllocSpace objects, 0(0B) LOS objects, 75% free, 23MB/95MB, paused 2.227ms total 101.692ms......1234567我们知道,类似上面logcat打印一样,触发垃圾回收的主要原因有以下几种:GC_MALLOC——内存分配失败时触发;GC_CONCURRENT——当分配的对象大小超过一个限定值(不同系统)时触发;GC_EXPLICIT——对垃圾收集的显式调用(System.gc()) ;GC_EXTERNAL_ALLOC——外部内存分配失败时触发;可以看见,这种不停的大面积打印GC导致所有线程暂停的操作必定会导致UI视觉的卡顿,所以我们要避免此类问题的出现,具体的常见优化方式如下:检查代码,尽量避免有些频繁触发的逻辑方法中存在大量对象分配;尽量避免在多次for循环中频繁分配对象;避免在自定义View的onDraw()方法中执行复杂的操作及创建对象(譬如Paint的实例化操作不要写在onDraw()方法中等);对于并发下载等类似逻辑的实现尽量避免多次创建线程对象,而是交给线程池处理。当然了,有了上面说明GC导致的性能后我们就该定位分析问题了,可以通过运行DDMS->Allocation Tracker标签打开一个新窗口,然后点击Start Tracing按钮,接着运行你想分析的代码,运行完毕后点击Get Allocations按钮就能够看见一个已分配对象的列表,如下: 点击上面第一个表格中的任何一项就能够在第二个表格中看见导致该内存分配的栈信息,通过这个工具我们可以很方便的知道代码分配了哪类对象、在哪个线 程、哪个类、哪个文件的哪一行。譬如我们可以通过Allocation Tracker分别做一次Paint对象实例化在onDraw与构造方法的一个自定义View的内存跟踪,然后你就明白这个工具的强大了。PS一句,Android Studio新版本除过DDMS以外在Memory视图的左侧已经集成了Allocation Tracker功能,只是用起来还是没有DDMS的方便实用,如下图: 2-3-6 使用Traceview和dmtracedump进行分析优化关于UI卡顿问题我们还可以通过运行Traceview工具进行分析,他是一个分析器,记录了应用程序中每个函数的执行时间;我们可以打开DDMS 然后选择一个进程,接着点击上面的“Start Method Profiling”按钮(红色小点变为黑色即开始运行),然后操作我们的卡顿UI(小范围测试,所以操作最好不要超过5s),完事再点一下刚才按的那个 按钮,稍等片刻即可出现下图,如下: 花花绿绿的一幅图我们怎么分析呢?下面我们解释下如何通过该工具定位问题:整个界面包括上下两部分,上面是你测试的进程中每个线程运行的时间线,下面是每个方法(包含parent及child)执行的各个指标的值。通过上 图的时间面板可以直观发现,整个trace时间段main线程做的事情特别多,其他的做的相对较少。当我们选择上面的一个线程后可以发现下面的性能面板很 复杂,其实这才是TraceView的核心图表,它主要展示了线程中各个方法的调用信息(CPU使用时间、调用次数等),这些信息就是我们分析UI性能卡 顿的核心关注点,所以我们先看几个重要的属性说明,如下: 属性名    含义       name    线程中调运的方法名;       Incl CPU Time    当前方法(包含内部调运的子方法)执行占用的CPU时间;       Excl CPU Time    当前方法(不包含内部调运的子方法)执行占用的CPU时间;       Incl Real Time    当前方法(包含内部调运的子方法)执行的真实时间,ms单位;       Excl Real Time    当前方法(不包含内部调运的子方法)执行的真实时间,ms单位;       Calls+Recur Calls/Total    当前方法被调运的次数及递归调运占总调运次数百分比;       CPU Time/Call    当前方法调运CPU时间与调运次数比,即当前方法平均执行CPU耗时时间;       Real Time/Call    当前方法调运真实时间与调运次数比,即当前方法平均执行真实耗时时间;(重点关注)     有了对上面Traceview图表的一个认识之后我们就来看看具体导致UI性能后该如何切入分析,一般Traceview可以定位两类性能问题:方法调运一次需要耗费很长时间导致卡顿;方法调运一次耗时不长,但被频繁调运导致累计时长卡顿。譬如我们来举个实例,有时候我们写完App在使用时不觉得有啥大的影响,但是当我们启动完App后静止在那却十分费电或者导致设备发热,这种情况我 们就可以打开Traceview然后按照Cpu Time/Call或者Real Time/Call进行降序排列,然后打开可疑的方法及其child进行分析查看,然后再回到代码定位检查逻辑优化即可;当然了,我们也可以通过该工具来 trace我们自定义View的一些方法来权衡性能问题,这里不再一一列举喽。可以看见,Traceview能够帮助我们分析程序性能,已经很方便了,然而Traceview家族还有一个更加直观强大的小工具,那就是可以通过dmtracedump生成方法调用图。具体做法如下:dmtracedump -g result.png target.trace  //结果png文件  目标trace文件1通过这个生成的方法调运图我们可以更加直观的发现一些方法的调运异常现象。不过本人优化到现在还没怎么用到它,每次用到Traceview分析就已经搞定问题了,所以说dmtracedump自己酌情使用吧。PS一句,Android Studio新版本除过DDMS以外在CPU视图的左侧已经集成了Traceview(start Method Tracing)功能,只是用起来还是没有DDMS的方便实用(这里有一篇AS MT个人觉得不错的分析文章(引用自网络,链接属于原作者功劳)),如下图: 2-3-7 使用Systrace进行分析优化Systrace其实有些类似Traceview,它是对整个系统进行分析(同一时间轴包含应用及SurfaceFlinger、 WindowManagerService等模块、服务运行信息),不过这个工具需要你的设备内核支持trace(命令行检查/sys/kernel /debug/tracing)且设备是eng或userdebug版本才可以,所以使用前麻烦自己确认一下。我们在分析UI性能时一般只关注图形性能(所以必须选择Graphics和View,其他随意),同时一般对于卡顿的抓取都是5s,最多10s。启动Systrace进行数据抓取可以通过两种方式,命令行方式如下:python systrace.py --time=10 -o mynewtrace.html sched gfx view wm1图形模式: 打开DDMS->Capture system wide trace using Android systrace->设置时间与选项点击OK就开始了抓取,接着操作APP,完事生成一个trace.html文件,用Chrome打开即可如下图: 在Chrome中浏览分析该文件我们可以通过键盘的W-A-S-D键来搞定,由于上面我们在进行trace时选择了一些选项,所以上图生成了左上方相关的 CPU频率、负载、状态等信息,其中的CPU N代表了CPU核数,每个CPU行的柱状图表代表了当前时间段当前核上的运行信息;下面我们再来看看SurfaceFlinger的解释,如下: 可以看见上面左边栏的SurfaceFlinger其实就是负责绘制Android程序UI的服务,所以SurfaceFlinger能反应出整体 绘制情况,可以关注上图VSYNC-app一行可以发现前5s多基本都能够达到16ms刷新间隔,5s多开始到7s多大于了15ms,说明此时存在绘制丢 帧卡顿;同时可以发现surfaceflinger一行明显存在类似不规律间隔,这是因为有的地方是不需要重新渲染UI,所以有大范围不规律,有的是因为 阻塞导致不规律,明显可以发现0到4s间大多是不需要渲染,而5s以后大多是阻塞导致;对应这个时间点我们放大可以看到每个部分所使用的时间和正在执行的 任务,具体如下:  可以发现具体的执行明显存在超时性能卡顿(原点不是绿色的基本都代表存在一定问题,下面和右侧都会提示你选择的帧相关详细信息或者alert信 息),但是遗憾的是通过Systrace只能大体上发现是否存在性能问题,具体问题还需要通过Traceview或者代码中嵌入Trace工具类等去继续 详细分析,总之很蛋疼。PS:如果你想使用Systrace很轻松的分析定位所有问题,看明白所有的行含义,你还需要具备非常扎实的Android系统框架的原理才可以将该工具使用的得心应手。2-3-8 使用traces.txt文件进行ANR分析优化ANR(Application Not Responding)是Android中AMS与WMS监测应用响应超时的表现;之所以把臭名昭著的ANR单独作为UI性能卡顿的分析来说明是因为 ANR是直接卡死UI不动且必须要解掉的Bug,我们必须尽量在开发时避免他的出现,当然了,万一出现了那就用下面介绍的方法来分析吧。我们应用开发中常见的ANR主要有如下几类:按键触摸事件派发超时ANR,一般阈值为5s(设置中开启ANR弹窗,默认有事件派发才会触发弹框ANR);广播阻塞ANR,一般阈值为10s(设置中开启ANR弹窗,默认不弹框,只有log提示);服务超时ANR,一般阈值为20s(设置中开启ANR弹窗,默认不弹框,只有log提示);当ANR发生时除过logcat可以看见的log以外我们还可以在系统指定目录下找到traces文件或dropbox文件进行分析,发生ANR后我们可以通过如下命令得到ANR trace文件:adb pull /data/anr/traces.txt ./1然后我们用txt编辑器打开可以发现如下结构分析://显示进程id、ANR发生时间点、ANR发生进程包名----- pid 19073 at 2015-10-08 17:24:38 -----Cmd line: com.example.yanbo.myapplication//一些GC等object信息,通常可以忽略......//ANR方法堆栈打印信息!重点!DALVIK THREADS (18):"main" prio=5 tid=1 Sleeping  | group="main" sCount=1 dsCount=0 obj=0x7497dfb8 self=0x7f9d09a000  | sysTid=19073 nice=0 cgrp=default sched=0/0 handle=0x7fa106c0a8  | state=S schedstat=( 125271779 68162762 280 ) utm=11 stm=1 core=0 HZ=100  | stack=0x7fe90d3000-0x7fe90d5000 stackSize=8MB  | held mutexes=  at java.lang.Thread.sleep!(Native method)  - sleeping on <0x0a2ae345> (a java.lang.Object)  at java.lang.Thread.sleep(Thread.java:1031)  - locked <0x0a2ae345> (a java.lang.Object)//真正导致ANR的问题点,可以发现是onClick中有sleep导致。我们平时可以类比分析即可,这里不详细说明。  at java.lang.Thread.sleep(Thread.java:985)  at com.example.yanbo.myapplication.MainActivity$1.onClick(MainActivity.java:21)  at android.view.View.performClick(View.java:4908)  at android.view.View$PerformClick.run(View.java:20389)  at android.os.Handler.handleCallback(Handler.java:815)  at android.os.Handler.dispatchMessage(Handler.java:104)  at android.os.Looper.loop(Looper.java:194)  at android.app.ActivityThread.main(ActivityThread.java:5743)  at java.lang.reflect.Method.invoke!(Native method)  at java.lang.reflect.Method.invoke(Method.java:372)  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:988)  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:783)......//省略一些不常关注堆栈打印......至此常见的应用开发中ANR分析定位就可以解决了。2-4 应用UI性能分析解决总结可以看见,关于Android UI卡顿的性能分析还是有很多工具的,上面只是介绍了应用开发中我们经常使用的一些而已,还有一些其他的,譬如Oprofile等工具不怎么常用,这里就不再详细介绍。通过上面UI性能的原理、原因、工具分析总结可以发现,我们在开发应用时一定要时刻重视性能问题,如若真的没留意出现了性能问题,不妨使用上面的一 些案例方式进行分析。但是那终归是补救措施,在我们知道上面UI卡顿原理之后我们应该尽量从项目代码架构搭建及编写时就避免一些UI性能问题,具体项目中 常见的注意事项如下:布局优化;尽量使用include、merge、ViewStub标签,尽量不存在冗余嵌套及过于复杂布局(譬如10层就会直接异常),尽 量使用GONE替换INVISIBLE,使用weight后尽量将width和heigh设置为0dp减少运算,Item存在非常复杂的嵌套时考虑使用自 定义Item View来取代,减少measure与layout次数等。列表及Adapter优化;尽量复用getView方法中的相关View,不重复获取实例导致卡顿,列表尽量在滑动过程中不进行UI元素刷新等。背景和图片等内存分配优化;尽量减少不必要的背景设置,图片尽量压缩处理显示,尽量避免频繁内存抖动等问题出现。自定义View等绘图与布局优化;尽量避免在draw、measure、layout中做过于耗时及耗内存操作,尤其是draw方法中,尽量减少draw、measure、layout等执行次数。避免ANR,不要在UI线程中做耗时操作,遵守ANR规避守则,譬如多次数据库操作等。当然了,上面只是列出了我们项目中常见的一些UI性能注意事项而已,相信还有很多其他的情况这里没有说到,欢迎补充。还有一点就是我们上面所谓的 UI性能优化分析总结等都是建议性的,因为性能这个问题是一个涉及面很广很泛的问题,有些优化不是必需的,有些优化是必需的,有些优化掉以后又是得不偿失 的,所以我们一般着手解决那些必须的就可以了。3 应用开发Memory内存性能分析优化说完了应用开发中的UI性能问题后我们就该来关注应用开发中的另一个重要、严重、非常重要的性能问题了,那就是内存性能优化分析。Android其 实就是嵌入式设备,嵌入式设备核心关注点之一就是内存资源;有人说现在的设备都在堆硬件配置(譬如国产某米的某兔跑分手机、盒子等),所以内存不会再像以 前那么紧张了,其实这句话听着没错,但为啥再牛逼配置的Android设备上有些应用还是越用系统越卡呢?这里面的原因有很多,不过相信有了这一章下面的 内容分析,作为一个移动开发者的你就有能力打理好自己应用的那一亩三分地内存了,能做到这样就足以了。关于Android内存优化,这里有一篇 Google的官方指导文档,但是本文为自己项目摸索,会有很多不一样的地方。3-1 Android内存管理原理系统级内存管理:Android系统内核是基于Linux,所以说Android的内存管理其实也是Linux的升级版而已。Linux在进程停止后就结束该进程, 而Android把这些停止的进程都保留在内存中,直到系统需要更多内存时才选择性的释放一些,保留在内存中的进程默认(不包含后台service与 Thread等单独UI线程的进程)不会影响整体系统的性能(速度与电量等)且当再次启动这些保留在内存的进程时可以明显提高启动速度,不需要再去加载。再直白点就是说Android系统级内存管理机制其实类似于Java的垃圾回收机制,这下明白了吧;在Android系统中框架会定义如下几类进程、在系统内存达到规定的不同level阈值时触发清空不同level的进程类型。 可以看见,所谓的我们的Service在后台跑着跑着挂了,或者盒子上有些大型游戏启动起来就挂(之前我在上家公司做盒子时遇见过),有一个直接的 原因就是这个阈值定义的太大,导致系统一直认为已经达到阈值,所以进行优先清除了符合类型的进程。所以说,该阈值的设定是有一些讲究的,额,扯多了,我们 主要是针对应用层内存分析的,系统级内存回收了解这些就基本够解释我们应用在设备上的一些表现特征了。应用级内存管理:在说应用级别内存管理原理时大家先想一个问题,假设有一个内存为1G的Android设备,上面运行了一个非常非常吃内存的应用,如果没有任何机制的情况下是不是用着用着整个设备会因为我们这个应用把1G内存吃光然后整个系统运行瘫痪呢?哈哈,其实Google的工程师才不会这么傻的把系统设计这么差劲。为了使系统不存在我们上面假想情况且能安全快速的运行,Android的框架使 得每个应用程序都运行在单独的进程中(这些应用进程都是由Zygote进程孵化出来的,每个应用进程都对应自己唯一的虚拟机实例);如果应用在运行时再存 在上面假想的情况,那么瘫痪的只会是自己的进程,不会直接影响系统运行及其他进程运行。既然每个Android应用程序都执行在自己的虚拟机中,那了解Java的一定明白,每个虚拟机必定会有堆内存阈值限制(值得一提的是这个阈值一般 都由厂商依据硬件配置及设备特性自己设定,没有统一标准,可以为64M,也可以为128M等;它的配置是在Android的属性系统的/system /build.prop中配置dalvik.vm.heapsize=128m即可,若存在dalvik.vm.heapstartsize则表示初始申 请大小),也即一个应用进程同时存在的对象必须小于阈值规定的内存大小才可以正常运行。接着我们运行的App在自己的虚拟机中内存管理基本就是遵循Java的内存管理机制了,系统在特定的情况下主动进行垃圾回收。但是要注意的一点就是 在Android系统中执行垃圾回收(GC)操作时所有线程(包含UI线程)都必须暂停,等垃圾回收操作完成之后其他线程才能继续运行。这些GC垃圾回收 一般都会有明显的log打印出回收类型,常见的如下:GC_MALLOC——内存分配失败时触发;GC_CONCURRENT——当分配的对象大小超过一个限定值(不同系统)时触发;GC_EXPLICIT——对垃圾收集的显式调用(System.gc()) ;GC_EXTERNAL_ALLOC——外部内存分配失败时触发;通过上面这几点的分析可以发现,应用的内存管理其实就是一个萝卜一个坑,坑都一般大,你在开发应用时要保证的是内存使用同一时刻不能超过坑的大小,否则就装不下了。3-2 Android内存泄露性能分析有了关于Android的一些内存认识,接着我们来看看关于Android应用开发中常出现的一种内存问题—-内存泄露。3-2-1 Android应用内存泄露概念众所周知,在Java中有些对象的生命周期是有限的,当它们完成了特定的逻辑后将会被垃圾回收;但是,如果在对象的生命周期本来该被垃圾回收时这个 对象还被别的对象所持有引用,那就会导致内存泄漏;这样的后果就是随着我们的应用被长时间使用,他所占用的内存越来越大。如下就是一个最常见简单的泄露例 子(其它的泄露不再一一列举了):public final class MainActivity extends Activity {     private DbManager mDbManager;    @Override    protected void onCreate(Bundle savedInstanceState) {         super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        //DbManager是一个单例模式类,这样就持有了MainActivity引用,导致泄露        mDbManager = DbManager.getInstance(this);    }}123456789101112可以看见,上面例子中我们让一个单例模式的对象持有了当前Activity的强引用,那在当前Acvitivy执行完onDestroy()后,这个Activity就无法得到垃圾回收,也就造成了内存泄露。内存泄露可以引发很多的问题,常见的内存泄露导致问题如下:应用卡顿,响应速度慢(内存占用高时JVM虚拟机会频繁触发GC);应用被从后台进程干为空进程(上面系统内存原理有介绍,也就是超过了阈值);应用莫名的崩溃(上面应用内存原理有介绍,也就是超过了阈值OOM);造成内存泄露泄露的最核心原理就是一个对象持有了超过自己生命周期以外的对象强引用导致该对象无法被正常垃圾回收;可以发现,应用内存泄露是个相当棘手重要的问题,我们必须重视。3-2-2 Android应用内存泄露察觉手段知道了内存泄露的概念之后肯定就是想办法来确认自己的项目是否存在内存泄露了,那该如何察觉自己项目是否存在内存泄露呢?如下提供了几种常用的方式: 察觉方式    场景       AS的Memory窗口    平时用来直观了解自己应用的全局内存情况,大的泄露才能有感知。       DDMS-Heap内存监测工具    同上,大的泄露才能有感知。       dumpsys meminfo命令    常用方式,可以很直观的察觉一些泄露,但不全面且常规足够用。       leakcanary神器    比较强大,可以感知泄露且定位泄露;实质是MAT原理,只是更加自动化了,当现有代码量已经庞大成型,且无法很快察觉掌控全局代码时极力推荐;或者是偶现泄露的情况下极力推荐。     AS的Memory窗口如下,详细的说明这里就不解释了,很简单很直观(使用频率高): DDMS-Heap内存监测工具窗口如下,详细的说明这里就不解释了,很简单(使用频率不高): dumpsys meminfo命令如下(使用频率非常高,非常高效,我的最爱之一,平时一般关注几个重要的Object个数即可判断一般的泄露;当然了,adb shell dumpsys meminfo不跟参数直接展示系统所有内存状态): leakcanary神器使用这里先不说,下文会专题介绍,你会震撼的一B。有了这些工具的定位我们就能很方便的察觉我们App的内存泄露问题,察觉到以后该怎么定位分析呢,继续往下看。3-2-3 Android应用内存泄露leakcanary工具定位分析leakcanary是一个开源项目,一个内存泄露自动检测工具,是著名的GitHub开源组织Square贡献的,它的主要优势就在于自动化过早 的发觉内存泄露、配置简单、抓取贴心,缺点在于还存在一些bug,不过正常使用百分之九十情况是OK的,其核心原理与MAT工具类似。关于leakcanary工具的配置使用方式这里不再详细介绍,因为真的很简单,详情点我参考官方教程学习使用即可。PS:之前在优化性能时发现我们有一个应用有两个界面退出后Activity没有被回收(dumpsys meminfo发现一直在加),所以就怀疑可能存在内存泄露。但是问题来了,这两个Activity的逻辑十分复杂,代码也不是我写的,相关联的代码量也 十分庞大,更加郁闷的是很难判断是哪个版本修改导致的,这时候只知道有泄露,却无法定位具体原因,使用MAT分析解决掉了一个可疑泄露后发现泄露又变成了 概率性的。可以发现,对于这种概率性的泄露用MAT去主动抓取肯定是很耗时耗力的,所以决定直接引入leakcanary神器来检测项目,后来很快就彻底 解决了项目中所有必现的、偶现的内存泄露。总之一点,工具再强大也只是帮我们定位可能的泄露点,而最核心的GC ROOT泄露信息推导出泄露问题及如何解决还是需要你把住代码逻辑及泄露核心概念去推理解决。3-2-4 Android应用内存泄露MAT工具定位分析Eclipse Memory Analysis Tools(点我下载)是一个专门分析Java堆数据内存引用的工具,我们可以使用它方便的定位内存泄露原因,核心任务就是找到GC ROOT位置即可,哎呀,关于这个工具的使用我是真的不想说了,自己搜索吧,实在简单、传统的不行了。PS:这是开发中使用频率非常高的一个工具之一,麻烦务必掌握其核心使用技巧,虽然Android Studio已经实现了部分功能,但是真的很难用,遇到问题目前还是使用Eclipse Memory Analysis Tools吧。原谅我该小节的放荡不羁!!!!(其实我是困了,呜呜!)3-2-5 Android应用开发规避内存泄露建议有了上面的原理及案例处理其实还不够,因为上面这些处理办法是补救的措施,我们正确的做法应该是在开发过程中就养成良好的习惯和敏锐的嗅觉才对,所以下面给出一些应用开发中常见的规避内存泄露建议:Context使用不当造成内存泄露;不要对一个Activity Context保持长生命周期的引用(譬如上面概念部分给出的示例)。尽量在一切可以使用应用ApplicationContext代替Context的 地方进行替换(原理我前面有一篇关于Context的文章有解释)。非静态内部类的静态实例容易造成内存泄漏;即一个类中如果你不能够控制它其中内部类的生命周期(譬如Activity中的一些特殊Handler等),则尽量使用静态类和弱引用来处理(譬如ViewRoot的实现)。警惕线程未终止造成的内存泄露;譬如在Activity中关联了一个生命周期超过Activity的Thread,在退出Activity 时切记结束线程。一个典型的例子就是HandlerThread的run方法是一个死循环,它不会自己结束,线程的生命周期超过了Activity生命周 期,我们必须手动在Activity的销毁方法中中调运thread.getLooper().quit();才不会泄露。对象的注册与反注册没有成对出现造成的内存泄露;譬如注册广播接收器、注册观察者(典型的譬如数据库的监听)等。创建与关闭没有成对出现造成的泄露;譬如Cursor资源必须手动关闭,WebView必须手动销毁,流等对象必须手动关闭等。不要在执行频率很高的方法或者循环中创建对象,可以使用HashTable等创建一组对象容器从容器中取那些对象,而不用每次new与释放。避免代码设计模式的错误造成内存泄露;譬如循环引用,A持有B,B持有C,C持有A,这样的设计谁都得不到释放。关于规避内存泄露上面我只是列出了我在项目中经常遇见的一些情况而已,肯定不全面,欢迎拍砖!当然了,只有我们做到好的规避加上强有力的判断嗅觉泄露才能让我们的应用驾驭好自己的一亩三分地。3-3 Android内存溢出OOM性能分析上面谈论了Android应用开发的内存泄露,下面谈谈内存溢出(OOM);其实可以认为内存溢出与内存泄露是交集关系,具体如下图: 下面我们就来看看内存溢出(OOM)相关的东东吧。3-3-1 Android应用内存溢出OOM概念上面我们探讨了Android内存管理和应用开发中的内存泄露问题,可以知道内存泄露一般影响就是导致应用卡顿,但是极端的影响是使应用挂掉。前面 也提到过应用的内存分配是有一个阈值的,超过阈值就会出问题,这里我们就来看看这个问题—–内存溢出(OOM–OutOfMemoryError)。内存溢出的主要导致原因有如下几类:应用代码存在内存泄露,长时间积累无法释放导致OOM;应用的某些逻辑操作疯狂的消耗掉大量内存(譬如加载一张不经过处理的超大超高清图片等)导致超过阈值OOM;可以发现,无论哪种类型,导致内存溢出(OutOfMemoryError)的核心原因就是应用的内存超过阈值了。3-3-2 Android应用内存溢出OOM性能分析通过上面的OOM概念和那幅交集图可以发现,要想分析OOM原因和避免OOM需要分两种情况考虑,泄露导致的OOM,申请过大导致的OOM。内存泄露导致的OOM分析:这种OOM一旦发生后会在logcat中打印相关OutOfMemoryError的异常栈信息,不过你别高兴太早,这种情况下导致的OOM打印异常信息是没有太大作用,因为这种OOM的导致一般都如下图情况(图示为了说明问题数据和场景有夸张,请忽略): 从图片可以看见,这种OOM我们有时也遇到,第一反应是去分析OOM异常打印栈,可是后来发现打印栈打印的地方没有啥问题,没有可优化的余地了,于是就郁闷了。其实这时候你留心观察几个现象即可,如下:留意你执行触发OOM操作前的界面是否有卡顿或者比较密集的GC打印;使用命令查看下当前应用占用内存情况;确认了以上这些现象你基本可以断定该OOM的log真的没用,真正导致问题的原因是内存泄露,所以我们应该按照上节介绍的方式去着手排查内存泄露问题,解决掉内存泄露后红色空间都能得到释放,再去显示一张0.8M的优化图片就不会再报OOM异常了。不珍惜内存导致的OOM分析:上面说了内存泄露导致的OOM异常,下面我们再来看一幅图(数据和场景描述有夸张,请忽略),如下: 可见,这种类型的OOM就很好定位原因了,一般都可以从OOM后的log中得出分析定位。如下例子,我们在Activity中的ImageView放置一张未优化的特大的(30多M)高清图片,运行直接崩溃如下://抛出OOM异常10-10 09:01:04.873 11703-11703/? E/art: Throwing OutOfMemoryError "Failed to allocate a 743620620 byte allocation with 4194208 free bytes and 239MB until OOM"10-10 09:01:04.940 11703-11703/? E/art: Throwing OutOfMemoryError "Failed to allocate a 743620620 byte allocation with 4194208 free bytes and 239MB until OOM"//堆栈打印10-10 09:01:04.958 11703-11703/? E/AndroidRuntime: FATAL EXCEPTION: main10-10 09:01:04.958 11703-11703/? E/AndroidRuntime: Process: com.example.application, PID: 1170310-10 09:01:04.958 11703-11703/? E/AndroidRuntime: java.lang.RuntimeException: Unable to start activity ComponentInfo{com.example.application/com.example.myapplication.MainActivity}: android.view.InflateException: Binary XML file line #21: Error inflating class <unknown>10-10 09:01:04.958 11703-11703/? E/AndroidRuntime:     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2610)10-10 09:01:04.958 11703-11703/? E/AndroidRuntime:     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2684)10-10 09:01:04.958 11703-11703/? E/AndroidRuntime:     at android.app.ActivityThread.access$800(ActivityThread.java:177)10-10 09:01:04.958 11703-11703/? E/AndroidRuntime:     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1542)10-10 09:01:04.958 11703-11703/? E/AndroidRuntime:     at android.os.Handler.dispatchMessage(Handler.java:111)10-10 09:01:04.958 11703-11703/? E/AndroidRuntime:     at android.os.Looper.loop(Looper.java:194)10-10 09:01:04.958 11703-11703/? E/AndroidRuntime:     at android.app.ActivityThread.main(ActivityThread.java:5743)10-10 09:01:04.958 11703-11703/? E/AndroidRuntime:     at java.lang.reflect.Method.invoke(Native Method)10-10 09:01:04.958 11703-11703/? E/AndroidRuntime:     at java.lang.reflect.Method.invoke(Method.java:372)10-10 09:01:04.958 11703-11703/? E/AndroidRuntime:     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:988)10-10 09:01:04.958 11703-11703/? E/AndroidRuntime:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:783)//出错地点,原因是21行的ImageView设置的src是一张未优化的31M的高清图片10-10 09:01:04.958 11703-11703/? E/AndroidRuntime:  Caused by: android.view.InflateException: Binary XML file line #21: Error inflating class <unknown>10-10 09:01:04.958 11703-11703/? E/AndroidRuntime:     at android.view.LayoutInflater.createView(LayoutInflater.java:633)通过上面的log可以很方便的看出来问题原因所在地,那接下来的做法就是优化呗,降低图片的相关规格即可(譬如使用BitmapFactory的Option类操作等)。PS:提醒一句的是记得应用所属的内存是区分Java堆和native堆的!3-3-3 Android应用规避内存溢出OOM建议还是那句话,等待OOM发生是为时已晚的事,我们应该将其扼杀于萌芽之中,至于如何在开发中规避OOM,如下给出一些我们应用开发中的常用的策略建议:时刻记得不要加载过大的Bitmap对象;譬如对于类似图片加载我们要通过BitmapFactory.Options设置图片的一些采样比率和复用等,具体做法点我参考官方文档,不过过我们一般都用fresco或Glide开源库进行加载。优化界面交互过程中频繁的内存使用;譬如在列表等操作中只加载可见区域的Bitmap、滑动时不加载、停止滑动后再开始加载。有些地方避免使用强引用,替换为弱引用等操作。避免各种内存泄露的存在导致OOM。对批量加载等操作进行缓存设计,譬如列表图片显示,Adapter的convertView缓存等。尽可能的复用资源;譬如系统本身有很多字符串、颜色、图片、动画、样式以及简单布局等资源可供我们直接使用,我们自己也要尽量复用style等资源达到节约内存。对于有缓存等存在的应用尽量实现onLowMemory()和onTrimMemory()方法。尽量使用线程池替代多线程操作,这样可以节约内存及CPU占用率。尽量管理好自己的Service、Thread等后台的生命周期,不要浪费内存占用。尽可能的不要使用依赖注入,中看不中用。尽量在做一些大内存分配等可疑内存操作时进行try catch操作,避免不必要的应用闪退。尽量的优化自己的代码,减少冗余,进行编译打包等优化对齐处理,避免类加载时浪费内存。可以发现,上面只是列出了我们开发中常见的导致OOM异常的一些规避原则,还有很多相信还没有列出来,大家可以自行追加参考即可。3-4 Android内存性能优化总结无论是什么电子设备的开发,内存问题永远都是一个很深奥、无底洞的话题,上面的这些内存分析建议也单单只是Android应用开发中一些常见的场景而已,真正的达到合理的优化还是需要很多知识和功底的。合理的应用架构设计、设计风格选择、开源Lib选择、代码逻辑规范等都会决定到应用的内存性能,我们必须时刻头脑清醒的意识到这些问题潜在的风险与优劣,因为内存优化必须要有一个度,不能一味的优化,亦不能置之不理。4 Android应用API使用及代码逻辑性能分析在我们开发中除过常规的那些经典UI、内存性能问题外其实还存在很多潜在的性能优化、这种优化不是十分明显,但是在某些场景下却是非常有必要的,所以我们简单列举一些常见的其他潜在性能优化技巧,具体如下探讨。4-1 Android应用String/StringBuilder/StringBuffer优化建议字符串操作在Android应用开发中是十分常见的操作,也就是这个最简单的字符串操作却也暗藏很多潜在的性能问题,下面我们实例来说说。先看下面这个关于String和StringBuffer的对比例子://性能差的实现String str1 = "Name:";String str2 = "GJRS";String Str = str1 + str2;//性能好的实现String str1 = "Name:";String str2 = "GJRS";StringBuffer str = new StringBuilder().append(str1).append(str2);通过这个例子可以看出来,String对象(记得是对象,不是常量)和StringBuffer对象的主要性能区别在于String对象是不可变 的,所以每次对String对象做改变操作(譬如“+”操作)时其实都生成了新的String对象实例,所以会导致内存消耗性能问题;而 StringBuffer对象做改变操作每次都会对自己进行操作,所以不需要消耗额外的内存空间。我们再看一个关于String和StringBuffer的对比例子://性能差的实现StringBuffer str = new StringBuilder().append("Name:").append("GJRS");//性能好的实现String Str = "Name:" + "GJRS";1234在这种情况下你会发现StringBuffer的性能反而没有String的好,原因是在JVM解释时认为 String Str = "Name:" + "GJRS";就是String Str = "Name:GJRS";,所以自然比StringBuffer快了。可以发现,如果我们拼接的是字符串常量则String效率比StringBuffer高,如果拼接的是字符串对象,则StringBuffer比 String效率高,我们在开发中要酌情选择。当然,除过注意StringBuffer和String的效率问题,我们还应该注意另一个问题,那就是 StringBuffer和StringBuilder的区别,其实StringBuffer和StringBuilder都继承自同一个父类,只是 StringBuffer是线程安全的,也就是说在不考虑多线程情况下StringBuilder的性能又比StringBuffer高。PS:如果想追究清楚他们之间具体细节差异,麻烦自己查看实现源码即可。4-2 Android应用OnTrimMemory()实现性能建议OnTrimMemory是Android 4.0之后加入的一个回调方法,作用是通知应用在不同的情况下进行自身的内存释放,以避免被系统直接杀掉,提高应用程序的用户体验(冷启动速度是热启动的 2~3倍)。系统会根据当前不同等级的内存使用情况调用这个方法,并且传入当前内存等级,这个等级有很多种,我们可以依据情况实现不同的等级,这里不详细 介绍,但是要说的是我们应用应该至少实现如下等级:TRIM_MEMORY_BACKGROUND 内存已经很低了,系统准备开始根据LRU缓存来清理进程。这时候如果我们手动释放一些不重要的缓存资源,则当用户返回我们应用时会感觉到很顺畅,而不是重新启动应用。可以实现OnTrimMemory方法的系统组件有Application、Activity、Fragement、 Service、ContentProvider;关于OnTrimMemory释放哪些内存其实在架构阶段就要考虑清楚哪些对象是要常驻内存的,哪些是伴随组件周期存在的,一般需要释放的都是缓存。 如下给出一个我们项目中常用的例子:@Overridepublic void onTrimMemory(int level) {    if (level >= ComponentCallbacks2.TRIM_MEMORY_BACKGROUND) {        clearCache();   }}通常在我们代码实现了onTrimMemory后很难复显这种内存消耗场景,但是你又怕引入新Bug,想想办法测试。好在我们有一个快捷的方式来模拟触发该水平内存释放,如下命令:adb shell dumpsys gfxinfo packagename -cmd trim value1packagename为包名或者进程id,value为ComponentCallbacks2.java里面定义的值,可以为80、60、40、20、5等,我们模拟触发其中的等级即可。4-3 Android应用HashMap与ArrayMap及SparseArray优化建议在Android开发中涉及到数据逻辑部分大部分用的都是Java的API(譬如HashMap),但是对于Android设备来说有些Java的 API并不适合,可能会导致系统性能下降,好在Google团队已经意识到这些问题,所以他们针对Android设备对Java的一些API进行了优化, 优化最多就是使用了ArrayMap及SparseArray替代HashMap来获得性能提升。HashMap:HashMap内部使用一个默认容量为16的数组来存储数据,数组中每一个元素存放一个链表的头结点,其实整个HashMap内部结构就是一个哈希 表的拉链结构。HashMap默认实现的扩容是以2倍增加,且获取一个节点采用了遍历法,所以相对来说无论从内存消耗还是节点查找上都是十分昂贵的。SparseArray:SparseArray比HashMap省内存是因为它避免了对Key进行自动装箱(int转Integer),它内部是用两个数组来进行数据存储 的(一个存Key,一个存Value),它内部对数据采用了压缩方式来表示稀疏数组数据,从而节约内存空间,而且其查找节点的实现采用了二分法,很明显可 以看见性能的提升。ArrayMap:ArrayMap内部使用两个数组进行数据存储,一个记录Key的Hash值,一个记录Value值,它和SparseArray类似,也会在查找时对Key采用二分法。有了上面的基本了解我们可以得出结论供开发时参考,当数据量不大(千位级内)且Key为int类型时使用SparseArray替换HashMap 效率高;当数据量不大(千位级内)且数据类型为Map类型时使用ArrayMap替换HashMap效率高;其他情况下HashMap效率相对高于二者。4-4 Android应用ContentProviderOperation优化建议ContentProvider是Android应用开发的核心组件之一,有时候在开发中需要使用ContentProvider对多行数据进行操 作,我们的做法一般是多次调运相关操作方法,殊不知这种实现方式是非常低性能的,取而代之的做法应该是使用批量操作,具体为了使批量更新、插入、删除数据 操作更加方便官方提供了ContentProviderOperation工具类。所以在我们开发中遇到类似情景时请务必使用批量操作,具体的优势如下:所有的操作都在一个事务中执行,可以保证数据的完整性。批量操作在一个事务中执行,所以只用打开、关闭一个事务。减轻应用程序与ContentProvider间的多次频繁交互,提升性能。可以看见,这对于数据库操作来说是一个非常有用的优化措施,烦请务必重视(我们项目优化过,的确有很大提升)。4-5 Android应用其他逻辑优化建议关于API及逻辑性能优化其实有多知识点的,这里无法一一列出,只能给出一些重要的知识点,下面再给出一些常见的优化建议:避免在Android中使用Java的枚举类型,因为编译后不但占空间,加载也费时,完全没有static final的变量好用、高效。Handler发送消息时尽量使用obtain去获取已经存在的Message对象进行复用,而不是新new Message对象,这样可以减轻内存压力。在使用后台Service时尽量将能够替换为IntentService的地方替换为此,这样可以减轻系统压力、省电、省内存、省CPU占用率。在当前类内部尽量不要通过自己的getXXX、setXXX对自己内部成员进行操作,而是直接使用,这样可以提高代码执行效率。不要一味的为了设计模式而过分的抽象代码,因为代码抽象系数与代码加载执行时间成正比。尽量减少个数、减小锁范围,避免造成性能问题。合理的选择使用for循环与增强型for循环,譬如不要在ArrayList上使用增强型for循环等。哎呀,类似的小优化技巧有很多,这里不一一列举了,自行发挥留意即可。5 Android应用移动设备电池耗电性能分析有了UI性能优化、内存性能优化、代码编写优化之后我们在来说说应用开发中很重要的一个优化模块—–电量优化。5-1 Android应用耗电量概念在盒子等开发时可能电量优化不是特别重视(视盒子待机真假待机模式而定),但是在移动设备开发中耗电量是一个非常重要的指标,如果用户一旦发现我们的应用非常耗电,不好意思,他们大多会选择卸载来解决此类问题,所以耗电量是一个十分重要的问题。关于我们应用的耗电量情况我们可以进行定长时间测试,至于具体的耗电量统计等请参考此文,同时我们还可以直接通过Battery Historian Tool来查看详细的应用电量消耗情况。最简单常用办法是通过命令直接查看,如下:adb shell dumpsys batterystats1其实我们一款应用耗电量最大的部分不是UI绘制显示等,常见耗电量最大原因基本都是因为网络数据交互、GPS定位、大量内存性能问题、冗余的后台线程和Service等造成。5-2 Android应用耗电量优化建议优化电量使用情况我们不仅可以使用系统提供的一些API去处理,还可以在平时编写代码时就养成好的习惯。具体的一些建议如下:在需要网络的应用中,执行某些操作前尽量先进行网络状态判断

标签: 0541n5no接近传感器300gi传感器px8811736n5pc接近传感器1836n5to接近传感器1728n5pc接近传感器

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台