原文 Developing for Android, III:The Rules: Performanceandroid
在Android中,性能和内存的关系很密切,由于系统的总体内存大小会影响全部进程的性能,由于垃圾回收器会对运行期间的性能产生很大的影响。下面的重点是运行期间的性能问题而不是内存。web
正如在第一篇文章中提到过的,在UI Thread作繁重的操做会影响到渲染的处理。一样会致使动画的问题,由于它依赖于每一帧的渲染。这就意味着在动画期间避免在UI进行繁重的操做就更加剧要。如下是一些能够避免的常见状况:数据库
Layout
Measurement 和 layout是比较繁重的操做,view的层级越复杂,操做就会越繁重。Measurement和layout是在UI Thred发发生的。所以当系统须要运行一个动画的时候紧接着还需进行layout,而它们都是在同一个线程,所以动画的流畅度可能就会受到影响。
假设你的动画在13ms内就能够完成全部的渲染,在16帧率以内。而后某一请求致使了layout,花费了5ms的时间。该layout在下一帧绘制前会发生,那么总的绘制时间就会达到18ms,最终你的动画就明显的跳过一帧。当动画过程当中须要进行layout的时候,为了不这种状况,能够在动画启动前进行layout或者延迟layout到动画完成。固然,尽可能为那些不会触发layout的属性添加动画。好比,View的translationX
和tanshlationY
属性影响到post-layout属性。LayoutParams 属性也会须要请求layout操做,所以对这些属性进行动画的时候在相对复杂的UI上会致使卡顿。网络
Inflation
View 填充也只会发生在UI Thread,也是比较繁重的操做(View的层级越大,工做越繁重)。填充工做会在手动填充一个View 或者启动一个activity的时候发生。这些都是在相同的UI线程进行的,当新的activity被填充的时候将会致使动画暂停。为了不这种状况,能够在动画完成的时候再启动Activity。或者避免滚动列表时填充不一样类型的View致使的卡顿,能够考虑预填充。好比,RecyclerView支持使用RecycledViewPool来装载不一样的View类型。并发
View的填充比较耗资源。不只要解析资源数据,还要实例化潜在的View以及它们所须要的数据,包括第一次须要的decode bitmap,layout和draw。UI越复杂,填充操做就会越繁重。app
上述全部都会下降启动速度。当用户启动一个应用时时,但愿等获得一个及时的反馈说明应用已经在运行了。Android经过使用了一个“Starting Window”来弱化这种问题,该window经过应用主题和一些指定背景的图片构成。这样能够很好的让系统进程在后台去进行加载和填充工做。当activity准备好展现的时候,starting window就会过渡到真实的内容紧接着用户就可使用应用了。异步
然而,这种starting window应该给用户更多的反馈以代表应用正在进行一些处理,固然这种策略不足以知足那种须要2秒甚至跟多时间去启动的应用,用户会被动的坐在那里一直等到彻底加载完毕。工具
为了启动更快,一些不须要当即展现的UI能够延迟加载。经过使用ViewStub能够搞定。任什么时候候都尽量的避免繁重的操做,好比进行大bitmap的decoding,避免因为内存分配和回收产生的内存搅动。可使用工具监视启动时间去解决瓶颈问题。布局
避免在Application对象中初始化代码。Application在每一次进程启动的时候会被建立,会致使更多的工做而占用了实际须要展现给用户的UI的初始化时间。好比用户正在浏览一张图片,决定share,选中了你的app,那么你的app须要作的就是展现给用户分享的UI,其它都是多余的。Application的子类更倾向在某些状况下须要作一些耗时的操做,建议你选择使用singletons去持有公共全局的状态,这样就会在它第一次被访问的时候进行初始化。有一点相关的注意事项,不要在Application对象中进行网络有关的操做。That object may be created when one of the app’s Services or BroadcastReceivers is started; hitting the network will turn code that does a local update at a specific frequency into a regular DDoS.post
还要注意,应用的不一样状态对于启动时间有一个很大的区别。若是应用第一次启动,那么就会作大量的工做:启动进程,初始化全部的状态,必要的填充,布局和绘制。若是应用已经启动了而且在后台存活,从新启动就就很简单。这两种极端的例子会有另外两种状况出现,一:应用在用户退出后还存在,可是任务须要从新建立(经过调用Activity.onCreaate()
),二:进程被系统干掉了,须要从新启动该进程,可是任务能够在Activity.onCreate()方法中经过保存的bundle恢复状态。你在进行应用启动时间测试的时候,确保优化最糟糕的状况:进程被干掉,须要从新启动。你能够经过从任务列表中移除你的应用来模拟这种状况。
布局越复杂,操做的时间就会越长:填充,布局和渲染(一些潜在的无用内容的内存开销,自定义View中多余数据的引入)。寻找最节省资源的方式去展现嵌套的内容。一种方法就是使用自定义View或者自定义布局,在自定义布局中去避免复杂的嵌套,对于一个单独的View来讲绘制一些text和icons,相对于一个嵌套的ViewGroup就更简单。如何在一个交互模块中绑定多个元素呢?若是用户能够经过一个元素就能够完成交互,那么该元素应该是一个独立的View,而不是和其它元素绑定在一块儿。
RelativeLayout使用起来很方便,由于能够任意指定View的相对位置。在不少时候,多是最好的选择,可是相对布局是消耗资源的一种方案,由于它须要两次measurement去确保本身处理了全部的布局关系。并且这个问题会伴随着View层级中的ReativeLayout的增多,而变得跟严重。想象一下,一个顶部是RelativeLayout的布局,原本就进行两次的measurement工做,若是它的第一个child也是RelativeLayout,那么该chilld RelativeLayout下面的布局也要进行两次measurement,整个布局就要进行4次measurement。
在不须要RelativeLayout的一些属性的时候,能够选择使用其它的布局类型。好比LinearLayout或者自定义的布局。确实须要对child进行相对布局的时候,能够考虑更优化的GridLayout,它已经预处理了Child View的关系,能够避免double-measurement的问题。
在UI Thread中复杂的操做会致使动画和绘制的延迟,最终会致使明显的卡顿。一些已知的应该避免耗时操做的方法:onDraw
,onLayout
,以及任何与View相关的在UI thread调用的相关方法。还有一些其它的操做,好比webservice的调用,网络操做以及数据库的操做。能够考虑使用Loaders或者其它执行在其它线程的工具去操做,完成后再填充到UI上。一个能够追踪卡顿缘由的工具是StrictMode
。
另外一个在UI Thread中避免访问文件系统和数据库的缘由是:Android设备的存储在处理多个并发的读写操做时支持的不够好。即便你的app处理空闲状态,可是其它的app可能正在执行繁重的I/O操做(Play Store更新apps)也可能会致使你的应用产生ANR或者一些比较大的延迟。
总的来讲,全部的事情都应该是异步的,UI Thread应该只操做那些核心的UI 操做,好比处理View的属性和绘制。
BroadcastReceivers能够用于从其它应用接收那些指望响应的信息和事件。可是过多的响应以致于超过了自己所需的话,这些事件就会致使app常常被唤醒,最终致使整个设备的性能和资源的耗费。当你的应用不须要关心这些结果时,考虑关闭BroadcastReceivers,而且慎重选择那些要响应的Intent。
大多数用户的手机比开发者手机的配置要低。所以去为这个市场的用户开发就很重要。在关注性能问题的时候,不要以本身的手机水平做为衡量标准,使用不一样档次的手机进行测试,确保你的应用能够知足不一样水平的设备。
低端手机的一些关注点还包括一些RAM的大小,屏幕的大小,好比512M的RAM或者768*480的屏幕分辨率的配置在低端手机中很常见。
使用Android提供的一些测试工具去追踪重要的性能相关的信息:渲染性能(是否达到60的帧率?),内存分配(内存分配是否致使垃圾回收最终致使动画的卡顿?),启动性能(在第一启动的时候是否作了太多的工做,致使用户等过久?)找到这些问题,解决它们!