您的位置:首页 > 移动开发 > Android开发

如何在Android上编写高效的Java代码

2015-08-02 10:19 1501 查看
转自:http://www.ituring.com.cn/article/177180



作者/ Erik Hellman

Factor10咨询公司资深移动开发顾问,曾任索尼公司Android团队首席架构师,主导Xperia系列产品开发;精通移动应用、Web技术、云计算和三维图形,定期在DroidCon、JFokus、JavaOne和其他专业开发人员大会上发表演讲。关于Erik的更多信息,可访问他的博客http://blog.hellsoft.se

Java平台一般有三个版本:Java ME(微型版,用于某些手机)、Java SE(标准版,用于台式电脑)、Java EE(企业版,用于服务器端应用)。在谈到Java时,我们通常是指Java SE,因为只有这个版本包含虚拟机和编译器。

首先,Java代码会被编译成称为字节码的中间格式。当字节码在目标电脑上运行时,虚拟机会快速将它解析成目标电脑硬件和操作系统所需要的本机格式。

除了为开发者提供“一次编写,到处运行”的优势,Java还能通过垃圾回收器(GC)实现自动内存管理,开发者可免去手动在代码中释放无用对象的内存。虽然这个功能非常有用,且大大降低了在代码中引入内存问题的风险,但是它会增加运行时的开销,因为需要不停地执行垃圾回收进程。

本文开头将比较Java SE和用于Android开发的Java之间的差异。首先我会介绍开发者习惯的Java SE语言结构以及它们是如何在Android上运行的。其次,我会介绍如何优化Android中的Java代码,如何优化内存分配,以及如何恰当地处理多线程。


比较Android上的Dalvik Java和Java SE

虽然远在Android出现之前,开发者就能用Java编程语言为移动设备编写应用程序,但它只是Java中功能极为有限的一个版本,称为Java ME(微型版)。不同的移动设备还需编写不同的代码,因此,写一个应用程序就能在支持Java ME的任何手机上运行是几乎不可能的。此外,由于当时不存在很好的在线商店,应用发布过程极其复杂。

Android的问世为开发者提供了构建智能手机强大应用的机会,开发者只需用Java编程语言以及他们熟知的标准Java API编写代码。然而,尽管Android开发者仍使用Java SE编译器来编译应用程序,你会发现,James Gosling开发的Java和Android设备上的Java存在许多不同之处。

在Android设备上运行的VM(虚拟机)称为Dalvik。它最初由谷歌的Dan Bornstein开发,适用于CPU和内存受限的移动设备。Java SE和Dalvik Java存在一些差异,主要体现在虚拟机上。Java SE使用了栈机设计,而Dalvik被设计成了基于寄存器的机器。Android SDK中有一个dx工具,它会把Java SE栈机器的字节码转换成基于寄存器的Dalvik机器字节码,该转换步骤由IDE自动完成。

基于栈的虚拟机和基于寄存器的虚拟机的定义以及差异将不列入我们的讨论范围。由于历史原因,Android使用基于寄存器的虚拟机。虽然基于寄存器的虚拟机最多可以比基于栈的虚拟机快32%,但这只限于执行时解释字节码的虚拟机(也就是说,解释型虚拟机)。在Android 2.2版本(也称为Froyo)之前,Dalvik虚拟机都是纯解释型的。Froyo版本引入了JIT编译器(即时编译),这是Java SE很早就有的一个优势。

JIT编译,也称为动态翻译。它在执行前把字节码翻译成本机代码(如图1所示),这样主要有两个好处。首先,它消除了那些纯解释型虚拟机的开销;其次,它能对本机代码执行优化,这通常是静态编译代码无法做到的。例如,JIT编译器可以在它运行的CPU上选择最合适的优化,也可以根据应用程序的输入来分析代码是如何运行的,以便进行下一步的优化。



图1 Android Java和Java SE翻译步骤

虽然Android的Dalvik JIT编译器有很大的发展前景,但要达到如Java SE的JIT编译器般稳定、成熟度尚需很长一段时间。不过,Dalvik JIT的出现为Android提供了巨大的性能优势,而且它也在不断得以改善。

JAVA SE虚拟机和Dalvik虚拟机的另一个区别是,后者进行了优化,可运行在同一个机器上的多个实例中。它在开机时会启动一个叫做zygote的进程,该进程会创建第一个Dalvik实例,由这个实例创建所有其他的实例。当应用程序启动时,zygote进程会收到一个创建新虚拟机实例的请求,并给该应用程序创建一个新进程(如图2所示)。如果开发者已习惯于Java SE开发,这样的设计可能看起来不切实际,但它有一个很大的优势,可以避免由一个应用程序运行失败导致Dalvik虚拟机崩溃,继而引发多应用程序崩溃。



图2 在Android中启动新Dalvik虚拟机实例

Android和Java SE除了运行的虚拟机不同之外,它们实现API的方式也不一样。Android中属于java和javax包中的API都来自Apache Harmony(这是一个开源项目,旨在重新实现Java SE软件栈,该项目从2011年11月不再维护)。在开发方面,这些API和Java SE包中的类似,但也存在一些差别。例如,谷歌对
HttpUrlConnection
类进行了Java
SE版本中所没有的重大升级。

此外,Android平台移除了Java SE中无关的API。例如,Swing/AWT包被完全移除,因为Android使用不同的UI框架。其他被移除的API还有RMI、CORBA、ImageIO和JMX。它们或者被替换为特定的Android版本(在android包空间内),或者因为一些实际原因根本不存在。


优化Android上的Java代码

经过多年的改进,Java SE具备了一些简化编写复杂代码结构的新特性。其中的一些特性会让整个流程变得更简单,但开发者需要了解何时以及如何正确地使用它们。另外,由于Java SE大多用于服务器端开发(使用Java企业版的API),因而开发人员专门对服务器端Java代码进行了优化。注解和Java虚拟机对脚本语言的支持就是对服务器端开发进行优化的例证。虽然这些工具在构建后端开发时很强大,但在开发Android客户端代码时,这些特性的作用很小,甚至起反作用。Java开发者已经习惯于无限量的RAM和CPU,而Android开发需要密切关注性能和内存分配。简单地说,开发者需要使用稍微不同的方法对待Android和后端的开发。

然而,随着Android的首次发布,情况有所改变。曾经一些在Android上尽量不用的Java规范重新被推荐,这主要因为Android目前的JIT编译器解决了这些规范导致的性能问题。

本文将讨论编写Android应用程序需要了解的Java代码。我们不会深究Java编程语言的细节,而是重点关注对Android开发重要的东西。不过,开发者仍需了解,大多数适用于Java SE的规则和建议同样适用于Android和Dalvik虚拟机。


Android上的类型安全枚举

Java SE 5.0新增了许多方便开发者的新特性。其中最值得期待的是引入了类型安全枚举。枚举在代码中用来表示属于某一组的几个选择。在早期版本的Java中,可以用多个整型常量解决这个问题。虽然这在技术上可行,但是很容易出错。请看下面的代码:
<code style="padding: 0px; font-family: Menlo, Monaco, Consolas, 'Courier New', monospace; color: inherit; border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; background-color: transparent; border: 0px;"><span class="kwd" style="color:#008b;">public</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">class</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#2b91af;">Machine</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">public</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">static</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">final</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">int</span><span class="pln" style="color:#000000;"> STOPPED </span><span class="pun" style="color:#000000;">=</span><span class="pln" style="color:#000000;"> </span><span class="lit" style="color:#8000;">10</span><span class="pun" style="color:#000000;">;</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">public</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">static</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">final</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">int</span><span class="pln" style="color:#000000;"> INITIALIZING </span><span class="pun" style="color:#000000;">=</span><span class="pln" style="color:#000000;"> </span><span class="lit" style="color:#8000;">20</span><span class="pun" style="color:#000000;">;</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">public</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">static</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">final</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">int</span><span class="pln" style="color:#000000;"> STARTING </span><span class="pun" style="color:#000000;">=</span><span class="pln" style="color:#000000;"> </span><span class="lit" style="color:#8000;">30</span><span class="pun" style="color:#000000;">;</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">public</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">static</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">final</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">int</span><span class="pln" style="color:#000000;"> RUNNING </span><span class="pun" style="color:#000000;">=</span><span class="pln" style="color:#000000;"> </span><span class="lit" style="color:#8000;">40</span><span class="pun" style="color:#000000;">;</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">public</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">static</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">final</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">int</span><span class="pln" style="color:#000000;"> STOPPING </span><span class="pun" style="color:#000000;">=</span><span class="pln" style="color:#000000;"> </span><span class="lit" style="color:#8000;">50</span><span class="pun" style="color:#000000;">;</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">public</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">static</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">final</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">int</span><span class="pln" style="color:#000000;"> CRASHED </span><span class="pun" style="color:#000000;">=</span><span class="pln" style="color:#000000;"> </span><span class="lit" style="color:#8000;">60</span><span class="pun" style="color:#000000;">;</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">private</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">int</span><span class="pln" style="color:#000000;"> mState</span><span class="pun" style="color:#000000;">;</span><span class="pln" style="color:#000000;">

</span><span class="kwd" style="color:#008b;">public</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#2b91af;">Machine</span><span class="pun" style="color:#000000;">()</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
mState </span><span class="pun" style="color:#000000;">=</span><span class="pln" style="color:#000000;"> STOPPED</span><span class="pun" style="color:#000000;">;</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">}</span><span class="pln" style="color:#000000;">

</span><span class="kwd" style="color:#008b;">public</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">int</span><span class="pln" style="color:#000000;"> getState</span><span class="pun" style="color:#000000;">()</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">return</span><span class="pln" style="color:#000000;"> mState</span><span class="pun" style="color:#000000;">;</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">}</span><span class="pln" style="color:#000000;">

</span><span class="kwd" style="color:#008b;">public</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">void</span><span class="pln" style="color:#000000;"> setState</span><span class="pun" style="color:#000000;">(</span><span class="kwd" style="color:#008b;">int</span><span class="pln" style="color:#000000;"> state</span><span class="pun" style="color:#000000;">)</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
mState </span><span class="pun" style="color:#000000;">=</span><span class="pln" style="color:#000000;"> state</span><span class="pun" style="color:#000000;">;</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">}</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">}</span></code>


问题是,虽然这些常量是期望的,但是没有机制保证
setState()
方法接收不同的值。如果要在设置方法中添加检查,那么一旦得到的是非预期值,开发者就需要处理错误。开发者所需要的是在编译时检查非法赋值。类型安全的枚举解决了这个问题,如下所示:
<code style="padding: 0px; font-family: Menlo, Monaco, Consolas, 'Courier New', monospace; color: inherit; border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; background-color: transparent; border: 0px;"><span class="kwd" style="color:#008b;">public</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">class</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#2b91af;">Machine</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">public</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">enum</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#2b91af;">State</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
STOPPED</span><span class="pun" style="color:#000000;">,</span><span class="pln" style="color:#000000;"> INITIALIZING</span><span class="pun" style="color:#000000;">,</span><span class="pln" style="color:#000000;"> STARTING</span><span class="pun" style="color:#000000;">,</span><span class="pln" style="color:#000000;"> RUNNING</span><span class="pun" style="color:#000000;">,</span><span class="pln" style="color:#000000;"> STOPPING</span><span class="pun" style="color:#000000;">,</span><span class="pln" style="color:#000000;"> CRASHED
</span><span class="pun" style="color:#000000;">}</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">private</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#2b91af;">State</span><span class="pln" style="color:#000000;"> mState</span><span class="pun" style="color:#000000;">;</span><span class="pln" style="color:#000000;">

</span><span class="kwd" style="color:#008b;">public</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#2b91af;">Machine</span><span class="pun" style="color:#000000;">()</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
mState </span><span class="pun" style="color:#000000;">=</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#2b91af;">State</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">STOPPED</span><span class="pun" style="color:#000000;">;</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">}</span><span class="pln" style="color:#000000;">

</span><span class="kwd" style="color:#008b;">public</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#2b91af;">State</span><span class="pln" style="color:#000000;"> getState</span><span class="pun" style="color:#000000;">()</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">return</span><span class="pln" style="color:#000000;"> mState</span><span class="pun" style="color:#000000;">;</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">}</span><span class="pln" style="color:#000000;">

</span><span class="kwd" style="color:#008b;">public</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">void</span><span class="pln" style="color:#000000;"> setState</span><span class="pun" style="color:#000000;">(</span><span class="typ" style="color:#2b91af;">State</span><span class="pln" style="color:#000000;"> state</span><span class="pun" style="color:#000000;">)</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
mState </span><span class="pun" style="color:#000000;">=</span><span class="pln" style="color:#000000;"> state</span><span class="pun" style="color:#000000;">;</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">}</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">}</span></code>


注意在声明不同类型安全值的地方新加的内部枚举类。这在编译时就会解决非法赋值的问题,所以代码更不容易出错。

如果Dalvik虚拟机还没有JIT编译器优化代码,不建议在Android平台上使用枚举类型,因为和使用整型常量相比,这种设计带来的内存和性能损失更大。这就是为什么在一些老版本的Android API中还存在如此多的整型常量的原因。如今有了更强的JIT编译器以及一个不断改进的Dalvik虚拟机,开发者不必再担心这个问题,放心大胆地使用类型安全枚举即可。

然而,仍然存在一些情况使用整型常量是更好的选择。像
int
这样的Java基本类型,不会增加GC的开销。此外,Android
SDK中许多已有的API仍然依赖基本类型,比如
Handler
类——在这种情况下,你没有太多的选择。


Android中增强版的for循环

Java SE 5.0还引入了增强版的
for
循环,提供了一个通用的缩写表达式来遍历集合和数组。首先,比较以下五种方法:
<code style="padding: 0px; font-family: Menlo, Monaco, Consolas, 'Courier New', monospace; color: inherit; border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; background-color: transparent; border: 0px;"><span class="kwd" style="color:#008b;">void</span><span class="pln" style="color:#000000;"> loopOne</span><span class="pun" style="color:#000000;">(</span><span class="typ" style="color:#2b91af;">String</span><span class="pun" style="color:#000000;">[]</span><span class="pln" style="color:#000000;"> names</span><span class="pun" style="color:#000000;">)</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">int</span><span class="pln" style="color:#000000;"> size </span><span class="pun" style="color:#000000;">=</span><span class="pln" style="color:#000000;"> names</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">length</span><span class="pun" style="color:#000000;">;</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">for</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">(</span><span class="kwd" style="color:#008b;">int</span><span class="pln" style="color:#000000;"> i </span><span class="pun" style="color:#000000;">=</span><span class="pln" style="color:#000000;"> </span><span class="lit" style="color:#8000;">0</span><span class="pun" style="color:#000000;">;</span><span class="pln" style="color:#000000;"> i </span><span class="pun" style="color:#000000;"><</span><span class="pln" style="color:#000000;"> size</span><span class="pun" style="color:#000000;">;</span><span class="pln" style="color:#000000;"> i</span><span class="pun" style="color:#000000;">++)</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
printName</span><span class="pun" style="color:#000000;">(</span><span class="pln" style="color:#000000;">names</span><span class="pun" style="color:#000000;">[</span><span class="pln" style="color:#000000;">i</span><span class="pun" style="color:#000000;">]);</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">}</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">}</span><span class="pln" style="color:#000000;">

</span><span class="kwd" style="color:#008b;">void</span><span class="pln" style="color:#000000;"> loopTwo</span><span class="pun" style="color:#000000;">(</span><span class="typ" style="color:#2b91af;">String</span><span class="pun" style="color:#000000;">[]</span><span class="pln" style="color:#000000;"> names</span><span class="pun" style="color:#000000;">)</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">for</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">(</span><span class="typ" style="color:#2b91af;">String</span><span class="pln" style="color:#000000;"> name </span><span class="pun" style="color:#000000;">:</span><span class="pln" style="color:#000000;"> names</span><span class="pun" style="color:#000000;">)</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
printName</span><span class="pun" style="color:#000000;">(</span><span class="pln" style="color:#000000;">name</span><span class="pun" style="color:#000000;">);</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">}</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">}</span><span class="pln" style="color:#000000;">

</span><span class="kwd" style="color:#008b;">void</span><span class="pln" style="color:#000000;"> loopThree</span><span class="pun" style="color:#000000;">(</span><span class="typ" style="color:#2b91af;">Collection</span><span class="pun" style="color:#000000;"><</span><span class="typ" style="color:#2b91af;">String</span><span class="pun" style="color:#000000;">></span><span class="pln" style="color:#000000;"> names</span><span class="pun" style="color:#000000;">)</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">for</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">(</span><span class="typ" style="color:#2b91af;">String</span><span class="pln" style="color:#000000;"> name </span><span class="pun" style="color:#000000;">:</span><span class="pln" style="color:#000000;"> names</span><span class="pun" style="color:#000000;">)</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
printName</span><span class="pun" style="color:#000000;">(</span><span class="pln" style="color:#000000;">name</span><span class="pun" style="color:#000000;">);</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">}</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">}</span><span class="pln" style="color:#000000;">

</span><span class="kwd" style="color:#008b;">void</span><span class="pln" style="color:#000000;"> loopFour</span><span class="pun" style="color:#000000;">(</span><span class="typ" style="color:#2b91af;">Collection</span><span class="pun" style="color:#000000;"><</span><span class="typ" style="color:#2b91af;">String</span><span class="pun" style="color:#000000;">></span><span class="pln" style="color:#000000;"> names</span><span class="pun" style="color:#000000;">)</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
</span><span class="typ" style="color:#2b91af;">Iterator</span><span class="pun" style="color:#000000;"><</span><span class="typ" style="color:#2b91af;">String</span><span class="pun" style="color:#000000;">></span><span class="pln" style="color:#000000;"> iterator </span><span class="pun" style="color:#000000;">=</span><span class="pln" style="color:#000000;"> names</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">iterator</span><span class="pun" style="color:#000000;">();</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">while</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">(</span><span class="pln" style="color:#000000;">iterator</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">hasNext</span><span class="pun" style="color:#000000;">())</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
printName</span><span class="pun" style="color:#000000;">(</span><span class="pln" style="color:#000000;">iterator</span><span class="pun" style="color:#000000;">.</span><span class="kwd" style="color:#008b;">next</span><span class="pun" style="color:#000000;">());</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">}</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">}</span><span class="pln" style="color:#000000;">

</span><span class="com" style="color:gray;">// 不要在ArrayList上使用增强版的for循环</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">void</span><span class="pln" style="color:#000000;"> loopFive</span><span class="pun" style="color:#000000;">(</span><span class="typ" style="color:#2b91af;">ArrayList</span><span class="pun" style="color:#000000;"><</span><span class="typ" style="color:#2b91af;">String</span><span class="pun" style="color:#000000;">></span><span class="pln" style="color:#000000;"> names</span><span class="pun" style="color:#000000;">)</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">int</span><span class="pln" style="color:#000000;"> size </span><span class="pun" style="color:#000000;">=</span><span class="pln" style="color:#000000;"> names</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">size</span><span class="pun" style="color:#000000;">();</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">for</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">(</span><span class="kwd" style="color:#008b;">int</span><span class="pln" style="color:#000000;"> i </span><span class="pun" style="color:#000000;">=</span><span class="pln" style="color:#000000;"> </span><span class="lit" style="color:#8000;">0</span><span class="pun" style="color:#000000;">;</span><span class="pln" style="color:#000000;"> i </span><span class="pun" style="color:#000000;"><</span><span class="pln" style="color:#000000;"> size</span><span class="pun" style="color:#000000;">;</span><span class="pln" style="color:#000000;"> i</span><span class="pun" style="color:#000000;">++)</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
printName</span><span class="pun" style="color:#000000;">(</span><span class="pln" style="color:#000000;">names</span><span class="pun" style="color:#000000;">.</span><span class="kwd" style="color:#008b;">get</span><span class="pun" style="color:#000000;">(</span><span class="pln" style="color:#000000;">i</span><span class="pun" style="color:#000000;">));</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">}</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">}</span></code>


上面显示了四种不同遍历集合和数组的方式。前面两种有着相同的性能,所以如果只是读取元素的话,可以放心地对数组使用增强版
for
循环。对
Collection
对象来说,增强版
for
循环和使用迭代器遍历元素有着相同的性能。
ArrayList
对象应避免使用增强版
for
循环。

如果不仅需要遍历元素,而且需要元素的位置,就一定要使用数组或者
ArrayList
,因为所有其他
Collection
类在这些情况下会更慢。

一般情况下,如果在读取元素几乎不变的数据集时对性能要求很高,建议使用常规数组。然而,数组的大小固定,添加数据会影响性能,所以编写代码时要考虑所有因素。


队列、同步和锁

通常情况下,应用程序会在一个线程中生产数据,在另一个线程中使用它们。常见的例子是在一个线程中获取网络上的数据,在另一个线程(操作UI的主线程)中把这些数据展现给用户。这种模式称为生产者/消费者模式,在面向对象编程课程中,开发者用算法来实现该模式可能要花上几个小时。下面会介绍一些简化生产者/消费者模式实现的现成类。

1. 更智能的队列

虽然已有现成的类并能用更少的代码实现该功能,但许多Java开发者仍然选择使用
LinkedList
以及同步块实现队列功能。开发者可在
java.util.concurrent
包中找到同步相关的类。此外,本包还包含信号量、锁以及对单个变量进行原子操作的类。考虑下面使用标准的
LinkedList
实现线程安全队列的代码。
<code style="padding: 0px; font-family: Menlo, Monaco, Consolas, 'Courier New', monospace; color: inherit; border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; background-color: transparent; border: 0px;"><span class="kwd" style="color:#008b;">public</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">class</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#2b91af;">ThreadSafeQueue</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">private</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#2b91af;">LinkedList</span><span class="pun" style="color:#000000;"><</span><span class="typ" style="color:#2b91af;">String</span><span class="pun" style="color:#000000;">></span><span class="pln" style="color:#000000;"> mList </span><span class="pun" style="color:#000000;">=</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">new</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#2b91af;">LinkedList</span><span class="pun" style="color:#000000;"><</span><span class="typ" style="color:#2b91af;">String</span><span class="pun" style="color:#000000;">>();</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">private</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">final</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#2b91af;">Object</span><span class="pln" style="color:#000000;"> mLock </span><span class="pun" style="color:#000000;">=</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">new</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#2b91af;">Object</span><span class="pun" style="color:#000000;">();</span><span class="pln" style="color:#000000;">

</span><span class="kwd" style="color:#008b;">public</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">void</span><span class="pln" style="color:#000000;"> offer</span><span class="pun" style="color:#000000;">(</span><span class="typ" style="color:#2b91af;">String</span><span class="pln" style="color:#000000;"> value</span><span class="pun" style="color:#000000;">)</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">synchronized</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">(</span><span class="pln" style="color:#000000;">mLock</span><span class="pun" style="color:#000000;">)</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
mList</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">offer</span><span class="pun" style="color:#000000;">(</span><span class="pln" style="color:#000000;">value</span><span class="pun" style="color:#000000;">);</span><span class="pln" style="color:#000000;">
mLock</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">notifyAll</span><span class="pun" style="color:#000000;">();</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">}</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">}</span><span class="pln" style="color:#000000;">

</span><span class="kwd" style="color:#008b;">public</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">synchronized</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#2b91af;">String</span><span class="pln" style="color:#000000;"> poll</span><span class="pun" style="color:#000000;">()</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">synchronized</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">(</span><span class="pln" style="color:#000000;">mLock</span><span class="pun" style="color:#000000;">)</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">while</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">(</span><span class="pln" style="color:#000000;">mList</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">isEmpty</span><span class="pun" style="color:#000000;">())</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">try</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
mLock</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">wait</span><span class="pun" style="color:#000000;">();</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">}</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">catch</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">(</span><span class="typ" style="color:#2b91af;">InterruptedException</span><span class="pln" style="color:#000000;"> e</span><span class="pun" style="color:#000000;">)</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
</span><span class="com" style="color:gray;">//简洁起见忽略异常处理</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">}</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">}</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">return</span><span class="pln" style="color:#000000;"> mList</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">poll</span><span class="pun" style="color:#000000;">();</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">}</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">}</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">}</span></code>


虽然这段代码是正确的,并有可能在考试中得满分,但实现和测试这样一段代码只是在浪费时间。实际上,所有前面的代码可用下面一行代替。
<code style="padding: 0px; font-family: Menlo, Monaco, Consolas, 'Courier New', monospace; color: inherit; border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; background-color: transparent; border: 0px;"><span class="typ" style="color:#2b91af;">LinkedBlockingQueue</span><span class="pun" style="color:#000000;"><</span><span class="typ" style="color:#2b91af;">String</span><span class="pun" style="color:#000000;">></span><span class="pln" style="color:#000000;"> blockingQueue </span><span class="pun" style="color:#000000;">=</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">new</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#2b91af;">LinkedBlockingQueue</span><span class="pun" style="color:#000000;"><</span><span class="typ" style="color:#2b91af;">String</span><span class="pun" style="color:#000000;">>();</span></code>


上面的一行代码能像前面的例子一样提供相同类型的阻塞队列,甚至能提供额外的线程安全操作。
java.util.concurrent
包含许多可选的队列以及并发映射类,所以,一般情况下,建议使用它们,而不是像之前的示例那样使用更多代码。

2. 更智能的锁

Java提供的
synchronized
关键字允许开发者创建线程安全的方法和代码块。
synchronized
关键字易于使用,也很容易滥用,对性能造成负面影响。当需要区分读数据和写数据时,
synchronized
关键字并不是最有效的。幸好,
java.util.concurrent.locks
包中的工具类对这种情况提供了很好的支持。
<code style="padding: 0px; font-family: Menlo, Monaco, Consolas, 'Courier New', monospace; color: inherit; border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; background-color: transparent; border: 0px;"><span class="kwd" style="color:#008b;">public</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">class</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#2b91af;">ReadWriteLockDemo</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">private</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">final</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#2b91af;">ReentrantReadWriteLock</span><span class="pln" style="color:#000000;"> mLock</span><span class="pun" style="color:#000000;">;</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">private</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#2b91af;">String</span><span class="pln" style="color:#000000;"> mName</span><span class="pun" style="color:#000000;">;</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">private</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">int</span><span class="pln" style="color:#000000;"> mAge</span><span class="pun" style="color:#000000;">;</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">private</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#2b91af;">String</span><span class="pln" style="color:#000000;"> mAddress</span><span class="pun" style="color:#000000;">;</span><span class="pln" style="color:#000000;">

</span><span class="kwd" style="color:#008b;">public</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#2b91af;">ReadWriteLockDemo</span><span class="pun" style="color:#000000;">()</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
mLock </span><span class="pun" style="color:#000000;">=</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">new</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#2b91af;">ReentrantReadWriteLock</span><span class="pun" style="color:#000000;">();</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">}</span><span class="pln" style="color:#000000;">

</span><span class="kwd" style="color:#008b;">public</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">void</span><span class="pln" style="color:#000000;"> setPersonData</span><span class="pun" style="color:#000000;">(</span><span class="typ" style="color:#2b91af;">String</span><span class="pln" style="color:#000000;"> name</span><span class="pun" style="color:#000000;">,</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">int</span><span class="pln" style="color:#000000;"> age</span><span class="pun" style="color:#000000;">,</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#2b91af;">String</span><span class="pln" style="color:#000000;"> address</span><span class="pun" style="color:#000000;">)</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
</span><span class="typ" style="color:#2b91af;">ReentrantReadWriteLock</span><span class="pun" style="color:#000000;">.</span><span class="typ" style="color:#2b91af;">WriteLock</span><span class="pln" style="color:#000000;"> writeLock </span><span class="pun" style="color:#000000;">=</span><span class="pln" style="color:#000000;">     mLock</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">writeLock</span><span class="pun" style="color:#000000;">();</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">try</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
writeLock</span><span class="pun" style="color:#000000;">.</span><span class="kwd" style="color:#008b;">lock</span><span class="pun" style="color:#000000;">();</span><span class="pln" style="color:#000000;">
mName </span><span class="pun" style="color:#000000;">=</span><span class="pln" style="color:#000000;"> name</span><span class="pun" style="color:#000000;">;</span><span class="pln" style="color:#000000;">
mAge </span><span class="pun" style="color:#000000;">=</span><span class="pln" style="color:#000000;"> age</span><span class="pun" style="color:#000000;">;</span><span class="pln" style="color:#000000;">
mAddress </span><span class="pun" style="color:#000000;">=</span><span class="pln" style="color:#000000;"> address</span><span class="pun" style="color:#000000;">;</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">}</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">finally</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
writeLock</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">unlock</span><span class="pun" style="color:#000000;">();</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">}</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">}</span><span class="pln" style="color:#000000;">

</span><span class="kwd" style="color:#008b;">public</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#2b91af;">String</span><span class="pln" style="color:#000000;"> getName</span><span class="pun" style="color:#000000;">()</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
</span><span class="typ" style="color:#2b91af;">ReentrantReadWriteLock</span><span class="pun" style="color:#000000;">.</span><span class="typ" style="color:#2b91af;">ReadLock</span><span class="pln" style="color:#000000;"> readLock </span><span class="pun" style="color:#000000;">=</span><span class="pln" style="color:#000000;"> mLock</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">readLock</span><span class="pun" style="color:#000000;">();</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">try</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
readLock</span><span class="pun" style="color:#000000;">.</span><span class="kwd" style="color:#008b;">lock</span><span class="pun" style="color:#000000;">();</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">return</span><span class="pln" style="color:#000000;"> mName</span><span class="pun" style="color:#000000;">;</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">}</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">finally</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
readLock</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">unlock</span><span class="pun" style="color:#000000;">();</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">}</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">}</span><span class="pln" style="color:#000000;">

</span><span class="com" style="color:gray;">// 重复代码不再赘述</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">}</span></code>


上面的代码展示了在什么地方使用
ReentrantReadWriteLock
,它允许多个并发线程对数据进行只读访问,并确保同一时间只有一个线程写入相同的数据。

在代码中使用
synchronized
关键字仍然是处理锁问题的有效方法,但无论何种情况下,都要考虑
ReentrantReadWriteLock
是否是更有效的解决方案。


理和分配内存

Java中的自动内存管理有效消除了软件开发过程中许多最常见的问题。当不再需要记住为每个新建的对象释放内存时,开发者可以用省下的时间改善功能以及软件的整体质量。

但需要为这个功能付出代价,因为自动垃圾回收器会和应用程序并行运行。垃圾回收器会一直运行,并检查是否有可以回收的内存。这种行为意味着应用程序进程会和垃圾回收器竞争CPU时间,所以至关重要的一点是,确保垃圾回收器不管何时运行都不会占用太长时间。

此外,自动内存管理并不能保证不会有内存泄漏。如果引用了不再需要的对象,垃圾回收器不会收集它们,这将导致内存的浪费。如果一直分配对象,但从不释放,最终会导致
OutOfMemory
异常,应用程序也会崩溃。所以,要尽量避免在Android的主要组件中引用对象,否则,这些对象在应用程序的生命周期中可能永远不会被“垃圾回收”。


少对象分配

Java和Android中,自动内存管理最常见的问题是分配了无用的对象,导致垃圾回收器一直运行。考虑一种情况,一个代表一对整数的简单类:
<code style="padding: 0px; font-family: Menlo, Monaco, Consolas, 'Courier New', monospace; color: inherit; border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; background-color: transparent; border: 0px;"><span class="kwd" style="color:#008b;">public</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">final</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">class</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#2b91af;">Pair</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">public</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">int</span><span class="pln" style="color:#000000;"> firstValue</span><span class="pun" style="color:#000000;">;</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">public</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">int</span><span class="pln" style="color:#000000;"> secondValue</span><span class="pun" style="color:#000000;">;</span><span class="pln" style="color:#000000;">

</span><span class="kwd" style="color:#008b;">public</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#2b91af;">Pair</span><span class="pun" style="color:#000000;">(</span><span class="kwd" style="color:#008b;">int</span><span class="pln" style="color:#000000;"> firstValue</span><span class="pun" style="color:#000000;">,</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">int</span><span class="pln" style="color:#000000;"> secondValue</span><span class="pun" style="color:#000000;">)</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">this</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">firstValue </span><span class="pun" style="color:#000000;">=</span><span class="pln" style="color:#000000;"> firstValue</span><span class="pun" style="color:#000000;">;</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">this</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">secondValue </span><span class="pun" style="color:#000000;">=</span><span class="pln" style="color:#000000;"> secondValue</span><span class="pun" style="color:#000000;">;</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">}</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">}</span></code>


现在,假如在应用程序中接收了一个整数数组,把它们进行分组,然后使用
sendPair
方法。下面是一个内存分配做得很差的例子:
<code style="padding: 0px; font-family: Menlo, Monaco, Consolas, 'Courier New', monospace; color: inherit; border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; background-color: transparent; border: 0px;"><span class="kwd" style="color:#008b;">public</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">void</span><span class="pln" style="color:#000000;"> badObjectAllocationExample</span><span class="pun" style="color:#000000;">(</span><span class="kwd" style="color:#008b;">int</span><span class="pun" style="color:#000000;">[]</span><span class="pln" style="color:#000000;"> pairs</span><span class="pun" style="color:#000000;">)</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">if</span><span class="pun" style="color:#000000;">(</span><span class="pln" style="color:#000000;">pairs</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">length </span><span class="pun" style="color:#000000;">%</span><span class="pln" style="color:#000000;"> </span><span class="lit" style="color:#8000;">2</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">!=</span><span class="pln" style="color:#000000;"> </span><span class="lit" style="color:#8000;">0</span><span class="pun" style="color:#000000;">)</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">throw</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">new</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#2b91af;">IllegalArgumentException</span><span class="pun" style="color:#000000;">(“</span><span class="typ" style="color:#2b91af;">Bad</span><span class="pln" style="color:#000000;"> array size</span><span class="pun" style="color:#000000;">!”);</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">}</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">for</span><span class="pun" style="color:#000000;">(</span><span class="kwd" style="color:#008b;">int</span><span class="pln" style="color:#000000;"> i </span><span class="pun" style="color:#000000;">=</span><span class="pln" style="color:#000000;"> </span><span class="lit" style="color:#8000;">0</span><span class="pun" style="color:#000000;">;</span><span class="pln" style="color:#000000;"> i </span><span class="pun" style="color:#000000;"><</span><span class="pln" style="color:#000000;"> pairs</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">length</span><span class="pun" style="color:#000000;">;</span><span class="pln" style="color:#000000;"> i</span><span class="pun" style="color:#000000;">+=</span><span class="lit" style="color:#8000;">2</span><span class="pun" style="color:#000000;">)</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
</span><span class="typ" style="color:#2b91af;">Pair</span><span class="pln" style="color:#000000;"> pair </span><span class="pun" style="color:#000000;">=</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">new</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#2b91af;">Pair</span><span class="pun" style="color:#000000;">(</span><span class="pln" style="color:#000000;">pairs</span><span class="pun" style="color:#000000;">[</span><span class="pln" style="color:#000000;">i</span><span class="pun" style="color:#000000;">],</span><span class="pln" style="color:#000000;"> pairs</span><span class="pun" style="color:#000000;">[</span><span class="pln" style="color:#000000;">i</span><span class="pun" style="color:#000000;">+</span><span class="lit" style="color:#8000;">1</span><span class="pun" style="color:#000000;">]);</span><span class="pln" style="color:#000000;">
sendPair</span><span class="pun" style="color:#000000;">(</span><span class="pln" style="color:#000000;">pair</span><span class="pun" style="color:#000000;">);</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">}</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">}</span></code>


虽然这是个展示如何生成
Pair
对象的简单粗糙的例子(如果数组的大小是奇数的话可能会引起崩溃),但它说明了一个很常见的错误:在循环中分配对象。在上面的循环中,垃圾回收器将会做很多工作,并很可能耗尽CPU从而导致应用程序用户界面卡顿。如果开发者知道
sendPair
方法返回时并不会持有
Pair
对象的引用,那么解决方案很简单,在循环外创建
Pair
对象并重用,如下所示:
<code style="padding: 0px; font-family: Menlo, Monaco, Consolas, 'Courier New', monospace; color: inherit; border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; background-color: transparent; border: 0px;"><span class="kwd" style="color:#008b;">public</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">void</span><span class="pln" style="color:#000000;"> betterObjectAllocationExample</span><span class="pun" style="color:#000000;">(</span><span class="kwd" style="color:#008b;">int</span><span class="pun" style="color:#000000;">[]</span><span class="pln" style="color:#000000;"> pairs</span><span class="pun" style="color:#000000;">)</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">if</span><span class="pun" style="color:#000000;">(</span><span class="pln" style="color:#000000;">pairs</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">length </span><span class="pun" style="color:#000000;">%</span><span class="pln" style="color:#000000;"> </span><span class="lit" style="color:#8000;">2</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">!=</span><span class="pln" style="color:#000000;"> </span><span class="lit" style="color:#8000;">0</span><span class="pun" style="color:#000000;">)</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">throw</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">new</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#2b91af;">IllegalArgumentException</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">(</span><span class="str" style="color:#8000;">"Bad array size!"</span><span class="pun" style="color:#000000;">);</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">}</span><span class="pln" style="color:#000000;">
</span><span class="typ" style="color:#2b91af;">Point</span><span class="pln" style="color:#000000;"> thePair </span><span class="pun" style="color:#000000;">=</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">new</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#2b91af;">Point</span><span class="pun" style="color:#000000;">(</span><span class="lit" style="color:#8000;">0</span><span class="pun" style="color:#000000;">,</span><span class="lit" style="color:#8000;">0</span><span class="pun" style="color:#000000;">);</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">for</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">(</span><span class="kwd" style="color:#008b;">int</span><span class="pln" style="color:#000000;"> i </span><span class="pun" style="color:#000000;">=</span><span class="pln" style="color:#000000;"> </span><span class="lit" style="color:#8000;">0</span><span class="pun" style="color:#000000;">;</span><span class="pln" style="color:#000000;"> i </span><span class="pun" style="color:#000000;"><</span><span class="pln" style="color:#000000;"> pairs</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">length</span><span class="pun" style="color:#000000;">;</span><span class="pln" style="color:#000000;"> i</span><span class="pun" style="color:#000000;">+=</span><span class="lit" style="color:#8000;">2</span><span class="pun" style="color:#000000;">)</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
thePair</span><span class="pun" style="color:#000000;">.</span><span class="kwd" style="color:#008b;">set</span><span class="pun" style="color:#000000;">(</span><span class="pln" style="color:#000000;">pairs </span><span class="pun" style="color:#000000;">[</span><span class="pln" style="color:#000000;">i</span><span class="pun" style="color:#000000;">],</span><span class="pln" style="color:#000000;"> pairs </span><span class="pun" style="color:#000000;">[</span><span class="pln" style="color:#000000;">i</span><span class="pun" style="color:#000000;">+</span><span class="lit" style="color:#8000;">1</span><span class="pun" style="color:#000000;">]);</span><span class="pln" style="color:#000000;">
sendPair</span><span class="pun" style="color:#000000;">(</span><span class="pln" style="color:#000000;">thePair</span><span class="pun" style="color:#000000;">);</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">}</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">}</span></code>


新版的方法确保了在整个运行过程中一直重用该对象。当方法返回时,只会有一次垃圾回收。请记住,应尽可能避免在循环中分配对象。

然而有时候无法避免在循环中创建对象,所以还需要采用某种方法处理这种情况。我们的解决方案是使用一个静态工厂方法按需分配对象,Joshua Bloch在《Effective Java中文版》一书的第一条中详细地描述了该方法。

这种方法在Android框架和API中很常见,它允许开发者使用一个按需填充的对象缓存。唯一的缺点是需要手动回收这些对象,否则缓存会一直是空的。

基于前面的例子,通过重构
Pair
类来使用一个简单的对象池重用对象。
<code style="padding: 0px; font-family: Menlo, Monaco, Consolas, 'Courier New', monospace; color: inherit; border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; background-color: transparent; border: 0px;"><span class="kwd" style="color:#008b;">public</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">final</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">class</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#2b91af;">Pair</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">public</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">int</span><span class="pln" style="color:#000000;"> firstValue</span><span class="pun" style="color:#000000;">;</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">public</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">int</span><span class="pln" style="color:#000000;"> secondValue</span><span class="pun" style="color:#000000;">;</span><span class="pln" style="color:#000000;">

</span><span class="com" style="color:gray;">// 引用对象池中的下一个对象</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">private</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#2b91af;">Pair</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">next</span><span class="pun" style="color:#000000;">;</span><span class="pln" style="color:#000000;">

</span><span class="com" style="color:gray;">// 同步锁</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">private</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">static</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">final</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#2b91af;">Object</span><span class="pln" style="color:#000000;"> sPoolSync </span><span class="pun" style="color:#000000;">=</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">new</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#2b91af;">Object</span><span class="pun" style="color:#000000;">();</span><span class="pln" style="color:#000000;">
</span><span class="com" style="color:gray;">// 对象池中第一个可用的对象</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">private</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">static</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#2b91af;">Pair</span><span class="pln" style="color:#000000;"> sPool</span><span class="pun" style="color:#000000;">;</span><span class="pln" style="color:#000000;">

</span><span class="kwd" style="color:#008b;">private</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">static</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">int</span><span class="pln" style="color:#000000;"> sPoolSize </span><span class="pun" style="color:#000000;">=</span><span class="pln" style="color:#000000;"> </span><span class="lit" style="color:#8000;">0</span><span class="pun" style="color:#000000;">;</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">private</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">static</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">final</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">int</span><span class="pln" style="color:#000000;"> MAX_POOL_SIZE </span><span class="pun" style="color:#000000;">=</span><span class="pln" style="color:#000000;"> </span><span class="lit" style="color:#8000;">50</span><span class="pun" style="color:#000000;">;</span><span class="pln" style="color:#000000;">

</span><span class="com" style="color:gray;">/**
* 只能用obtain()方法获取对象
*/</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">private</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#2b91af;">Pair</span><span class="pun" style="color:#000000;">()</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">}</span><span class="pln" style="color:#000000;">

</span><span class="com" style="color:gray;">/**
* 返回回收的对象或者当对象池为空时创建一个新对象
*/</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">public</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">static</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#2b91af;">Pair</span><span class="pln" style="color:#000000;"> obtain</span><span class="pun" style="color:#000000;">()</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">synchronized</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">(</span><span class="pln" style="color:#000000;">sPoolSync</span><span class="pun" style="color:#000000;">)</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">if</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">(</span><span class="pln" style="color:#000000;">sPool </span><span class="pun" style="color:#000000;">!=</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">null</span><span class="pun" style="color:#000000;">)</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
</span><span class="typ" style="color:#2b91af;">Pair</span><span class="pln" style="color:#000000;"> m </span><span class="pun" style="color:#000000;">=</span><span class="pln" style="color:#000000;"> sPool</span><span class="pun" style="color:#000000;">;</span><span class="pln" style="color:#000000;">
sPool </span><span class="pun" style="color:#000000;">=</span><span class="pln" style="color:#000000;"> m</span><span class="pun" style="color:#000000;">.</span><span class="kwd" style="color:#008b;">next</span><span class="pun" style="color:#000000;">;</span><span class="pln" style="color:#000000;">
m</span><span class="pun" style="color:#000000;">.</span><span class="kwd" style="color:#008b;">next</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">=</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">null</span><span class="pun" style="color:#000000;">;</span><span class="pln" style="color:#000000;">
sPoolSize</span><span class="pun" style="color:#000000;">--;</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">return</span><span class="pln" style="color:#000000;"> m</span><span class="pun" style="color:#000000;">;</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">}</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">}</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">return</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">new</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#2b91af;">Pair</span><span class="pun" style="color:#000000;">();</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">}</span><span class="pln" style="color:#000000;">

</span><span class="com" style="color:gray;">/**
* 回收该对象。调用该方法后需要释放所有对该实例的引用
*/</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">public</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">void</span><span class="pln" style="color:#000000;"> recycle</span><span class="pun" style="color:#000000;">()</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">synchronized</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">(</span><span class="pln" style="color:#000000;">sPoolSync</span><span class="pun" style="color:#000000;">)</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">if</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">(</span><span class="pln" style="color:#000000;">sPoolSize </span><span class="pun" style="color:#000000;"><</span><span class="pln" style="color:#000000;"> MAX_POOL_SIZE</span><span class="pun" style="color:#000000;">)</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">next</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">=</span><span class="pln" style="color:#000000;"> sPool</span><span class="pun" style="color:#000000;">;</span><span class="pln" style="color:#000000;">
sPool </span><span class="pun" style="color:#000000;">=</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">this</span><span class="pun" style="color:#000000;">;</span><span class="pln" style="color:#000000;">
sPoolSize</span><span class="pun" style="color:#000000;">++;</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">}</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">}</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">}</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">}</span></code>


注意,本例增加了多个字段,有静态的也有非静态的。可使用这些字段实现传统的
Pair
对象链表。只能通过
obtain
方法创建该类的对象。通过使用私有构造函数来防止在类外面创建对象。
obtain
方法首先会检查对象池中是否包含任何存在的对象,并删除列表中的第一个元素然后返回它。如果对象池为空,
obtain
方法会创建一个新的对象。要把对象重新放回池中,需要在使用完该对象时,对它调用
recycle
方法。这时,不能再有对该对象的引用。

修改
Pair
类后,之前的循环方法也需要修改。
<code style="padding: 0px; font-family: Menlo, Monaco, Consolas, 'Courier New', monospace; color: inherit; border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; background-color: transparent; border: 0px;"><span class="kwd" style="color:#008b;">public</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">void</span><span class="pln" style="color:#000000;"> bestObjectAllocationExample</span><span class="pun" style="color:#000000;">(</span><span class="kwd" style="color:#008b;">int</span><span class="pun" style="color:#000000;">[]</span><span class="pln" style="color:#000000;"> pairs</span><span class="pun" style="color:#000000;">)</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">if</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">(</span><span class="pln" style="color:#000000;">pairs</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">length </span><span class="pun" style="color:#000000;">%</span><span class="pln" style="color:#000000;"> </span><span class="lit" style="color:#8000;">2</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">!=</span><span class="pln" style="color:#000000;"> </span><span class="lit" style="color:#8000;">0</span><span class="pun" style="color:#000000;">)</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">throw</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">new</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#2b91af;">IllegalArgumentException</span><span class="pun" style="color:#000000;">(</span><span class="str" style="color:#8000;">"Bad array size!"</span><span class="pun" style="color:#000000;">);</span><span class="pln" style="color:#000000;">

</span><span class="kwd" style="color:#008b;">for</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">(</span><span class="kwd" style="color:#008b;">int</span><span class="pln" style="color:#000000;"> i </span><span class="pun" style="color:#000000;">=</span><span class="pln" style="color:#000000;"> </span><span class="lit" style="color:#8000;">0</span><span class="pun" style="color:#000000;">;</span><span class="pln" style="color:#000000;"> i </span><span class="pun" style="color:#000000;"><</span><span class="pln" style="color:#000000;"> pairs</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">length</span><span class="pun" style="color:#000000;">;</span><span class="pln" style="color:#000000;"> i </span><span class="pun" style="color:#000000;">+=</span><span class="pln" style="color:#000000;"> </span><span class="lit" style="color:#8000;">2</span><span class="pun" style="color:#000000;">)</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
</span><span class="typ" style="color:#2b91af;">Pair</span><span class="pln" style="color:#000000;"> pair </span><span class="pun" style="color:#000000;">=</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#2b91af;">Pair</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">obtain</span><span class="pun" style="color:#000000;">();</span><span class="pln" style="color:#000000;">
pair</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">firstValue </span><span class="pun" style="color:#000000;">=</span><span class="pln" style="color:#000000;"> pairs</span><span class="pun" style="color:#000000;">[</span><span class="pln" style="color:#000000;">i</span><span class="pun" style="color:#000000;">];</span><span class="pln" style="color:#000000;">
pair</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">secondValue </span><span class="pun" style="color:#000000;">=</span><span class="pln" style="color:#000000;"> pairs</span><span class="pun" style="color:#000000;">[</span><span class="pln" style="color:#000000;">i </span><span class="pun" style="color:#000000;">+</span><span class="pln" style="color:#000000;"> </span><span class="lit" style="color:#8000;">1</span><span class="pun" style="color:#000000;">];</span><span class="pln" style="color:#000000;">
sendPair</span><span class="pun" style="color:#000000;">(</span><span class="pln" style="color:#000000;">pair</span><span class="pun" style="color:#000000;">);</span><span class="pln" style="color:#000000;">
pair</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">recycle</span><span class="pun" style="color:#000000;">();</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">}</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">}</span></code>


第一次运行这个方法会创建一个新的
Pair
实例,接下来的每次迭代会重用改对象。不过,下次再运行该方法时,不会再创建新的对象。另外,由于
obtain
recycle
是线程安全的,可以在多个并发线程中安全地使用这两个方法。唯一的缺点是,必须记住要手动调用
recycle
方法,不过这是一个很小的代价。这样就只会在应用退出时才会对
Pair
类进行垃圾回收。

Pair
类的例子很琐碎,但是它描述了一个模式能明显减少类的创建。这个设计可能看起来很熟悉,因为它出现在Android源代码和API的多个地方。一些经常使用的类,比如
Message
MotionEvent
以及
Parcel
都通过实现这个模式来减少不必要的垃圾回收。之前的
Pair
类基本上就是复制
Message
类的实现。使用这种方法时记得在使用完对象后调用
recycle
方法,否则对象池将一直是空的。


Android中的多线程

编程中最难的部分之一编写在多个线程中执行的代码。这是对当今应用的一个要求,因为不可能只在一个线程中按顺序执行所有代码。Android应用程序从主线程开始运行,也称为UI线程(这里UI线程和主线程含义相同)。除非启动另一个线程或者通过隐式调用函数来启动一个线程,否则所有在Android应用中的操作都会运行在主线程中。这意味着,如果在主线程执行很耗时的操作(比如在
onResume
中运行代码),所有的绘制以及输入事件将被阻塞,直到该操作完成。所以,编写代码时首选需要牢记的是:确保永远不要阻塞主线程。

但是怎样才能知道一个方法是否在主线程中执行?Android官方文档指出:“默认情况下,应用的所有组件都运行在同一个进程和线程中(主线程)。”更具体点儿,Android组件(
Activity
BroadcastReceiver
Service
以及
Application
)的所有回调(基本上是所有的
onX
方法)都运行在主线程里。因此,
Service
onStartCommand
方法和
Activity
onResume
也运行在同一个线程里。需要记住的时,阻塞上面任意一个方法,都可能导致系统“杀死”应用程序。

只要应用程序进程还在执行,主线程会一直运行。通过使用
Looper
类,主线程会在应用程序的生命周期中一直执行。
Looper
类会在当前线程中一直查询消息队列(使用
MessageQueue
类)。对该队列的查询会被阻塞直到有新的消息进入,这能确保空闲时线程进入休眠状态。所有对主线程的操作都是通过直接使用
Handler
对象或者间接使用部分Android
API(比如,
runOnUiThread
方法)往队列里发送消息完成的。可以通过
Context.getMainLooper()
来查询应用程序主线程的
Looper
对象。

什么样的代码在主线程中执行才是安全的?什么样的代码需要放到其他线程中?严格地讲,只有那些必须在主线程执行的方法才能放在主线程中。其他一切操作都应放在另一个单独的线程中执行。实际情况下,那些不会耗时的操作也可以放在主线程中。如果能确保在另一个单独的线程中执行文件、数据库或者网络操作,通常主线程会是安全的。另外,对于某些应用或者游戏,开发人员可能会不定期执行一些与UI无关的计算,这些操作也应该放在一个单独的线程中执行。然而,也要确保同一时间不会运行太多线程,原因是CPU切换线程也会造成性能损失。

在编写Android代码时如何声明和管理各种线程?


Thread类

Thread
类是Android中所有线程的基类,Java SE中也包含它。如果要在线程中执行代码,既可以创建一个具体的类(即继承自
Thread
的新类),也可以把实现
Runnable
接口的类对象传给
Thread
的构造函数。

本例需要遍历
Objects
的数组,从而把数据“上传”到服务器(用于上传的代码不是本例的一部分)。需要在一个单独的线程中执行此操作,否则会阻塞用户界面。此外,需要通过增加
ProgressBar
来更新上传的进度。下面的代码显示了通过实现
Runnable
接口来解决这个问题:
<code style="padding: 0px; font-family: Menlo, Monaco, Consolas, 'Courier New', monospace; color: inherit; border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; background-color: transparent; border: 0px;"><span class="kwd" style="color:#008b;">public</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">class</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#2b91af;">MyThread</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">implements</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#2b91af;">Runnable</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">private</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#2b91af;">Object</span><span class="pun" style="color:#000000;">[]</span><span class="pln" style="color:#000000;"> mInput</span><span class="pun" style="color:#000000;">;</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">private</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#2b91af;">Activity</span><span class="pln" style="color:#000000;"> mActivity</span><span class="pun" style="color:#000000;">;</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">private</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">int</span><span class="pln" style="color:#000000;"> mProgress </span><span class="pun" style="color:#000000;">=</span><span class="pln" style="color:#000000;"> </span><span class="lit" style="color:#8000;">0</span><span class="pun" style="color:#000000;">;</span><span class="pln" style="color:#000000;">

</span><span class="kwd" style="color:#008b;">public</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#2b91af;">MyThread</span><span class="pun" style="color:#000000;">(</span><span class="typ" style="color:#2b91af;">Activity</span><span class="pln" style="color:#000000;"> activity</span><span class="pun" style="color:#000000;">,</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#2b91af;">Object</span><span class="pun" style="color:#000000;">...</span><span class="pln" style="color:#000000;"> input</span><span class="pun" style="color:#000000;">)</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
mActivity </span><span class="pun" style="color:#000000;">=</span><span class="pln" style="color:#000000;"> activity</span><span class="pun" style="color:#000000;">;</span><span class="pln" style="color:#000000;">
mInput </span><span class="pun" style="color:#000000;">=</span><span class="pln" style="color:#000000;"> input</span><span class="pun" style="color:#000000;">;</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">}</span><span class="pln" style="color:#000000;">

</span><span class="lit" style="color:#8000;">@Override</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">public</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">void</span><span class="pln" style="color:#000000;"> run</span><span class="pun" style="color:#000000;">()</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
mProgress </span><span class="pun" style="color:#000000;">=</span><span class="pln" style="color:#000000;"> </span><span class="lit" style="color:#8000;">0</span><span class="pun" style="color:#000000;">;</span><span class="pln" style="color:#000000;">
</span><span class="typ" style="color:#2b91af;">Runnable</span><span class="pln" style="color:#000000;"> runnable </span><span class="pun" style="color:#000000;">=</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">new</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#2b91af;">Runnable</span><span class="pun" style="color:#000000;">()</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">public</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">void</span><span class="pln" style="color:#000000;"> run</span><span class="pun" style="color:#000000;">()</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
mActivity</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">findViewById</span><span class="pun" style="color:#000000;">(</span><span class="pln" style="color:#000000;">R</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">id</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">progressBar</span><span class="pun" style="color:#000000;">).</span><span class="pln" style="color:#000000;">
setVisibility</span><span class="pun" style="color:#000000;">(</span><span class="typ" style="color:#2b91af;">View</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">VISIBLE</span><span class="pun" style="color:#000000;">);</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">((</span><span class="typ" style="color:#2b91af;">ProgressBar</span><span class="pun" style="color:#000000;">)</span><span class="pln" style="color:#000000;"> mActivity</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">
findViewById</span><span class="pun" style="color:#000000;">(</span><span class="pln" style="color:#000000;">R</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">id</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">progressBar</span><span class="pun" style="color:#000000;">)).</span><span class="pln" style="color:#000000;">setProgress</span><span class="pun" style="color:#000000;">(</span><span class="lit" style="color:#8000;">0</span><span class="pun" style="color:#000000;">);</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">}</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">};</span><span class="pln" style="color:#000000;">
mActivity</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">runOnUiThread</span><span class="pun" style="color:#000000;">(</span><span class="pln" style="color:#000000;">runnable</span><span class="pun" style="color:#000000;">);</span><span class="pln" style="color:#000000;">

</span><span class="com" style="color:gray;">// 循环并处理输入</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">for</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">(</span><span class="typ" style="color:#2b91af;">Object</span><span class="pln" style="color:#000000;"> input </span><span class="pun" style="color:#000000;">:</span><span class="pln" style="color:#000000;"> mInput</span><span class="pun" style="color:#000000;">)</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
</span><span class="com" style="color:gray;">// 上传到服务器 (用睡眠模拟)</span><span class="pln" style="color:#000000;">
</span><span class="typ" style="color:#2b91af;">SystemClock</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">sleep</span><span class="pun" style="color:#000000;">(</span><span class="lit" style="color:#8000;">50</span><span class="pun" style="color:#000000;">);</span><span class="pln" style="color:#000000;">

runnable </span><span class="pun" style="color:#000000;">=</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">new</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#2b91af;">Runnable</span><span class="pun" style="color:#000000;">()</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">public</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">void</span><span class="pln" style="color:#000000;"> run</span><span class="pun" style="color:#000000;">()</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">((</span><span class="typ" style="color:#2b91af;">ProgressBar</span><span class="pun" style="color:#000000;">)</span><span class="pln" style="color:#000000;"> mActivity</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">
findViewById</span><span class="pun" style="color:#000000;">(</span><span class="pln" style="color:#000000;">R</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">id</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">progressBar</span><span class="pun" style="color:#000000;">)).</span><span class="pln" style="color:#000000;">
setMax</span><span class="pun" style="color:#000000;">(++</span><span class="pln" style="color:#000000;">mProgress</span><span class="pun" style="color:#000000;">);</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">((</span><span class="typ" style="color:#2b91af;">ProgressBar</span><span class="pun" style="color:#000000;">)</span><span class="pln" style="color:#000000;"> mActivity</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">
findViewById</span><span class="pun" style="color:#000000;">(</span><span class="pln" style="color:#000000;">R</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">id</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">progressBar</span><span class="pun" style="color:#000000;">)).</span><span class="pln" style="color:#000000;">
setProgress</span><span class="pun" style="color:#000000;">(</span><span class="pln" style="color:#000000;">mInput</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">length</span><span class="pun" style="color:#000000;">);</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">}</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">};</span><span class="pln" style="color:#000000;">
mActivity</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">runOnUiThread</span><span class="pun" style="color:#000000;">(</span><span class="pln" style="color:#000000;">runnable</span><span class="pun" style="color:#000000;">);</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">}</span><span class="pln" style="color:#000000;">

runnable </span><span class="pun" style="color:#000000;">=</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">new</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#2b91af;">Runnable</span><span class="pun" style="color:#000000;">()</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">public</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">void</span><span class="pln" style="color:#000000;"> run</span><span class="pun" style="color:#000000;">()</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
mActivity</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">findViewById</span><span class="pun" style="color:#000000;">(</span><span class="pln" style="color:#000000;">R</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">id</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">progressBar</span><span class="pun" style="color:#000000;">).</span><span class="pln" style="color:#000000;">
setVisibility</span><span class="pun" style="color:#000000;">(</span><span class="typ" style="color:#2b91af;">View</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">INVISIBLE</span><span class="pun" style="color:#000000;">);</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">}</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">};</span><span class="pln" style="color:#000000;">
mActivity</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">runOnUiThread</span><span class="pun" style="color:#000000;">(</span><span class="pln" style="color:#000000;">runnable</span><span class="pun" style="color:#000000;">);</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">}</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">}</span></code>


从上面的例子可以看出,每次更新UI都需要创建一个新的
Runnable
对象。这使得代码变得很乱,而且垃圾回收器还会进行不必要的对象回收,这些都是开发者要避免的。为了在主线程中使用
runOnUiThread
方法更新UI,必须使用
Runnable


这种方案还有一个问题:因为只能对
Thread
实例调用一次
start
方法,所以每次执行操作都需要创建一个新的
Thread
对象。不断创建新的线程是非常昂贵的,本例还有改进的空间。总之,这不是一个非常灵活的方法,开发者应避免直接使用
Thread
类。


AsyncTask

AsyncTask
是Android中比较流行的几个类中的一个,因为它很容易使用。它允许开发者定义一个运行在单独线程中的任务,还能在任务的不同阶段提供回调函数。这些回调函数被设计成无需使用
runOnUiThread
方法即可更新UI,这非常适合表示长时间运行的操作的进度。下面的示例使用
AsyncTask
来完成
Thread
例子中的功能:
<code style="padding: 0px; font-family: Menlo, Monaco, Consolas, 'Courier New', monospace; color: inherit; border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; background-color: transparent; border: 0px;"><span class="kwd" style="color:#008b;">public</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">class</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#2b91af;">MyAsyncTask</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">extends</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#2b91af;">AsyncTask</span><span class="pun" style="color:#000000;"><</span><span class="typ" style="color:#2b91af;">String</span><span class="pun" style="color:#000000;">,</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#2b91af;">Integer</span><span class="pun" style="color:#000000;">,</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#2b91af;">Integer</span><span class="pun" style="color:#000000;">></span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">private</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#2b91af;">Activity</span><span class="pln" style="color:#000000;"> mActivity</span><span class="pun" style="color:#000000;">;</span><span class="pln" style="color:#000000;">

</span><span class="kwd" style="color:#008b;">public</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#2b91af;">MyAsyncTask</span><span class="pun" style="color:#000000;">(</span><span class="typ" style="color:#2b91af;">Activity</span><span class="pln" style="color:#000000;"> activity</span><span class="pun" style="color:#000000;">)</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
mActivity </span><span class="pun" style="color:#000000;">=</span><span class="pln" style="color:#000000;"> activity</span><span class="pun" style="color:#000000;">;</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">}</span><span class="pln" style="color:#000000;">

</span><span class="lit" style="color:#8000;">@Override</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">protected</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">void</span><span class="pln" style="color:#000000;"> onPreExecute</span><span class="pun" style="color:#000000;">()</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">super</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">onPreExecute</span><span class="pun" style="color:#000000;">();</span><span class="pln" style="color:#000000;">
</span><span class="com" style="color:gray;">// 下面的代码会运行在主线程中</span><span class="pln" style="color:#000000;">
mActivity</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">findViewById</span><span class="pun" style="color:#000000;">(</span><span class="pln" style="color:#000000;">R</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">id</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">progressBar</span><span class="pun" style="color:#000000;">).</span><span class="pln" style="color:#000000;">
setVisibility</span><span class="pun" style="color:#000000;">(</span><span class="typ" style="color:#2b91af;">View</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">VISIBLE</span><span class="pun" style="color:#000000;">);</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">((</span><span class="typ" style="color:#2b91af;">ProgressBar</span><span class="pun" style="color:#000000;">)</span><span class="pln" style="color:#000000;"> mActivity</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">findViewById</span><span class="pun" style="color:#000000;">(</span><span class="pln" style="color:#000000;">R</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">id</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">progressBar</span><span class="pun" style="color:#000000;">)).</span><span class="pln" style="color:#000000;">
setProgress</span><span class="pun" style="color:#000000;">(</span><span class="lit" style="color:#8000;">0</span><span class="pun" style="color:#000000;">);</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">}</span><span class="pln" style="color:#000000;">

</span><span class="lit" style="color:#8000;">@Override</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">protected</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#2b91af;">Integer</span><span class="pln" style="color:#000000;"> doInBackground</span><span class="pun" style="color:#000000;">(</span><span class="typ" style="color:#2b91af;">String</span><span class="pun" style="color:#000000;">...</span><span class="pln" style="color:#000000;"> inputs</span><span class="pun" style="color:#000000;">)</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
</span><span class="com" style="color:gray;">// 下面的代码不会运行在主线程中</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">int</span><span class="pln" style="color:#000000;"> progress </span><span class="pun" style="color:#000000;">=</span><span class="pln" style="color:#000000;"> </span><span class="lit" style="color:#8000;">0</span><span class="pun" style="color:#000000;">;</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">for</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">(</span><span class="typ" style="color:#2b91af;">String</span><span class="pln" style="color:#000000;"> input </span><span class="pun" style="color:#000000;">:</span><span class="pln" style="color:#000000;"> inputs</span><span class="pun" style="color:#000000;">)</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
</span><span class="com" style="color:gray;">// 把输入上传到服务器(用睡眠代替)</span><span class="pln" style="color:#000000;">
</span><span class="typ" style="color:#2b91af;">SystemClock</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">sleep</span><span class="pun" style="color:#000000;">(</span><span class="lit" style="color:#8000;">50</span><span class="pun" style="color:#000000;">);</span><span class="pln" style="color:#000000;">
publishProgress</span><span class="pun" style="color:#000000;">(++</span><span class="pln" style="color:#000000;">progress</span><span class="pun" style="color:#000000;">,</span><span class="pln" style="color:#000000;"> inputs</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">length</span><span class="pun" style="color:#000000;">);</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">}</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">return</span><span class="pln" style="color:#000000;"> progress</span><span class="pun" style="color:#000000;">;</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">}</span><span class="pln" style="color:#000000;">

</span><span class="lit" style="color:#8000;">@Override</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">protected</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">void</span><span class="pln" style="color:#000000;"> onProgressUpdate</span><span class="pun" style="color:#000000;">(</span><span class="typ" style="color:#2b91af;">Integer</span><span class="pun" style="color:#000000;">...</span><span class="pln" style="color:#000000;"> values</span><span class="pun" style="color:#000000;">)</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
</span><span class="com" style="color:gray;">// 下面的代码会运行在主线程中</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">((</span><span class="typ" style="color:#2b91af;">ProgressBar</span><span class="pun" style="color:#000000;">)</span><span class="pln" style="color:#000000;"> mActivity</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">findViewById</span><span class="pun" style="color:#000000;">(</span><span class="pln" style="color:#000000;">R</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">id</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">progressBar</span><span class="pun" style="color:#000000;">)).</span><span class="pln" style="color:#000000;">
setMax</span><span class="pun" style="color:#000000;">(</span><span class="pln" style="color:#000000;">values</span><span class="pun" style="color:#000000;">[</span><span class="lit" style="color:#8000;">1</span><span class="pun" style="color:#000000;">]);</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">((</span><span class="typ" style="color:#2b91af;">ProgressBar</span><span class="pun" style="color:#000000;">)</span><span class="pln" style="color:#000000;"> mActivity</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">findViewById</span><span class="pun" style="color:#000000;">(</span><span class="pln" style="color:#000000;">R</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">id</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">progressBar</span><span class="pun" style="color:#000000;">)).</span><span class="pln" style="color:#000000;">
setProgress</span><span class="pun" style="color:#000000;">(</span><span class="pln" style="color:#000000;">values</span><span class="pun" style="color:#000000;">[</span><span class="lit" style="color:#8000;">0</span><span class="pun" style="color:#000000;">]);</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">}</span><span class="pln" style="color:#000000;">

</span><span class="lit" style="color:#8000;">@Override</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">protected</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">void</span><span class="pln" style="color:#000000;"> onPostExecute</span><span class="pun" style="color:#000000;">(</span><span class="typ" style="color:#2b91af;">Integer</span><span class="pln" style="color:#000000;"> i</span><span class="pun" style="color:#000000;">)</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">super</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">onPostExecute</span><span class="pun" style="color:#000000;">(</span><span class="pln" style="color:#000000;">i</span><span class="pun" style="color:#000000;">);</span><span class="pln" style="color:#000000;">
</span><span class="com" style="color:gray;">// 下面的代码会运行在主线程中</span><span class="pln" style="color:#000000;">
mActivity</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">findViewById</span><span class="pun" style="color:#000000;">(</span><span class="pln" style="color:#000000;">R</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">id</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">progressBar</span><span class="pun" style="color:#000000;">).</span><span class="pln" style="color:#000000;">
setVisibility</span><span class="pun" style="color:#000000;">(</span><span class="typ" style="color:#2b91af;">View</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">INVISIBLE</span><span class="pun" style="color:#000000;">);</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">}</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">}</span></code>


上面的例子实现了四个回调函数,并在代码注释中表明了它们会运行在哪个线程。可以看到,
onPreExecute
onProgressUpdate
onPostExecute
方法都运行在主线程,所以可以安全地在这些线程中更新UI。每次触发
onProgressUpdate
回调函数都会调用
publishProgress
,这样可以更新进度条。

通过
AsyncTask
类,开发者可以很容易在其他线程中执行耗时的任务,也可以在需要时很方便地和主线程通信。使用
AsyncTask
唯一的问题是该类的实例只能使用一次,这意味着每次执行操作都要新建一个
MyAsyncTask
对象。虽然是个轻量级的类(实际的线程是由
ExecutorService
管理的),但它不适合那些频繁的操作,因为这会快速聚集需要垃圾回收的对象,并最终导致应用程序界面卡顿。

此外,
AsyncTask
不能对操作设置执行时间,也无法间隔一段时间执行操作。它适合文件下载,以及不会频繁发生或通过用户交互等类似情况的操作。然而,由于容易实现,
AsyncTask
很可能是开发时首选的类。


Handler类

当需要更细粒度地控制在一个单独的线程中执行操作时,
Handler
类会是一个很有用的工具。该类允许开发者准确地控制操作的执行时间,还可以重复多次使用它。执行操作的线程会一直运行,直到被显式地终止。
Looper
类会处理幕后的事情,但开发者很少需要直接和它打交道,相反可以通过包装类
HandlerThread
创建它。下面的例子展示了如何在
Activity
中创建一个
Handler
实例。
<code style="padding: 0px; font-family: Menlo, Monaco, Consolas, 'Courier New', monospace; color: inherit; border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; background-color: transparent; border: 0px;"><span class="kwd" style="color:#008b;">public</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">class</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#2b91af;">SampleActivity</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">extends</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#2b91af;">Activity</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">implements</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#2b91af;">Handler</span><span class="pun" style="color:#000000;">.</span><span class="typ" style="color:#2b91af;">Callback</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">private</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#2b91af;">Handler</span><span class="pln" style="color:#000000;"> mHandler</span><span class="pun" style="color:#000000;">;</span><span class="pln" style="color:#000000;">

</span><span class="lit" style="color:#8000;">@Override</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">public</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">void</span><span class="pln" style="color:#000000;"> onCreate</span><span class="pun" style="color:#000000;">(</span><span class="typ" style="color:#2b91af;">Bundle</span><span class="pln" style="color:#000000;"> savedInstanceState</span><span class="pun" style="color:#000000;">)</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">super</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">onCreate</span><span class="pun" style="color:#000000;">(</span><span class="pln" style="color:#000000;">savedInstanceState</span><span class="pun" style="color:#000000;">);</span><span class="pln" style="color:#000000;">
setContentView</span><span class="pun" style="color:#000000;">(</span><span class="pln" style="color:#000000;">R</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">layout</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">main</span><span class="pun" style="color:#000000;">);</span><span class="pln" style="color:#000000;">
</span><span class="com" style="color:gray;">// 使用Looper开启一个新线程</span><span class="pln" style="color:#000000;">
</span><span class="typ" style="color:#2b91af;">HandlerThread</span><span class="pln" style="color:#000000;"> handlerThread
</span><span class="pun" style="color:#000000;">=</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">new</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#2b91af;">HandlerThread</span><span class="pun" style="color:#000000;">(“</span><span class="typ" style="color:#2b91af;">BackgroundThread</span><span class="pun" style="color:#000000;">”);</span><span class="pln" style="color:#000000;">
handlerThread</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">start</span><span class="pun" style="color:#000000;">();</span><span class="pln" style="color:#000000;">
</span><span class="com" style="color:gray;">// 创建Handler对象</span><span class="pln" style="color:#000000;">
mHandler </span><span class="pun" style="color:#000000;">=</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">new</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#2b91af;">Handler</span><span class="pun" style="color:#000000;">(</span><span class="pln" style="color:#000000;">handlerThread</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">getLooper</span><span class="pun" style="color:#000000;">(),</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">this</span><span class="pun" style="color:#000000;">);</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">}</span><span class="pln" style="color:#000000;">

</span><span class="lit" style="color:#8000;">@Override</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">protected</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">void</span><span class="pln" style="color:#000000;"> onDestroy</span><span class="pun" style="color:#000000;">()</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">super</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">onDestroy</span><span class="pun" style="color:#000000;">();</span><span class="pln" style="color:#000000;">
</span><span class="com" style="color:gray;">// 关闭Looper线程</span><span class="pln" style="color:#000000;">
mHandler</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">getLooper</span><span class="pun" style="color:#000000;">().</span><span class="pln" style="color:#000000;">quit</span><span class="pun" style="color:#000000;">();</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">}</span><span class="pln" style="color:#000000;">

</span><span class="lit" style="color:#8000;">@Override</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">public</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">boolean</span><span class="pln" style="color:#000000;"> handleMessage</span><span class="pun" style="color:#000000;">(</span><span class="typ" style="color:#2b91af;">Message</span><span class="pln" style="color:#000000;"> message</span><span class="pun" style="color:#000000;">)</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
</span><span class="com" style="color:gray;">// 处理消息...</span><span class="pln" style="color:#000000;">
</span><span class="com" style="color:gray;">// 回收消息对象</span><span class="pln" style="color:#000000;">
message</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">recycle</span><span class="pun" style="color:#000000;">();</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">return</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">true</span><span class="pun" style="color:#000000;">;</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">}</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">}</span></code>


通过新建的
Handler
对象,开发者可以安全地精确安排操作的执行时间。使用
Handler
类最常见的方式是发送
Message
。当向后台线程传递数据和参数时,这些消息对象简单、易于创建,并且可以重用。
Message
对象通常是由它的公有整型成员变量
what
定义的,可以在
handleMessage
回调函数中将其作为
switch-case
语句的一个标志位来使用它。还有两个名为
arg1
arg2
的整型成员变量,它们用于创建低开销的参数,以及
obj
成员变量(可以存储任意单个对象的引用)。如果需要的话,还可以用
setData(Bundle
obj)
方法设置更复杂的数据。我们可以使用多种方法给
Handler
发送消息,下面列出最常见的三种:
<code style="padding: 0px; font-family: Menlo, Monaco, Consolas, 'Courier New', monospace; color: inherit; border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; background-color: transparent; border: 0px;"><span class="kwd" style="color:#008b;">public</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">void</span><span class="pln" style="color:#000000;"> sendMessageDemo</span><span class="pun" style="color:#000000;">(</span><span class="typ" style="color:#2b91af;">Object</span><span class="pln" style="color:#000000;"> data</span><span class="pun" style="color:#000000;">)</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
</span><span class="com" style="color:gray;">// 创建一个带有data参数的Message,然后立刻把它发送到handler执行</span><span class="pln" style="color:#000000;">
</span><span class="typ" style="color:#2b91af;">Message</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">obtain</span><span class="pun" style="color:#000000;">(</span><span class="pln" style="color:#000000;">mHandler</span><span class="pun" style="color:#000000;">,</span><span class="pln" style="color:#000000;"> SYNC_DATA</span><span class="pun" style="color:#000000;">,</span><span class="pln" style="color:#000000;"> data</span><span class="pun" style="color:#000000;">).</span><span class="pln" style="color:#000000;">sendToTarget</span><span class="pun" style="color:#000000;">();</span><span class="pln" style="color:#000000;">
</span><span class="com" style="color:gray;">// 立刻给handler发送一个简单的空消息</span><span class="pln" style="color:#000000;">
mHandler</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">sendEmptyMessage</span><span class="pun" style="color:#000000;">(</span><span class="pln" style="color:#000000;">SYNC_DATA</span><span class="pun" style="color:#000000;">);</span><span class="pln" style="color:#000000;">
</span><span class="com" style="color:gray;">// 给handler发送一个简单的空消息,该消息会在30秒后执行</span><span class="pln" style="color:#000000;">
mHandler</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">sendEmptyMessageAtTime</span><span class="pun" style="color:#000000;">(</span><span class="pln" style="color:#000000;">SYNC_DATA</span><span class="pun" style="color:#000000;">,</span><span class="pln" style="color:#000000;">
THIRTY_SECONDS_IN_MILLISECONDS</span><span class="pun" style="color:#000000;">);</span><span class="pln" style="color:#000000;">
</span><span class="com" style="color:gray;">// 给handler发送带有arguments和obj参数的消息,并在两分钟后执行</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">int</span><span class="pln" style="color:#000000;"> recipient </span><span class="pun" style="color:#000000;">=</span><span class="pln" style="color:#000000;"> getRecipientId</span><span class="pun" style="color:#000000;">();</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">int</span><span class="pln" style="color:#000000;"> priority </span><span class="pun" style="color:#000000;">=</span><span class="pln" style="color:#000000;"> </span><span class="lit" style="color:#8000;">5</span><span class="pun" style="color:#000000;">;</span><span class="pln" style="color:#000000;">
</span><span class="typ" style="color:#2b91af;">Message</span><span class="pln" style="color:#000000;"> msg </span><span class="pun" style="color:#000000;">=</span><span class="pln" style="color:#000000;"> mHandler</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">obtainMessage</span><span class="pun" style="color:#000000;">(</span><span class="pln" style="color:#000000;">SYNC_DATA</span><span class="pun" style="color:#000000;">,</span><span class="pln" style="color:#000000;"> recipient</span><span class="pun" style="color:#000000;">,</span><span class="pln" style="color:#000000;">
priority</span><span class="pun" style="color:#000000;">,</span><span class="pln" style="color:#000000;"> data</span><span class="pun" style="color:#000000;">);</span><span class="pln" style="color:#000000;">
mHandler</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">sendMessageDelayed</span><span class="pun" style="color:#000000;">(</span><span class="pln" style="color:#000000;">msg</span><span class="pun" style="color:#000000;">,</span><span class="pln" style="color:#000000;"> TWO_MINUTES_IN_MILLISECONDS</span><span class="pun" style="color:#000000;">);</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">}</span></code>


前面两个例子表明既可以用
Message
类也可以用
Handler
对象创建和发送消息。在第三个和第四个例子中,可以看到如何精确到毫秒来安排消息的处理。

循环线程会从消息队列中读取
Message
对象,然后把它发送到回调函数中。多个
Handler
对象可以共用一个回调函数,就像代理方法一样,处理应用程序消息会很有用。甚至可以在
Activity
Service
之间共享回调函数。实现回调函数最有效的方式是在实现它的类中保持所有代表
what
值的常量,然后用标准的
switch-case
语句处理每种消息类型。前面的例子在
Activity
中实现了回调函数,但是使用一个单独的类并把应用程序的
Context
传给它通常会更有用,因为这样就可以在应用程序的各个部分中使用它。下面是一个典型的回调函数示例:
<code style="padding: 0px; font-family: Menlo, Monaco, Consolas, 'Courier New', monospace; color: inherit; border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; background-color: transparent; border: 0px;"><span class="com" style="color:gray;">// 用于what成员变量的常量值</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">public</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">static</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">final</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">int</span><span class="pln" style="color:#000000;"> SYNC_DATA </span><span class="pun" style="color:#000000;">=</span><span class="pln" style="color:#000000;"> </span><span class="lit" style="color:#8000;">10</span><span class="pun" style="color:#000000;">;</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">public</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">static</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">final</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">int</span><span class="pln" style="color:#000000;"> PING_SERVER </span><span class="pun" style="color:#000000;">=</span><span class="pln" style="color:#000000;"> </span><span class="lit" style="color:#8000;">20</span><span class="pun" style="color:#000000;">;</span><span class="pln" style="color:#000000;">

</span><span class="lit" style="color:#8000;">@Override</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">public</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">boolean</span><span class="pln" style="color:#000000;"> handleMessage</span><span class="pun" style="color:#000000;">(</span><span class="typ" style="color:#2b91af;">Message</span><span class="pln" style="color:#000000;"> message</span><span class="pun" style="color:#000000;">)</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">switch</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">(</span><span class="pln" style="color:#000000;">message</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">what</span><span class="pun" style="color:#000000;">)</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">case</span><span class="pln" style="color:#000000;"> SYNC_DATA</span><span class="pun" style="color:#000000;">:</span><span class="pln" style="color:#000000;">
</span><span class="com" style="color:gray;">// 执行耗时的网络输入/输出操作</span><span class="pln" style="color:#000000;">
syncDataWithServer</span><span class="pun" style="color:#000000;">(</span><span class="pln" style="color:#000000;">message</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">obj</span><span class="pun" style="color:#000000;">);</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">break</span><span class="pun" style="color:#000000;">;</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">case</span><span class="pln" style="color:#000000;"> PING_SERVER</span><span class="pun" style="color:#000000;">:</span><span class="pln" style="color:#000000;">
</span><span class="com" style="color:gray;">// ping服务器,应该定期执行</span><span class="pln" style="color:#000000;">
pingServer</span><span class="pun" style="color:#000000;">();</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">break</span><span class="pun" style="color:#000000;">;</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">}</span><span class="pln" style="color:#000000;">
</span><span class="com" style="color:gray;">// 回收消息对象以便节省内存</span><span class="pln" style="color:#000000;">
message</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">recycle</span><span class="pun" style="color:#000000;">();</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">return</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">true</span><span class="pun" style="color:#000000;">;</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">}</span></code>


本例中的
handleMessage
回调只实现了两个操作,
SYNC_DATA
PING_SERVER
。第一个可能会被用户事件触发,比如,保存文件或者准备好将新数据上传到服务器。第二个应该每间隔一段时间执行一次。然而,
Handler
类并没有方法间隔地发送消息,所以开发者要自己实现这种行为。


1. 间隔地执行操作

假设
Activity
一启动,就每分钟ping一次服务器。退出
Activity
后停止执行ping操作。

接下来的例子在
onResume()
onPause()
中增加了对
Handler
的调用(前面有如何创建
Handler
实例的例子),这样就能在
Activity
显示或者消失时有效地执行上面的操作。在
onResume
方法中,把是否需要ping服务器的布尔值设置成
true
,然后立刻发送一个
PING_SERVER
消息(第一个ping操作应尽快发生)。消息会到达前面例子所述的回调函数中,并在该回调函数中执行
pingServer()
方法。
<code style="padding: 0px; font-family: Menlo, Monaco, Consolas, 'Courier New', monospace; color: inherit; border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; background-color: transparent; border: 0px;"><span class="kwd" style="color:#008b;">public</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">class</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#2b91af;">SampleActivity</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">extends</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#2b91af;">Activity</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">implements</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#2b91af;">Handler</span><span class="pun" style="color:#000000;">.</span><span class="typ" style="color:#2b91af;">Callback</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">private</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">static</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">final</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#2b91af;">String</span><span class="pln" style="color:#000000;"> PING_URL </span><span class="pun" style="color:#000000;">=</span><span class="pln" style="color:#000000;"> </span><span class="str" style="color:#8000;">"http://www.server.com/ping”;
private static final int SIXTY_SECONDS_IN_MILLISECONDS = 60 * 1000;
public static final int SYNC_DATA = 10;
public static final int PING_SERVER = 20;
private Handler mHandler;
private boolean mPingServer = false;
private int mFailedPings = 0;

简单起见,移除了前面例子的代码

@Override
protected void onResume() {
super.onResume();
mPingServer = true;
mHandler.sendEmptyMessage(PING_SERVER);
}

@Override
protected void onPause() {
super.onPause();
mPingServer = false;
mHandler.removeMessages(PING_SERVER);
}

private void pingServer() {
HttpURLConnection urlConnection;
try {
URL pingUrl = new URL(PING_URL);
urlConnection = (HttpURLConnection) pingUrl.openConnection();
urlConnection.setRequestMethod("</span><span class="pln" style="color:#000000;">GET</span><span class="str" style="color:#8000;">");
urlConnection.connect();
if (urlConnection.getResponseCode() == 200) {
mFailedPings = 0;
}// 这儿也需要处理网络失败的情况
} catch (IOException e) {
// 还需要处理网络错误
} finally {
if (urlConnection != null) urlConnection.disconnect();
}
if (mPingServer) {
mHandler.sendEmptyMessageDelayed(PING_SERVER,
SIXTY_SECONDS_IN_MILLISECONDS);
}
}
}</span></code>


pingServer()
方法中,通过发送一个简单的HTTP请求来看服务器是否还处在活动中。一旦请求完成,需要检查是否要继续ping服务器,如果是的话,60秒后再发送一个
PING_SERVER
消息。在
onPause()
方法中,把该布尔值设置成
false
,然后移除消息队列中所有的
PING_SERVER
消息。


2. 在Handler中使用
MainLooper

因为在构造函数中传递
Looper
对象可以为
Handler
分配线程,所以我们可以创建一个处理主线程消息的
Handler
。如果想避免使用
runOnUiThread()
方法,这样做特别有用。经常使用
runOnUiThread
会导致代码丑陋且低效。笔者经常在应用程序中使用这种方式,这样就可以在主线程和后台线程之间简单地发送消息。
<code style="padding: 0px; font-family: Menlo, Monaco, Consolas, 'Courier New', monospace; color: inherit; border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; background-color: transparent; border: 0px;"><span class="lit" style="color:#8000;">@Override</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">public</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">boolean</span><span class="pln" style="color:#000000;"> handleMessage</span><span class="pun" style="color:#000000;">(</span><span class="typ" style="color:#2b91af;">Message</span><span class="pln" style="color:#000000;"> message</span><span class="pun" style="color:#000000;">)</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">switch</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">(</span><span class="pln" style="color:#000000;">message</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">what</span><span class="pun" style="color:#000000;">)</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">case</span><span class="pln" style="color:#000000;"> SYNC_DATA</span><span class="pun" style="color:#000000;">:</span><span class="pln" style="color:#000000;">
syncDataWithServer</span><span class="pun" style="color:#000000;">(</span><span class="pln" style="color:#000000;">message</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">obj</span><span class="pun" style="color:#000000;">);</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">break</span><span class="pun" style="color:#000000;">;</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">case</span><span class="pln" style="color:#000000;"> SET_PROGRESS</span><span class="pun" style="color:#000000;">:</span><span class="pln" style="color:#000000;">
</span><span class="typ" style="color:#2b91af;">ProgressBar</span><span class="pln" style="color:#000000;"> progressBar </span><span class="pun" style="color:#000000;">=</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">(</span><span class="typ" style="color:#2b91af;">ProgressBar</span><span class="pun" style="color:#000000;">)</span><span class="pln" style="color:#000000;"> findViewById</span><span class="pun" style="color:#000000;">(</span><span class="pln" style="color:#000000;">R</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">id</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">progressBar</span><span class="pun" style="color:#000000;">);</span><span class="pln" style="color:#000000;">
progressBar</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">setProgress</span><span class="pun" style="color:#000000;">(</span><span class="pln" style="color:#000000;">message</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">arg1</span><span class="pun" style="color:#000000;">);</span><span class="pln" style="color:#000000;">
progressBar</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">setMax</span><span class="pun" style="color:#000000;">(</span><span class="pln" style="color:#000000;">message</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">arg2</span><span class="pun" style="color:#000000;">);</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">break</span><span class="pun" style="color:#000000;">;</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">}</span><span class="pln" style="color:#000000;">
message</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">recycle</span><span class="pun" style="color:#000000;">();</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">return</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">true</span><span class="pun" style="color:#000000;">;</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">}</span></code>


前面的
handleMessage
例子可以接收两种类型的消息,
SYNC_DATA
SET_PROGRESS
。第一个需要运行在一个单独的线程中,而第二个由于要更新UI需要运行在主线程中。要做到这一点,需要创建一个额外的
Handler
对象来发送消息,以便主线程处理。
<code style="padding: 0px; font-family: Menlo, Monaco, Consolas, 'Courier New', monospace; color: inherit; border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; background-color: transparent; border: 0px;"><span class="lit" style="color:#8000;">@Override</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">public</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">void</span><span class="pln" style="color:#000000;"> onCreate</span><span class="pun" style="color:#000000;">(</span><span class="typ" style="color:#2b91af;">Bundle</span><span class="pln" style="color:#000000;"> savedInstanceState</span><span class="pun" style="color:#000000;">)</span><span class="pln" style="color:#000000;"> </span><span class="pun" style="color:#000000;">{</span><span class="pln" style="color:#000000;">
</span><span class="kwd" style="color:#008b;">super</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">onCreate</span><span class="pun" style="color:#000000;">(</span><span class="pln" style="color:#000000;">savedInstanceState</span><span class="pun" style="color:#000000;">);</span><span class="pln" style="color:#000000;">
setContentView</span><span class="pun" style="color:#000000;">(</span><span class="pln" style="color:#000000;">R</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">layout</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">main</span><span class="pun" style="color:#000000;">);</span><span class="pln" style="color:#000000;">
mMainHandler </span><span class="pun" style="color:#000000;">=</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">new</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#2b91af;">Handler</span><span class="pun" style="color:#000000;">(</span><span class="pln" style="color:#000000;">getMainLooper</span><span class="pun" style="color:#000000;">(),</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">this</span><span class="pun" style="color:#000000;">);</span><span class="pln" style="color:#000000;">
</span><span class="typ" style="color:#2b91af;">HandlerThread</span><span class="pln" style="color:#000000;"> handlerThread </span><span class="pun" style="color:#000000;">=</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">new</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#2b91af;">HandlerThread</span><span class="pun" style="color:#000000;">(“</span><span class="typ" style="color:#2b91af;">BackgroundThread</span><span class="pun" style="color:#000000;">”);</span><span class="pln" style="color:#000000;">
handlerThread</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">start</span><span class="pun" style="color:#000000;">();</span><span class="pln" style="color:#000000;">
mHandler </span><span class="pun" style="color:#000000;">=</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">new</span><span class="pln" style="color:#000000;"> </span><span class="typ" style="color:#2b91af;">Handler</span><span class="pun" style="color:#000000;">(</span><span class="pln" style="color:#000000;">handlerThread</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">getLooper</span><span class="pun" style="color:#000000;">(),</span><span class="pln" style="color:#000000;"> </span><span class="kwd" style="color:#008b;">this</span><span class="pun" style="color:#000000;">);</span><span class="pln" style="color:#000000;">
</span><span class="pun" style="color:#000000;">}</span></code>


需要注意的是本例的
onCreate
方法和之前的基本相同。唯一例外的地方是创建
mMainHandler
的一行代码。不是启动一个
HandlerThread
,而是简单地获取主线程的
Looper
对象。这并不影响主线程的运行,只需要一个额外的
Handler
,然后在主线程处理回调。系统会在回调函数中处理发送给该
Handler
的消息,该回调函数同样会处理第二个用于后台操作
Handler
的消息。如果要更新
ProgressBar
,只需如下所示发送一个简单的消息:
<code style="padding: 0px; font-family: Menlo, Monaco, Consolas, 'Courier New', monospace; color: inherit; border-top-left-radius: 3px; border-top-right-radius: 3px; border-bottom-right-radius: 3px; border-bottom-left-radius: 3px; background-color: transparent; border: 0px;"><span class="typ" style="color:#2b91af;">Message</span><span class="pun" style="color:#000000;">.</span><span class="pln" style="color:#000000;">obtain</span><span class="pun" style="color:#000000;">(</span><span class="pln" style="color:#000000;">mMainHandler</span><span class="pun" style="color:#000000;">,</span><span class="pln" style="color:#000000;"> SET_PROGRESS</span><span class="pun" style="color:#000000;">,</span><span class="pln" style="color:#000000;"> progress</span><span class="pun" style="color:#000000;">,</span><span class="pln" style="color:#000000;"> maxValue</span><span class="pun" style="color:#000000;">).</span><span class="pln" style="color:#000000;">
sendToTarget</span><span class="pun" style="color:#000000;">();</span></code>


任何必须在主线程运行的操作都可以使用这种方法。既可以像上面一样发送简单的
Message
对象,也可以给
obj
成员变量设置更复杂的数据,或者在
setData
中设置
Bundle
参数。只需要确保把消息发送给正确的
Handler
即可。


选择合适的线程

前面显示了三种在Android上创建和使用线程的方式。API中和线程相关的类还有
ExecutorService
Loader
ExecutorService
适合处理并行运行的多个任务,这非常适合编写响应多客户端的服务器应用。
AsyncTask
内部同样使用
ExecutorService
处理多线程。如果希望能够并行执行多个
AsyncTask
,也可以通过使用正确的
ExecutorService
来完成。

如果需要一个专门的线程来进行操作,可以从前面所示的三个例子开始。不建议直接使用
Thread
类,除非是要完全控制线程的执行。大多数情况下推荐使用
AsyncTask
Handler
类,具体使用哪一个取决于具体的需求。如果不是很频繁地执行操作,比如超过每分钟一次,那么
AsyncTask
可能是个不错的选择。如果需要安排操作的时间或者需要快速间隔地执行操作,
Handler
会是更好的选择。从长远来看,使用
Handler
生成的代码更少,不过
AsyncTask
更容易使用。


小结

本文介绍了Java SE 5.0的几个高级的特性,有了JIT Dalvik虚拟机,开发者就可以在Android上安全地使用它们了。了解和使用这些特性可以简化代码编写,从而让代码更易于测试和维护。本文同样介绍了怎样使用
java.
util.concurrent
包中的并发API,而不需要自己实现队列和锁。重复造轮子是一个很常见但是很大的错误:你需要测试和维护更多的代码,而代码多了也更容易引入bug。

本文同样解释了怎样避免内存分配的诸多陷阱。如果在代码中创建了很多临时的、生命周期短的变量,应用程序很可能在用户界面上表现不佳。高效且安全地重用对象可以带来更流畅的用户体验。

本文最后介绍了三种在Android上使用线程的方法,但只推荐使用其中的两个(
AsyncTask
Handler
)。多线程是一个复杂的话题,往往是很多难以发现的bug的原因。始终尝试使用现有的工具类来处理线程,因为它们会让事情变得更简单,且允许开发者关注自己代码的功能。
Java是一门强大的语言,它使开发人员更容易表达他们想要实现的目标。学会如何更有效地使用它会让你成为更优秀的开发者,并帮助开发者创建高质量的代码。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: