Google Developing for Android 三 - Performance最佳实践

发表于 2015-06-07   |   分类于 Android最佳实践

原文 Developing for Android, III:The Rules: Performance

在Android中,性能和内存的关系很密切,因为系统的整体内存大小会影响所有进程的性能,因为垃圾回收器会对运行期间的性能产生很大的影响。下面的重点是运行期间的性能问题而不是内存。

避免在动画和交互期间繁重的操作

正如在第一篇文章中提到过的,在UI Thread做繁重的操作会影响到渲染的处理。同样会导致动画的问题,因为它依赖于每一帧的渲染。这就意味着在动画期间避免在UI进行繁重的操作就更加重要。以下是一些可以避免的常见情况:

  • Layout
    Measurement 和 layout是比较繁重的操作,view的层级越复杂,操作就会越繁重。Measurement和layout是在UI Thred发发生的。因此当系统需要运行一个动画的时候紧接着还需进行layout,而它们都是在同一个线程,因此动画的流畅度可能就会受到影响。
    假设你的动画在13ms内就可以完成所有的渲染,在16帧率之内。然后某一请求导致了layout,花费了5ms的时间。该layout在下一帧绘制前会发生,那么总的绘制时间就会达到18ms,最终你的动画就明显的跳过一帧。当动画过程中需要进行layout的时候,为了避免这种情况,可以在动画启动前进行layout或者延迟layout到动画完成。当然,尽量为那些不会触发layout的属性添加动画。比如,View的translationXtanshlationY属性影响到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越复杂,填充操作就会越繁重。

上述所有都会降低启动速度。当用户启动一个应用时时,希望等得到一个及时的反馈说明应用已经在运行了。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.

还要注意,应用的不同状态对于启动时间有一个很大的区别。如果应用第一次启动,那么就会做大量的工作:启动进程,初始化所有的状态,必要的填充,布局和绘制。如果应用已经启动了并且在后台存活,重新启动就就很简单。这两种极端的例子会有另外两种情况出现,一:应用在用户退出后还存在,但是任务需要重新创建(通过调用Activity.onCreaate()),二:进程被系统干掉了,需要重新启动该进程,但是任务可以在Activity.onCreate()方法中通过保存的bundle恢复状态。你在进行应用启动时间测试的时候,确保优化最糟糕的情况:进程被干掉,需要重新启动。你可以通过从任务列表中移除你的应用来模拟这种情况。

避免复杂的View层级

布局越复杂,操作的时间就会越长:填充,布局和渲染(一些潜在的无用内容的内存开销,自定义View中多余数据的引入)。寻找最节省资源的方式去展示嵌套的内容。一种方法就是使用自定义View或者自定义布局,在自定义布局中去避免复杂的嵌套,对于一个单独的View来说绘制一些text和icons,相对于一个嵌套的ViewGroup就更简单。如何在一个交互模块中绑定多个元素呢?如果用户可以通过一个元素就可以完成交互,那么该元素应该是一个独立的View,而不是和其它元素绑定在一起。

避免在View层级的顶层使用RelativeLayout

RelativeLayout使用起来很方便,因为可以任意指定View的相对位置。在很多时候,可能是最好的选择,但是相对布局是消耗资源的一种方案,因为它需要两次measurement去确保自己处理了所有的布局关系。而且这个问题会伴随着View层级中的ReativeLayout的增多,而变得跟严重。想象一下,一个顶部是RelativeLayout的布局,本来就进行两次的measurement工作,如果它的第一个child也是RelativeLayout,那么该chilld RelativeLayout下面的布局也要进行两次measurement,整个布局就要进行4次measurement。

在不需要RelativeLayout的一些属性的时候,可以选择使用其它的布局类型。比如LinearLayout或者自定义的布局。确实需要对child进行相对布局的时候,可以考虑更优化的GridLayout,它已经预处理了Child View的关系,可以避免double-measurement的问题。

避免在UI Thread 进行繁重的操作

在UI Thread中复杂的操作会导致动画和绘制的延迟,最终会导致明显的卡顿。一些已知的应该避免耗时操作的方法:onDrawonLayout,以及任何与View相关的在UI thread调用的相关方法。还有一些其它的操作,比如webservice的调用,网络操作以及数据库的操作。可以考虑使用Loaders或者其它执行在其它线程的工具去操作,完成后再填充到UI上。一个可以追踪卡顿原因的工具是StrictMode

另一个在UI Thread中避免访问文件系统和数据库的原因是:Android设备的存储在处理多个并发的读写操作时支持的不够好。即使你的app处理空闲状态,但是其它的app可能正在执行繁重的I/O操作(Play Store更新apps)也可能会导致你的应用产生ANR或者一些比较大的延迟。

总的来说,所有的事情都应该是异步的,UI Thread应该只操作那些核心的UI 操作,比如处理View的属性和绘制。

最小化 Wakeups

BroadcastReceivers可以用于从其它应用接收那些期望响应的信息和事件。但是过多的响应以至于超过了本身所需的话,这些事件就会导致app经常被唤醒,最终导致整个设备的性能和资源的耗费。当你的应用不需要关心这些结果时,考虑关闭BroadcastReceivers,并且慎重选择那些要响应的Intent。

为低端手机考虑

大多数用户的手机比开发者手机的配置要低。因此去为这个市场的用户开发就很重要。在关注性能问题的时候,不要以自己的手机水平作为衡量标准,使用不同档次的手机进行测试,确保你的应用可以满足不同水平的设备。

低端手机的一些关注点还包括一些RAM的大小,屏幕的大小,比如512M的RAM或者768*480的屏幕分辨率的配置在低端手机中很常见。

使用Android提供的一些测试工具去追踪重要的性能相关的信息:渲染性能(是否达到60的帧率?),内存分配(内存分配是否导致垃圾回收最终导致动画的卡顿?),启动性能(在第一启动的时候是否做了太多的工作,导致用户等太久?)找到这些问题,解决它们!

Google Developing for Android 三 - Performance最佳实践的更多相关文章

  1. Google Developing for Android 二 - Memory 最佳实践 // lightSky‘Blog

    Google Developing for Android 二 - Memory 最佳实践   |   分类于 Android最佳实践 原文:Developing for Android, II Th ...

  2. 给HTML初学者的三十条最佳实践

    Nettuts +运营最困难的方面是为很多技能水平不同的用户提供服务.如果我们发布太多高级教程,我的新手用户将无法从中受益.相反也是如此.我们尽我们最大的努力,但如果你觉得你被忽略了请联系我们.这个网 ...

  3. Google Developing for Android 一 - 相关上下文介绍

    前几天在G+上看到Google Developers站点,有一个Android系列的文章,分享到个人微博,周末闲来没事就学写了下,把它们简单的翻译了下,没想到一发不可收拾,六篇文章全部都翻译完了,有些 ...

  4. 【机器学习】Google机器学习工程的43条最佳实践

    https://blog.csdn.net/ChenVast/article/details/81449509 本文档旨在帮助那些掌握机器学习基础知识的人从Google机器学习的最佳实践中获益.它提供 ...

  5. 来自Google资深工程师的API设计最佳实践

    来自Google资深工程师Joshua Bloch的分享:API设计最佳实践 为什么API设计如此重要?API是一个公司最重要的资产. 为什么API的设计对程序员如此重要? API一旦发布,出于兼容性 ...

  6. (转)Android开发:性能最佳实践-管理应用内存

    翻自:http://developer.android.com/training/articles/memory.html 在任何软件开发环境中,RAM都是宝贵的资源,但在移动操作系统中更加珍贵.尽管 ...

  7. Android 组件化最佳实践 ARetrofit 原理

    本文首发于 vivo互联网技术 微信公众号 https://mp.weixin.qq.com/s/TXFt7ymgQXLJyBOJL8F6xg作者:朱壹飞 ARetrofit 是一款针对Android ...

  8. 执行Android后台任务的最佳实践

    灵活执行后台任务可以帮助提升应用性能,并最小化电量损耗. Android后台任务主题包含以下三个子主题: 1. 在IntentService中执行后台任务: 2. 使用CursorLoader在后台加 ...

  9. Android+PHP开发最佳实践

    本书以一个完整的微博应用项目实例为主线,由浅入深地讲解了Android客户端开发和PHP服务端开发的思路和技巧.从前期的产品设计.架构设计,到客户端和服务器的编码实现,再到性能测试和系统优化,以及最后 ...

随机推荐

  1. jsp的九大内置对象和四大作用域(转)

    定义:可以不加声明就在JSP页面脚本(Java程序片和Java表达式)中使用的成员变量 JSP共有以下9种基本内置组件(可与ASP的6种内部组件相对应): 1.request对象(作用域)  客户端的 ...

  2. 一次 surface pro 3 的售后保修 黑色三月维权(HSD)

    已更新结束....原创 半根毛线 博文原址 http://www.cnblogs.com/hsd-/ 发现大量转载 转载请与作者联系 drizzle1996@outlook.com或注明转载 ---- ...

  3. Android下载更新代码

    其实是昨天反编译一个apk,给它添加一个自动更新的功能用到的.为了在smali下方便查看,代码写的不规范,反正到了smali都一个吊样~~~~ 权限: <uses-permission andr ...

  4. js中属性和方法的类型和区别

    对象的属性:私有属性(var).类属性(静态属性).对象属性(this).原型属性(prototype). 对象的方法: 私有方法(funtion).类方法(静态方法).对象方法(this).原型方法 ...

  5. Clojure web初探

    项目环境:3.2.0-52-generic #78-Ubuntu SMP Fri Jul 26 16:21:44 UTC 2013 x86_64 x86_64 x86_64 GNU/LinuxLein ...

  6. java数组引用

    public class Arriy { public static void main(String args[]){ int data[]=new int[3]; data[0]=10; data ...

  7. 【转载】让你的MATLAB代码飞起来

    原文地址:http://developer.51cto.com/art/201104/255128_all.htm MATLAB语言是一种被称为是"演算纸"式的语言,因此追求的是方 ...

  8. js问题

    1.原型链问题 1.js中万物皆对象,但对象也分为普通对象和函数对象,Object,Function都是js自带的函数对象,凡是通过 new Function() 创建的对象都是函数对象,其他的都是普 ...

  9. oracle 块的学习——有定义和执行部分的块

    declare --定义变量 v_ename varchar2(5); begin --执行部分 select ename from emp where empno=&no; --在控制台显示 ...

  10. Codeforces Round #361 Jul.6th A题 ☺译

    A.迈克和手机 当迈克在沙滩上游泳的时候,他意外的把他的手机扔进了水里.不过你甭担心因为他立马买了个便宜些的代替品,这个代替品是老款九键键盘,这个键盘只有十个等大的数字按键,按以下方式排列: 1 2 ...