介绍

Android 中的 Java 代码会需要经过编译优化再执行的过程。代码的不同写法会影响到 Java 编译器的优化效率。例如 for 循环的不同写法就会对编译器优化这段代码产生不同的效率,当程序中包含大量这种可优化的代码的时候,运算性能就会出现问题。想要知道如何优化代码的运算性能就需要知道代码在硬件层的执行差异。

1. 低效率函数

如果你写了一段代码,它的执行效率比想象中的要差很多。我们需要知道有哪些因素有可能影响到这段代码的执行效率。例如:比较两个 float 数值大小的执行时间是 int 数值的 4 倍左右。这是因为 CPU 的运算架构导致的,如下图所示:


虽然现代的 CPU 架构得到了很大的提升,也许并不存在上面所示的那么大的差异,但是这个例子说明了代码写法上的差异会对运算性能产生很大的影响。

通常来说有两类运行效率差的情况:第 1 种是相对执行时间长的方法,我们可以很轻松的找到这些方法并做一定的优化。第 2 种是执行时间短,但是执行频次很高的方法,因为执行次数多,累积效应下就会对性能产生很大的影响。

修复这些细节效率问题,需要使用 Android SDK 提供的工具,进行仔细的测量,然后再进行微调修复。

2.Traceview

前面已经讲过,这里就不在介绍了。

3. 计算性能优化

(1). 批处理与缓存

为了提升运算性能,这里介绍 2 个非常重要的技术: 批处理与缓存
是在真正执行运算操作之前对数据进行批量预处理,例如你需要有这样一个方法,它的作用是查找某个值是否存在与于一堆数据中。假设一个前提,我们会先对数据做排序,然后使用二分查找法来判断值是否存在。我们先看第一种情况,下图中存在着多次重复的排序操作。



在上面的那种写法下,如果数据的量级并不大的话,应该还可以接受,可是如果数据集非常大,就会有严重的效率问题。那么我们看下改进的写法,把排序的操作打包绑定只执行一次:


上面就是批处理的一种示例:把重复的操作拎出来,打包只执行一次。
缓存相对比较简单,就是把经常处理的数据先缓存起来,使用时直接获取,但是要注意,当代码执行完毕需要回收(当然也可以使用 weakRefrence)。

(2). 主线程阻塞

为了确保应用程序的高性能,每项功能都应该尽可能高效地运行。但是这些功能的执行时间以及它们在代码中所处的位置也很重要,当你首次启动一个 Android 应用程序时,朱执行线程就已经创建了,主线程非常重要,因为它负责运行你的代码,并在合适的视图位置发送事件和执行绘图功能。这些前面我们已经讲过,基本上来说,主线程是应用程序所在的线程,有时候,主线程也称为 UI 线程。例如,如果你触摸屏幕上的按钮,UI 线程将会发送一个触摸事件给视图,视图将按钮状态设定为已按下设定,然后向事件队列发送一个有效请求,然后 UI 线程处理此请求,并通知按钮将其本身绘制为已按下状态。如果你有任何触摸事件的处理代码块,将会在线程中执行,这些触摸处理所用的时间越长,线程的执行时间就会越长,在绘图功能执行完之前,视图将会更新显示状态,让用户能够看到其状态,这里需要记住的是,输入处理代码与渲染和更新代码,共享这个线程的处理周期时间。



这意味着,在触摸事件处理,网络访问或数据库查询等计算周期时间,UI 不会更新绘图,在简单的情况下,渲染周期可能会延误 16 毫秒左右,而让用户感到延迟。但是,如果你暂停 UI 线程渲染超过 5 秒,用户将会看到“应用程序未响应”对话框,并询问用户是否会想要关闭你的应用程序,这样可能导致用户停止使用。那你如何解决这个问题,你要找出不需要在主线程上执行的功能,也就是说,不需要等它们完成之后,才能执行绘图。你应该将这个功能转移到一个单独的独立线程,这个线程不会阻止 UI 线程。例如,如果你按一下提交按钮,以完成一个订单,然后编写和发送确认邮件,


这可以在单独的线程上完成。Android 有系列很好用的 API,能够简化这些工作。

(3). 异步任务

给耗时操作建立异步任务,比如网络请求,图片处理等等,任务完成通知主线程。

(4). 容器性能

前面我们讲过,一些类型的硬件可能会造成程序执行速度较慢,还记得那个浮点分支问题吗?对于今天的硬件来说,这已经不是问题。但是有一些问题还是需要引起注意,比如说,你所使用的编程语言的基本元素的效率,以排序等基本算法为例,现在,有很多的排序算法,对于不同的情况,它们各有优劣,例如,当元素数量少于一千或在大型已排序列表中寻找一个对象时,快速排序法通常比起冒泡排序法更快。一般情况下,最好的方法是二分查找算法,但是,当在未排数组中寻找对象时情况变得完全不同,不同于比较每一个对象以查找你想要的值。你可以使用一个哈希函数来立即找到它,这是现代计算机科学和数据结构方面的基本知识。

幸运的是,现代编程语言像 Java 等,为你提供了这些容器和算法,因此你不再需要自己反复地编写 Murmur3 哈希函数和快速排序算法。但是你需要知道另外一些事情,在我多年的编程生涯中,一个经常会影响项目性能的问题,是由于这些语言提供的容器对象的性能所引起的。这听起来不可思议!Java 提供一个矢量类的实现,你可以任意 push、pop,添加和取消对象,为了获得这种灵活性,它在内部使用链式列表结构,这种结构具有一系列独特的性能特性,在你操作这种列表时,它的速度超级快,但是,当你在其他位置进行插入或删除时,它会消耗大量的时间。我要说的是,底层系统提供的这些容器并不会考虑,你的程序将会如何实际使用它们,James Sutherland 发表了一系列的基准测试报告,他认为,我们需要注意性能与功能之间的一些差异。例如,他发现 Hashtable 比 HashMap 大约快 22%,具体视你如何使用这些容器而有所不同,我们需要思考的是,你是否曾经分析过你在代码中使用的容器类。你是否坚信,你在代码中使用的容器的实际运行速度绝对是最快的。一个好消息是,你可以使用 Android 中的 MPI 来剖析这些容器的性能。



(5). 数据结构

创建应用时,容器中不恰当的数据结构所造成的性能问题,为此我们可以使用 Android SDK 中的工具,来识别不恰当数据结构带来的性能问题。



  • Android

    开放手机联盟(一个由 30 多家科技公司和手机公司组成的团体)已开发出 Android,Android 是第一个完整、开放、免费的手机平台。

    293 引用
感谢    赞同    分享    收藏    关注    反对    举报    ...