0°

提高Android效率的建议

内容预览:
  • 这样的方法调用是很昂贵,甚至超过上文所说的域查找~
  • 对于ArrayList,使用手写的for循环能比使用增强型for循环快3倍左右(无论...~
  • 2.11总是量化 在开始优化之前,请确实你是真的有一个需要解决的问题~

始发于微信公众号: 程序员小乐

分享编程技能、互联网技术、生活感悟、打造干货分享平台,将总结的技术、心得、经验分享给大家,这里不只限于技术!值得你去关注,点击上方 蓝字 快速关注。本号支持 投稿



  一、简介



提高Android效率的建议


本文档介绍了关于提高Android程序效率的一些建议。读者应当将这些建议融入到编程的习惯当中。关于如何写出高效的代码,有以下两条基本原则:


  • 不要进行没有必要的工作

  • 如果能够避免,不要进行内存的管理分配。


    当你进行Android app的微型优化时,一件难题之一是让你的app确保运行在不同的硬件设备上。不同版本的虚拟机(VM)运行在不同速度的处理器上。不仅仅是硬件设备的差异,同一设备是否使用JIT[1]都会存在巨大的差异。使用JIT设备上的高效代码并不总是不使用JIT设备的高效代码。


      二、优化建议



    接下来将逐条介绍官方文档中的优化建议。


    2.1避免创建不必要的对象(Avoid Creating Unnecessary Objects)


    对象的创建永远都不是免费的。通常情况下带线程管理池的垃圾收集器能够使得临时对象的管理更加廉价。但是管理内存总是比不管理内存要昂贵。

    当你分配越来越多的对象时,你将会迫于定期的垃圾收集机制,使得用户体验遇到一些小麻烦。在Android 2.3中介绍了并发垃圾收集器,但是不必要的工作总是应当避免的。


    因此你应当避免创建不必要的对象实例。以下的一些例子会有所帮助:


    • 如果你有一个方法返回一个字符串,并且你知道无论如何返回的结果总是会被追加到StringBuffer后面。代替创建一个短期的临时对象,直接进行追加。

    • 当从输入数据集中取出字符串时,尽量尝试从原数据中返回子字符串,而不是创建一个拷贝。

    一个更激进的想法是,将多维数组分割成平行的单维数组。

    • 一个int数组好于一个Integer数组。这也同样可以概括为两个平行的int数组比一个(int,int)数组更为有效。该结论适用于其他基本数据类型的组合。

    • 如果你需要实现一个存储元组(Foo,Bar)对象的容器,尽量使用两个平行的数组Foo[]和Bar[],这样会好于单个的自定义数组(Foo,Bar)。(例外情况是,当你为其他代码设计API时,通常情况需要对速度做一些小的妥协从而获得更好的API设计。但是在你自己的代码中,你应当尝试尽量使代码尽可能高效。)


      通常情况下,尽可能避免创建短期临时的对象。创建的对象越少,意味着垃圾的收集频率越少,这将直接影响到用户体验。


      2.2考虑静态方法


      如果你不会访问到对象的内部域,那就把该方法变成static吧。这样的调用将会提高15%~20%的速度。这也是一个好的惯例,因为你能够区别该方法是否会修改方法内对象的状态。


      2.3常量使用Static Final


      考虑下面例子中的类顶部声明:


      提高Android效率的建议


      编译器生成一个被称作clinit的类初始化方法,这将会在该类第一次被使用时执行。该方法为intVal存储值32,并在类文件字符串常量表中为strVal取出一个引用。当这些值在稍后被提及时,它们将会通过域查找的方式被访问。

      我们可以使用final关键字进行改进:

      提高Android效率的建议


      这样一来,该类不再需要clinit方法,因为这些常量被放置在dex文件的静态域初始化器中。涉及intVal的代码将会直接指向整型值42,而strVal的访问将会使用一个相对廉价的“字符串常量”指令来代替欲查找的方式。



      2.4避免内部的get()/set()方法


      在类似于C++这样的原生语言,有一个常见的惯例是,使用get方法(i=getCount())代替直接进入域(i=count)。对于C++来说这是一个非常好的习惯,并常常在其他面向对象语言中也是如此,例如C#和JAVA,因为编译器通常会内联访问,如果你想要对成员的访问进行限制,你可以在任何时候这样去编写代码。


      但是,这一点对于Android来说并不是很好。这样的方法调用是很昂贵,甚至超过上文所说的域查找。遵循常见的面向对象编程惯例,使用get/set的公共接口固然是合理的,但是在类的内部你应当总是直接访问成员变量。


      没有JIT的情况下,直接访问会比琐碎的get方法调用快3倍左右。使用JIT的情况下,直接访问会比调用get方法快7倍左右。


      请注意,如果你使用ProGuard,那么直接访问成员将会更加受益,因为ProGuard能够进行内联访问。


      2.5使用增强型For循环语法


      增强型for循环(有时也被称为“for-each”循环)能够用于数组,以及实现Iterable接口的数据集合中。对于ArrayList,使用手写的for循环能比使用增强型for循环快3倍左右(无论是否使用JIT)。但是对其他的集合而言,增强型for循环是一种清晰的迭代器用法。


      对于数组的迭代,有如下几种可选项:


      提高Android效率的建议


      • zero()是最慢的。因为JIT并不能优化每次循环中获取数组长度的成本。

      • one()会较快一些。它使用局部变量获得每一项传入参数,避免了查找的成本。一次性获取数组的长度会使得性能收益。

      • 不使用JIT的情况下,two()是最快的。在不使用JIT的情况下,性能则与one()难分伯仲。该方法中使用了增强型for循环。


        因此,默认情况下,你应当使用增强型for循环。但是遇到ArrayList时,请考虑手写循环的方式。


        2.6考虑使用包级访问来代替私有内联类的访问


        考虑如下类的定义:


        提高Android效率的建议


        这里的关键在于,我们定义了一个私有内联类(Foo$Inner),该内部类中直接访问了外部类中的私有方法和私有成员变量。这是合法的,该代码的运行结果将会打印出“Value is 27”,这也是我们所期望的。


        问题在于,虚拟机认为直接从Foo$Inner中访问Foo的私有成员是非法的,因为Foo和Foo$Inner是两个不同的类,即使Java语言允许一个内部类可以访问外部类的私有成员。为了解决该问题,编译器生成了一组方法:


        提高Android效率的建议


        当内部类需要访问外部类中的mValue或调用外部类的doStuff()时,内部类代码会调用这些静态方法。这意味着上述代码确实可以归结为内部类访问外部私有成员的方法。前文中我们讨论过get/set这样的方法是慢于直接访问成员的。所以这是一个导致隐形性能损失的确切案例。


        如果你在性能要求较高的地方使用这样的代码,你可以通过声明被内部类访问的方法和变量来进行包级访问,而不是私有成员访问。不幸的是,这意味着成员能够直接被同一包下的其他类所访问,因此在公共API中,你不应该这么做。


        2.7避免使用Float


        在Android设备中,float比int慢两倍左右。

        就速度而言,float和double在当今的绝大多数硬件设备中并没有什么区别。至于空间方面,double是float的两倍。和桌面设备一样,不考虑内存空间,你应当优先选择double,都不是float。


        2.8使用库


        除了常见的优先使用类库而不是自己重复造轮子的原因之外,请记住系统能够使用汇编语言自由地替换类库的方法调用,这可能会比使用JIT所能够做的最佳代码还要好。一个典型的案例是String.indexOf()和相关的API。Dalvik虚拟机会使用内部的原有代码进行替换。类似地,在使用JIT的Nexus One上,System.arraycopy()方法会比手写的循环快9倍左右。


        2.9谨慎使用原生方法(Native Methods)


        使用原生语言的NDK开发Android应用并不一定比使用JAVA编程更高效。比如,原生语言和JAVA之间的转化需要一定的成本,并且JIT无法对这两边进行优化。如果你正在使用原生资源,这很显然比直接使用资源集要困难的多。你需要为你想要执行的每一个处进行编译。你可能甚至不得不编译多个版本,比如为G1的ARM处理器进行编译的版本并不能完全利用Nexus One的ARM处理器,并且为Nexus One的ARM处理器编译的版本不能运行在G1上面。

        原生代码主要用于,当你已经持有原生的代码库,并想把它导入Android中,而并不是用于提高Java所编写部分的速度。


        2.10性能神话


        在不使用JIT的设备中,通过具体类型的变量调用方法比调用接口会更高效。(例如,调用HashMap map的方法会比调用Map map的方法更高效,即使在两者中map都是HashMap)。这并不是指会造成2倍的速度差异,实际的差异更接近于6%。此外,JIT使得二者间性能的差异几乎难以分辨。

        在不使用JIT的设备中,缓存会比重复访问变量快20%左右。使用JIT时二者的成本几乎是相同的。所以并不值得进行优化,除非你想让你的代码更加易读。


        2.11总是量化


        在开始优化之前,请确实你是真的有一个需要解决的问题。确定你能清楚地量化现有的性能,否则的话你无法估量各种优化方式对性能的影响。


          总结



        最后,再将本文档所阐述的优化建议概述如下:


        • 避免多余对象的创建

        • 使用静态方法定义不会访问到对象内部的函数

        • 使用Static Final定义常量

        • 避免在类内部使用get/set方法

        • ArrayList不使用增强型for循环

        • 使用包级访问代替私有内联类的访问

        • 使用double代替float

        • 优先使用第三方类库而不是重复造轮子

        • 谨慎使用NDK

        • 不要执迷于性能神话

        • 量化性能以及优化方法性能的量化,从而选择合适的优化方式


        JIT : Just In Time Compiler 又译及时编译、实时编译,动态编译的一种形式,是一种提高程序运行效率的方法。


        提高Android效率的建议


        如何您想进技术群和大牛们交流,关注公众号在后台回复 “加群”,或者 “学习” 即可

        作者:Chuckiefan

        链接:http://www.jianshu.com/p/2a343de95c7a

        著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

        如果您觉得不错,请别忘了分享到您的朋友圈让更多的人看到!! 您的举手之劳,就是对我最好的支持,非常感谢!

        每日英文


        Don’t forget the things you once you owned.Keep those lost things in memory.Don’t give up the things that belong to you and treasure the things you can’t get. 

        曾经拥有的,不要忘记。已经失去的,留作回忆。 属于自己的,不要放弃,不能得到的,更要珍惜。


        乐乐有话说


        努力做一个可爱的人。不埋怨谁,不嘲笑谁,也不羡慕谁,阳光下灿烂,风雨中奔跑,做自己的梦,走自己的路。



        提高Android效率的建议


        推荐阅读




        知识星球
        这里聚集了业界内的大牛,值得各位大牛的加入!

        提高Android效率的建议 

        看完本文有收获?请转发分享给更多人
        关注「杨守乐」,提升编程技能

        【QQ技术群】279126311 []
        【QQ技术群】484572225 [未]

        以上就是:提高Android效率的建议 的全部内容。

        本站部分内容来源于互联网和用户投稿,如有侵权请联系我们删除,谢谢。
        Email:[email protected]


        0 条回复 A 作者 M 管理员
          所有的伟大,都源于一个勇敢的开始!
        欢迎您,新朋友,感谢参与互动!欢迎您 {{author}},您在本站有{{commentsCount}}条评论