admin管理员组

文章数量:1660066

原文:Pro Android Flash

协议:CC BY-NC-SA 4.0

九、设计人员——开发人员工作流

无论您是设计师还是开发人员,现在都是进入移动开发的激动人心的时刻,这是一个充满潜力和机会的年轻行业。但是移动开发行业确实面临着其他软件开发项目所面临的同样的挑战,那些通信和工作流的挑战。图 9–1 取笑软件项目中的沟通和解释问题。这幅漫画与许多公司的做法相差无几。一个项目可能有许多实际的需求,但是大多数参与的人只会表达那些他们关心或感兴趣的需求。

图 9–1。 【项目如何真正工作】出自[www.projectcartoon](http://www.projectcartoon) 1 知识共享署名下许可 3.0 未许可许可:[creativecommons/licenses/by/3.0/](http://creativecommons/licenses/by/3.0/)


2006 年 7 月 24 日, 1 “项目如何真正运作”,【http://www.projectcartoon】??

项目可能会在很多地方失败。一个智能的工作流可以真正帮助缓解这些痛点,这样客户要求的就是设计师设计的,开发者执行的。但是首先必须理解设计者和开发者的角色,以及他们使用的工具。

视觉设计师的角色

设计师的角色是理解客户的需求,将这些转化为应用,并为其创造视觉设计。设计师与客户讨论应用应该如何工作,GUI 如何完成用户故事,以及为什么它会这样工作。这是一条双行道,因为客户的输入也被考虑在内。设计师也根据开发者的需求调整视觉设计。有时候,开发人员可以预见设计人员没有意识到的技术挑战,在这种情况下,他们可以也应该合作解决问题。有时候合作只是澄清事情是如何运作的。其他时候,它可能导致设计和技术限制之间的折衷。

“设计是一种有意识的、直觉的努力,旨在建立有意义的秩序。”

–维克多·帕帕内克,设计师兼教育家

从 Adobe Device Central 开始

Adobe Device Central 简化了移动电话、平板电脑和消费电子设备的内容创建。它允许设计人员和开发人员计划、预览和测试移动体验。您可以通过动态更新的在线设备库访问最新的设备配置文件,并在设备外观环境中模拟背光超时和阳光反射等显示条件,以针对现实条件调整设计。

提示欲了解更多信息,请参见[www.adobe/products/devicecentral.html](http://www.adobe/products/devicecentral.html)

使用设备中心

Adobe Device Central CS5.5 与大多数设计程序集成,包括 Photoshop、Illustrator、Fireworks 和 Flash,使您能够利用手机数据,从移动项目的开始到最终启动提高工作效率。

从 Device Central 创建新文档

开始新的移动项目时,Device Central CS5.5 是一个不错的起点。当您启动 Device Central 时,会出现一个欢迎屏幕(参见 Figure 9–2)。

图 9–2。 从 CS5 中部设备启动新的 Fireworks 文档;从 Device Central CS4 开始,添加了 Captivate 和 Fireworks 文件格式。

  1. 从右边的栏中点击您想要创建的文件类型(参见 Figure 9–3)开始一个新项目。中间的消息指出,“要开始,请在‘文本设备’面板中选择设备。”

  2. Click the Browse button, located in the upper right corner. This will display a list of devices to create and test against (see Figure 9–3).

    图 9–3。 浏览设备库中的设备,按名称排序,显示创建者评级或搜索特定设备。

  3. 选择要测试的设备,并将其拖至测试设备面板。

  4. Double-click the device name to view the device details (see Figure 9–4).

    图 9–4。 点击并拖动设备到测试设备面板,将其添加到要测试的设备

  5. 准备好基于所选配置文件创建新文件后,双击左侧列出的测试设备中的配置文件。然后,点击右上角的创建(参见图 9–5)。

图 9–5。 从测试设备面板双击设备配置文件。单击右上角的 Create,基于该配置文件创建一个新文档。

新文档会自动设置为适合目标设备的正确显示尺寸和屏幕方向。现在,您已经准备好创建自己的手机设计了。在分析设备和模拟内容外观方面,Device Central 的帮助再大也不为过。这有助于加快设计工作流程,肯定比购买许多不同的设备要好。

就组织和生产力而言,Fireworks 的一个流行功能是能够在单个文件中创建具有不同尺寸、屏幕方向甚至文档分辨率的多个页面。这意味着您可以轻松地同时处理纵向和横向布局,这在针对多点触摸设备和使用加速度计时非常方便。你甚至可以将应用图标和你的主要内容保存在同一个文件中。除了 Fireworks,没有任何 Adobe 产品能做到这一点。

在 Device Central 中预览内容

为移动设备进行设计时,在设计过程中有些时候,您可能希望在实际的手机环境中预览您的作品。最快、最简单的方法是从 Photoshop、Illustrator、Flash 或 Fireworks 中启动预览。

  1. 在 Photoshop 中,选择文件保存为网络&设备…
  2. 在左下角,选择“设备中心…”
  3. 现在,您将能够看到您的设计在各种设备上的外观。
  4. 当您在 Device Central 中查看您的工作时,您可以通过在“测试设备”面板中双击不同的设备配置文件来更改设备外观。
  5. 您还可以使用 Device Central 中的显示面板来调整照明或反射,以便在不同的照明条件下测试您的内容(请参见 Figure 9–6)。

图 9–6。 在 Device Central 中预览三星 Galaxy S 上的设计,选择室内反射

创建自定义配置文件

您可能想要创建自定设备描述文件有几个原因:

  • 您会注意到桌面上仿真工作区中显示的内容与您在实际设备上看到的内容之间存在差异。
  • 出于演示目的,您希望修改设备外观(例如,删除或添加运营商徽标)。
  • 您制造设备并需要创建新的配置文件(一旦设备发货,自定义配置文件就可以分发给社区)。

创建自定描述文件的第一步是制作现有设备描述文件的副本,用作模板。我建议选择尽可能与您想要的自定义配置文件相似的内容。原始配置文件和您的自定义配置文件之间的相似之处越多,您以后在编辑单个数据点时需要做的工作就越少。

  1. 在 Device Central 中,单击“浏览”(位于右上角),以便进入“浏览”工作区。
  2. 如果您移动了面板,您可以随时通过选择窗口 工作区 重置浏览来恢复默认设置。
  3. 右键单击 Flash Player 10.1 32 320×480 多点触控配置文件,并选择创建可编辑副本(参见图 9–7)。
  4. 为配置文件键入新名称,例如,键入“My_Multitouch_320×480”,然后点按“好”。

图 9–7。 创建概要文件的可编辑副本

请注意,如果您计划与他人共享您的自定义配置文件,您应该给他们起一个既独特又有描述性的名称。此外,尽可能完整地填写所有字段。这是一个显而易见的最佳实践,有助于为整个社区的共同利益发展一个准确和完整的数据集。

在右边,您现在应该会看到一个圆形,在设备皮肤的正上方有一个铅笔,表示该配置文件现在是可编辑的。类似地,当您将指针悬停在任何属性上时,比如输入控件或语言,会出现相同的铅笔图标。如果悬停时属性不显示铅笔图标,则该属性不可编辑。

接下来,您可以直接从 CS5 设备中心编辑设备配置文件:

  1. 将指针悬停在语言上;出现铅笔图标,表示该属性是可编辑的。
  2. 单击语言并选择要显示的语言。
  3. 单击复选标记确认您的选择。

您选择的语言现在应该显示在您的自定义配置文件中。

重复这些步骤,从 Device Central 中编辑所有自定义设备配置文件信息。这种从界面编辑配置文件数据的简单而直接的方法是一种真正的时间节省,是对早期版本的巨大改进。

Adobe Photoshop

Adobe Photoshop CS5 非常注重摄影,但也用于创建应用设计,因为它在设计创建和图像编辑方面非常灵活。Adobe Photoshop CS5 拥有卓越的图像选择、图像润饰和逼真绘画的突破性功能,以及广泛的工作流程和性能增强。

**提示:**更多信息见[www.adobe/products/photoshop.html](http://www.adobe/products/photoshop.html)

一旦在 Photoshop 中创建了设计(图 9–8),工作流程中的下一步将是将这些图形引入 Flash Professional 或 Flash Builder 进行进一步开发。这可以通过分别导出每个图像,或将 Photoshop 文件(.psd)直接导入 Flash Professional 来完成。

图 9–8。 一个在 CS5 Adobe Photoshop 中创建的应用设计,包括形状层、文本和智能对象,仅举几个例子

Photoshop 到 Flash Professional 的工作流程

Flash Professional CS3 中引入的一个令人兴奋的功能是导入 PSD 文件的能力(图 9–9)。在导入时,Flash Professional 使您能够确定如何导入每个层。例如,您可以在 Flash Professional 中将文本层作为可编辑文本导入。在 Flash Professional 中,形状层也可以转换为可编辑的形状。甚至电影剪辑也可以从光栅图形创建,包括实例名称。Photoshop 中的图层在 Flash Professional 中可以显示为图层,并带有仍可编辑的图层效果。甚至物品的位置都可以保持。最终结果是 Flash Professional 中的完整设计,可以制作动画并进一步开发用于移动设备。

图 9–9。 将原 Photoshop 文件,导入到 Flash Professional 中;每个层可以不同方式导入,保持文本、形状层,甚至层效果。

尽管导入 Photoshop 文件非常容易而且非常有用,但是您必须注意一些事情。请注意,当导入许多层时,文件会变大,请考虑合并它们。例如,如果在构成背景的不同图层上有多个图形,请考虑在导入之前在 Photoshop 中合并这些图层。此外,考虑在 Flash 中绘制矢量元素,而不是导入它们。这将使您在编辑时有更多的控制权。如果有帮助的话,您甚至可以从 Photoshop 中导入一个图形作为向导,同时在 Flash Professional 中将所有部分创建为矢量元素。

如果 Photoshop 文件相当复杂,由多个图层组成背景,请考虑将这些图层合并为一个背景图层。一般的规则是,如果图形不动,看看能不能和其他图形合并。

Photoshop 到 Flash Builder 的工作流程

Flash Builder 不像 Flash Professional 那样导入 Photoshop 文件。相反,需要从 Photoshop 中导出单独的图像。最有效的方法是将每个元素分离成自己的 Photoshop 文件,并导出适当的文件类型(图 9–10)。

图 9–10。 单独 PSD 文件中的单个图形准备导出为 PNG、JPG 或 GIF。请务必保留原始 PSD 文件,以防以后需要进行更改。

在 Photoshop 中,导出图形的最佳方式是使用“文件”菜单下的“存储为 Web 和设备所用格式”选项。这使您能够选择想要导出的格式并查看其质量(Figure 9–11)。在 Flash Builder 中,您可以导入适当的文件类型,无论它是 JPG、GID、PNG、SWF 还是 FXG。

图 9–11。 使用文件从 Photoshop 中导出图形保存为 Web 和设备格式

图形文件格式

基本上有四种不同的文件类型可以在 Flash 应用中使用。您选择哪一个取决于图形的内容。

PNG-24(便携式网络图形,24 位深度)

PNG-24 可能是富图形最流行的图形文件类型之一,因为它允许不同级别的透明度和 24 位颜色。还有一个 PNG-8,它不允许透明,但文件大小更小,因为颜色深度是 8 位(256 色)。

GIF(图形交换格式)

GIF 是一种 8 位文件格式,允许多达 256 种颜色,这使文件大小保持较小。由于颜色数量有限,gif 适用于边缘锐利的线条艺术和平面颜色,如徽标。相反,该格式不用于摄影或带有渐变的图像。gif 可以用来存储游戏的低颜色精灵数据。gif 也可以用于小动画,因为它们可以包含多个帧。GIF 文件也可以有透明度,但不像 PNG-24 文件那样有不同的透明度。GIF 中的每个像素要么不透明,要么透明。

联合图像专家组

JPG 文件通常用于摄影图像。这种格式具有有损压缩,这意味着图像可以被压缩,导致文件较小,但这可能会导致图像质量的一些损失。将图像压缩到 JPG 是在保持图像质量的同时保持文件大小较小的一个很好的平衡。

FXG (Flash XML 图形)

Adobe Flash Platform 基于 XML 的图形交换格式使设计人员能够为 web、交互式和 RIA 项目的开发人员提供更多可编辑、可工作的内容。FXG 用作跨应用文件支持的图形交换格式。它基于 XML,可以包含图像、文本和矢量数据。Flash Professional、Fireworks 和 Illustrator 都可以创建 FXG 文件。然后,这些文件可以在 Flash Professional 或 Flash Builder 中使用(参见 Figure 9–12)。

图 9–12。 在 Flash Builder 中打开的一个 FXG 文件,包含矢量、文本和位图数据

所有这些文件格式都可以从大多数图像编辑程序中创建。所使用的程序在很大程度上取决于设计师最喜欢什么,但是如果我们更客观地看一看,你会注意到每个程序在移动 Flash 开发方面都有自己独特的优势。例如,FXG 格式非常灵活,可以在 Flash Builder 中向开发人员展示各种文本和矢量图形元素。PNG-24 文件格式在设计师需要具有不同透明度的像素级完美图形时非常有用。如果照片有多种颜色和阴影,并且不需要透明度,JPG 格式非常适合。最后,GIF 非常适合平面图形,比如徽标。

Adobe Illustrator

Adobe Illustrator 帮助设计师为几乎所有项目创建矢量作品。Illustrator 拥有复杂的绘图工具、自然笔刷和大量内置的省时工具,可用于矢量图像编辑。Illustrator CS5 允许用户在文件的像素网格上精确地创建和对齐矢量对象,以获得干净、清晰的光栅图形。用户还可以利用光栅效果,如投影、模糊和纹理,并跨媒体保持其外观,因为图像与分辨率无关。这使得 Illustrator 成为一个很好的开始创建图形的地方,不管输出是什么。

**提示:**更多信息见[www.adobe/products/illustrator.html](http://www.adobe/products/illustrator.html)

Illustrator 转 Flash 专业工作流程

使用 Illustrator,您可以创建移动设计,并将单个图形转换为电影剪辑元件。元件的每个实例都可以有一个实例名,就像在 Flash 中一样。可以将影片剪辑元件实例复制并粘贴到 Flash Professional 中。Flash 维护电影剪辑甚至实例名称(参见图 9–13)。

图 9–13。 在 Illustrator(左)中,您可以创建可以直接复制并粘贴到 Flash Professional(右)中的电影剪辑元件。符号和实例名称保持不变。

土坯烟花

Adobe Fireworks CS5 软件提供了为 Web 或几乎任何设备创建高度优化的图形所需的工具。Fireworks 允许您创建、导入和编辑矢量和位图图像。

Fireworks 到 Flash Builder 工作流

在 Fireworks 中创建图形后(参见 Figure 9–14),可以将其导出为最流行的图形格式,包括 FXG 和 MXML,专门用于 Flash Builder。以基于 XML 的 FXG 格式导出有助于确保为 Adobe Flash Builder 精确转换丰富的应用设计。FXG 和 MXML 都是基于 XML 的格式,可以包含可以在 Flash Builder 中打开和编辑的矢量图形和文本(参见图 9–15)。基于位图的图像被外部引用。

图 9–14。 烟花中的画面设计

图 9–15。 FXG-和 MXML-创建的文件在 Flash Builder 中打开;请注意第 12 行的文本引用以及第 5–7 行的文本标签属性。位图图像在文件外部。

**提示:**更多信息见[www.adobe/products/fireworks.html](http://www.adobe/products/fireworks.html)

开发者的角色

从技术角度来看,开发人员应该能够将最基本的设计和技术规范转化为实际的应用。优秀的开发人员在许多方面不同于他们更普通的同事。一些重要的例子如下:

  • 培养理解能力:几乎任何人都可以盲目地遵循为他们制定的指令,但是优秀的开发人员会重视理解他们正在做的事情,这样他们就可以随时发现潜在的问题和改进的机会。
  • 掌握结构和应用架构:在软件开发中,没有任何一种“正确的”做事方式,因为同一个问题通常可以用许多种方式来解决。然而,通常有一些方法比其他方法“更正确”。掌握众所周知的数据结构和应用架构意味着以最直接的方式解决问题,并以最灵活和有效的方式构建应用。
  • 专业化:专业化展示了不断学习和成长的意愿,这有助于开发人员与众不同。对移动开发技术的特殊掌握使开发人员成为任何公司的宝贵员工和资源。
开发者的工具箱

开发人员的工具箱有限。他们应该知道他们所选择的一种或多种语言的开发环境(包括编译器和调试器),以及开发团队的每个成员都需要使用的一些常用工具。这些工具通常被集成到一个平台中,该平台既充当编译器又充当调试器。这通常是用于学习语言的相同工具,因此学习开发环境通常不是一个大挑战。以下开发环境通常用于移动 Flash 开发。

Adobe Flash Professional

Adobe Flash Professional CS5.5 是制作富有表现力的交互式内容的领先创作环境,是设计人员和开发人员共享的工具。ActionScript 是使用的编码语言,可以在二进制 FLA 文件格式中编写,其中可以包含图形、声音、字体、动画,有时还可以包含设计人员添加的视频。代码片段是在 Flash Professional CS5 中引入的,可用于加速开发。ActionScript 也可以在外部 ActionScript 文件(.as)中编写,这是为Document类和其他对象类例行完成的。通常这取决于项目类型来决定动作脚本将被写在哪里。对于较小的项目,在 FLA 中编写 ActionScript 就可以了。对于较大的项目,许多开发人员更喜欢外部 ActionScript 文件来帮助组织他们的代码。

Flash Professional CS5.5 包括舞台元件栅格化,以提高移动设备上复杂矢量对象的渲染性能。此外,还添加了 20 多个新的代码片段,包括用于创建移动和 AIR 应用的代码片段。在通过 USB 电缆连接的支持 Adobe AIR 的设备上可以进行源代码级调试,直接在设备上运行内容。

**提示:**更多信息见[www.adobe/products/flash.html](http://www.adobe/products/flash.html)

Flash 专业工作流程

设计师通常要么在 Flash Professional 中创建图形,要么从其他来源导入图形。FLA 可以在会议中用作原型,向客户展示最终的应用将如何工作,和/或它可以用作最终的源。最终结果如下(参见图 9–16)。

  • 客户端请求应用。
  • 设计师创建一个初始设计。
  • 设计者给开发者一个 FLA 或图形文件。
  • 开发者对设计进行编程,并从设计者的 FLA 中合并图形,或者导入设计者的图形文件。
  • 客户请求更改。
  • 如果设计发生变化,设计师会发送新的图形文件。
  • 开发者用新的图形/动画更新应用。

图 9–16。 典型的客户、设计师、开发人员工作流程

Flash Builder 4.5

Adobe Flash Builder 4.5(以前称为 Adobe Flex Builder)是一个基于 Eclipse 的开发工具,用于使用 ActionScript 和开源 Flex 框架快速构建富有表现力的移动、web 和桌面应用。Flash Builder 4.5 允许开发人员为一个或多个移动平台(Android、BlackBerry 或 iOS)构建独立的 Flex/ActionScript 应用。设计和代码视图支持使用移动就绪组件进行移动开发。使用移动 Adobe AIR 运行时仿真器在桌面上测试移动应用,或者使用一键式打包、部署和启动流程在本地连接的移动设备上进行测试。开发人员可以将所需资源部署、打包和签名为特定于平台的安装程序文件,以便上传到移动应用分发站点或商店。

Flash Builder 工作流程

Flash Builder 可以导入许多流行的图形文件格式(参见 Figure 9–17)。内容应该决定将使用什么类型的文件。对于摄影,可以使用 JPG。如果有动画,将需要一个 SWF 文件。大概最灵活的文件格式是 FXG。它是一种基于 XML 的格式,公开了大量内容,使开发人员能够进一步编辑或在需要时进行动态更改。

图 9–17。 将图形文件导入 Flash Builder

在 Flex 框架中使用 Flash Builder 时,情况与 FLA 工作流略有不同。首先,没有 Fla。Flex 就像传统的 web 开发一样。你所有的文件都在一个文件夹中,由开发人员来组织它们并将它们全部签入到源代码控制中(如果正在使用的话)。代码也在适当的文件夹中公开和组织,或者作为 MXML (Flex 框架)文件或者作为(ActionScript)文件。因此,设计师目前无法轻松地在他们自己的设计“沙盒”中游戏,就像他们可以在 Flash Professional 中使用自己的 FLA 一样。这有利有弊。好处是没有设计师可以编辑开发者的作品。缺点是设计师不能检验他或她的设计。决定使用 Flash Builder 还是 Flash Professional 工作流取决于开发人员的技能和偏好。

总结

一个好的工作流程真的可以决定一个项目成功还是失败。你可以在一个项目中拥有最好的设计师和开发人员,但是如果他们不能有效地一起工作,交换想法和素材,所有这些都很容易失去。设计师设想的可能不是开发人员执行的,项目经理解释的可能不是最初要求的。你可以很容易地看到一个项目在很多地方可能会失败。一个好的工作流可以缓解过程中的许多痛点,并且可以很容易地确定一个项目是否失败。

十、性能调整

业界认为闪存技术发展缓慢。媒体上的负面言论进一步强化了这一点,例如苹果公司首席执行官史蒂夫·乔布斯在他的《关于 Flash 的思考》中称“Flash 在移动设备上的表现不佳”

虽然可以用 Flash 或任何其他移动技术编写运行缓慢的应用,但使用正确的工具和技术,您也可以创建快速、响应迅速的应用。就像本机应用一样,Flash 技术让您可以利用硬件加速的视频播放和 GPU 加速的渲染。正确使用这些技术可以显著提高应用的性能,减少电池消耗。

还很容易陷入这样一个陷阱:将针对桌面应用优化的现有内容用于移动应用。移动设备的屏幕更小,处理器更慢,内存更少,网络连接通常更慢或不可靠。如果您在构建应用时考虑到这些约束,并经常在目标移动设备上进行测试,那么您将会获得更好的结果。

在本章中,您将详细了解 Flash 运行时是如何工作的,从而理解限制您的应用性能的关键因素。然后,我们将深入研究几个不同的性能敏感领域,包括图像、媒体和文本。在此过程中,我们将回顾 ActionScript 和 Flex 中专门为优化移动内容而引入的新 API,您应该充分利用这些 API。

总会有一些写得很差的 Flash 应用让批评者指出 Flash 不适合移动设备的原因。但是,通过遵循本章中的建议和准则,您将确保您的应用不是其中之一。


2011 年 4 月,www.apple/hotnews/thoughts-on-flash/,苹果公司

移动性能调优基础知识

性能调优移动应用与桌面应用并没有太大的不同,并分为相同的三个基本考虑事项:

  • 执行时间
  • 内存使用
  • 应用大小

执行时间是在显示每一帧之前,应用在处理上花费的 CPU 周期。这可能是您编写的用于准备或更新内容的应用逻辑、您的应用等待外部服务器响应的网络 I/O,或者在底层 Flash 运行时中用于验证或渲染图形的时间。

内存是应用运行时使用的设备 RAM 的数量。这通常会随着应用的执行而增长,直到达到一个稳定状态,不再创建其他对象,或者新对象的数量大致等于被释放对象的数量。内存的持续增长可能表明存在内存泄漏,即没有释放资源或者没有取消对不可见/屏幕外对象的引用。

移动设备增加了额外的复杂性,主系统和 GPU 上都有内存限制。垃圾收集也是一个因素,因为当收集器复制活动对象以释放未使用的内存时,使用的内存通常是实际需要的两倍。

应用的大小也是一个重要的考虑因素,因为它会影响应用从 Android Market 的初始下载及其启动性能。编译后的 ActionScript 实际上非常紧凑,因此静态素材(如嵌入到项目中的图像和视频)通常会决定应用的大小。

所有这些因素对于决定应用的整体性能都很重要。然而,比执行时间、内存和应用大小的绝对度量更重要的是最终用户感受到的性能。

感知绩效与实际绩效

如果您编写了一个广泛使用的应用,您可能会遇到用户对性能不满意的情况。对于每一个抱怨性能缓慢的用户,都有数十或数百人放弃或停止使用该应用,而不是报告问题。

科罗拉多州立大学的 John Hoxmeier 和 Chris DiCesare 的研究证实了应用性能低、使用率低和用户满意度低之间的相关性。 2 通过用 100 名学生组成的控制组进行测试,他们证明了以下假设:

  • 响应时间越长,满意度越低
  • 不满意导致停止使用
  • 易用性随着满意度的下降而下降

虽然他们是在基于 web 的应用上进行测试,但是这些发现与您在基于 Flash 平台的富客户端应用上的体验非常相似。在这项研究中,花了 6 秒或更短时间的反应被认为是强有力的和足够快的,而花了 9 秒或更长时间的反应被评为非常不利。

**注意:**在这项研究中,他们还反驳了专家用户更有可能容忍较慢响应时间的假设,所以不要认为这项研究不适用于您的应用。

那么你的应用需要多快才能让用户满意呢?根据本·施奈德曼的说法, 3 你应该保持在以下界限内:

  • 打字、光标移动、鼠标选择:50-150 毫秒
  • 简单频繁的任务 : 1 秒
  • 常见任务:2-4 秒
  • 复杂任务:8-12 秒

此外,通过进度条或旋转的等待图标向用户提供关于长时间运行任务的反馈会极大地改变他们等待的意愿。除了 15 秒之外,这对于确保用户在上下文切换后等待或回来是绝对重要的。

那么这对您的 Flash 应用意味着什么呢?

Flash 应用通常利用动画和过渡来改善用户体验。如果你打算利用这些,它们需要有相对较高的帧速率,以便给用户留下应用响应迅速的印象。这些的目标应该是大约每秒 24 帧或大约 42 毫秒,这是用户感觉动画流畅的最小帧速率。在下一节中,我们将更多地讨论如何调整渲染性能来达到这个目标。


John A. Hoxmeier 和 Chris DiCesare,“系统响应时间和用户满意度:基于浏览器的应用的实验研究。” AMCIS 2000 年会议录 (2000 年)。347 号文件。

Ben Shneiderman,“计算机对人类行为的反应时间和显示率。”计算调查 16 (1984),第 265–285 页。

对于频繁的任务,例如显示细节、提交表单或拖放,您应该将响应时间控制在一秒以内。在执行这些操作时,Flash 应用比 web 应用有明显的优势,因为它们可以在后台执行任务以检索或发送数据时给用户即时反馈。

一些常见的任务,比如加载一个新的页面,或者通过一个标签或者链接来导航,可能会花费更长的时间,但是应该有一个不确定的进度指示器来让用户知道活动正在发生。此外,合理使用过渡动画可以使加载看起来比实际发生得更快。

复杂的任务,如搜索或填充大型数据列表,可能需要更长的时间,但应该限制在 12 秒内完成,或者提供一个进度条,指示任务需要多长时间才能完成。通常可以显示中间结果,如部分搜索结果或前几页数据。这将允许用户在后台加载额外数据的同时继续使用应用,从而显著改变感知的等待时间。

调整图形性能

在其核心,Flash 运行时是一个基于帧的动画引擎,处理保留模式图形。即使您正在使用 Flex 之类的高级框架构建应用,了解 Flash Player 的渲染基础也会有所帮助,这样您就可以优化处理和内容以获得最佳性能。

Flash 引擎的心跳是每秒帧数设置,它控制每秒在屏幕上绘制的帧数。虽然性能瓶颈可能会导致每秒帧数减少,但处理的帧数永远不会超过这个数字。

许多图形工具包使用所谓的即时模式渲染来绘制到屏幕上。在即时模式呈现中,应用实现了一个回调,它必须在每个时钟周期重新绘制屏幕内容。虽然这在概念上很简单,并且接近硬件实现的内容,但是它将保存状态和为动画提供连续性的工作留给了应用开发人员。

Flash 使用保留模式图形,您可以创建一个将在屏幕上渲染的所有对象的显示列表,并让框架负责在每个时钟周期渲染和传输最终图形。这更适合动画和图形应用,但基于显示列表的大小和复杂性,可能会耗费更多资源。

弹性跑道

Ted Patrick 为 Flash 播放器如何处理渲染提出了一个非常有用的概念模型,他称之为弹性跑道。 4 如图图 10–1 所示,该模型将每一帧中的工作在代码执行和渲染之间进行拆分。

图 10–1。 闪光播放器弹性跑道

代码执行是运行与该帧相关联的任何 ActionScript 所花费的时间,包括因用户输入而触发的事件处理程序、计时器和ENTER_FRAME事件。渲染包括 Flash Player 为准备显示列表、合成图像和将图形传输到屏幕上而进行的处理。为了保持稳定的帧速率,这两个活动的总持续时间不能超过为该帧分配的时间片。

那么你有多少时间来执行你所有的逻辑呢?Table 10–1 列出了一些常见的帧速率,以及处理代码执行和渲染需要多少毫秒。


4 特德·帕特里克,“Flash Player 心智模型——弹性跑道”,[ted.onflash/2005/07/flash-player-mental-model-elastic.php](http://ted.onflash/2005/07/flash-player-mental-model-elastic.php),2005 年 7 月

Flash 播放器的默认帧速率是 24fps 低于这个值的任何值对用户来说都是明显不稳定或滞后的。然而,用户可以轻松感知高达 60fps 的帧速率差异,特别是在有大量运动或滚动的任务中。拍摄高于 60fps 的帧速率通常是不值得的,尤其是考虑到大多数液晶显示器的刷新率上限为 60hz,一些设备的最大帧速率上限为 60。

当尝试诊断低帧速率时,第一步是确定您是否受到长时间代码执行或缓慢渲染的限制。代码执行是两者中比较容易分析的,因为它在您的控制之下,如果它接近或超过您的目标帧速率的总帧长度,这就是您想要开始优化的地方。

减少代码执行时间

如果您的代码执行时间比单个帧周期稍长,您可以通过优化代码来获得足够的性能。这将根据您是使用纯 ActionScript 还是基于 Flex 构建而有所不同。此外,如果您正在进行复杂或长时间运行的操作,可能需要不同的方法。

值得研究的一些常见 ActionScript 代码性能最佳实践包括:

  • 更喜欢向量而不是数组:向量数据类型经过了高度优化,比使用数组进行基本的列表操作要快得多。在某些情况下,比如大型稀疏列表,数组的性能会更好,但这是一个罕见的例外。
  • 尽可能指定强类型 : ActionScript 是动态类型的,允许你省略显式类型信息。但是,如果提供了类型信息,编译器可以生成更高效的代码。
  • 保持构造函数轻便:实时(JIT)编译器不优化变量初始化器或构造函数中的代码,强制代码以解释模式运行。一般来说,对象构造是很昂贵的,应该推迟到元素在屏幕上可见的时候。
  • 明智地使用绑定:绑定引入了额外的开销,这对更新 UI 是有意义的,但在其他地方应该避免。
  • 正则表达式开销很大:尽量少用正则表达式来验证数据。如果需要搜索,String.index 的速度要快一个数量级。

如果您正在编写一个 Flex 应用,您还需要了解以下内容:

  • 最小化组和容器的嵌套:大型对象图的测量和布局可能非常昂贵。通过保持容器尽可能的平坦,你将加速你的应用。这在构建可重复使用的网格或列表呈现器时尤为重要。
  • 更喜欢组而不是容器:新的 Spark 图形库在重新设计时考虑到了性能。因此,与容器相比,组是非常轻量级的,应该用于布局。

如果在调优代码后,代码执行仍然是瓶颈,那么您可能希望将工作负载分散到多个框架上。例如,如果您正在执行命中检测算法,可能无法检查单个帧中的所有对象。但是,如果您可以按区域对对象进行分组,并对它们进行增量处理,则工作可以分布在多个帧上,从而提高应用的渲染速度。

加速渲染

在 CPU 上运行时,Flash 使用高度优化的保留模式软件渲染器将图形绘制到屏幕上。为了渲染每一帧,它遍历 DisplayList 中所有对象的列表,以确定哪些是可见的,哪些需要绘制。

软件渲染器逐行扫描更新区域,通过查看显示列表中每个元素的顺序、位置和不透明度来计算每个像素的值。Figure 10–2 包含一个在 Flash 中创建的示例图形,该图形由几层文本和图形复合而成。

图 10–2。 内脏的样本闪现图示 5

当放置在舞台中时,该场景将具有类似于图 10–3 所示的显示列表。

图 10–3。 显示样本闪光器官图形列表


5 基于公共领域器官库的图形:米凯尔·海格斯特伦,《内脏》,【http://commons.wikimedia/wiki/File:Internal_organs.png

在渲染阶段,Flash 将使用这个显示列表来决定如何在屏幕上绘制每个像素。由于图形是不透明的,并且嵌套只有三层深,这将在屏幕上非常快地呈现。随着显示列表的复杂性增加,您需要特别注意应用中使用的对象类型,以及应用于它们的效果。

提高应用呈现性能的一些方法包括:

  • 保持你的显示列表很小:一个修剪良好的显示列表将帮助 Flash 渲染器节省内存和扫描层次结构的执行时间。如果不再使用对象,请确保为其父对象移除它们。否则,您可以通过动态更改单个元素的可见性来隐藏和显示它们。
  • 使用合适的对象类型:形状或Bitmap是显示列表中最小的对象,仅占用 236 字节。Sprites更重,具有交互和事件处理功能,占用 414 字节。MovieClips是场景中最昂贵的对象,440 字节和额外的开销来支持动画。为了加快渲染速度,您应该选择满足您需求的最简单的对象类型。
  • 避免 alpha、遮罩、滤镜和混合:如果使用这些功能,Flash 渲染引擎无法进行某些优化,这会降低渲染性能。与其使用 alpha 来隐藏和显示对象,不如使用 visibility 标志。遮罩非常昂贵,通常可以用简单的剪切或场景分层来代替。混合模式特别昂贵,应尽可能避免。

如果您正在开发一个基于 Flex 的应用,那么您将需要特别注意 UIComponents、GraphicElements 和 FXG 的使用。Table 10–2 列出了使用这些不同对象类型的利弊。

UIComponents是 Flex 中最复杂的对象类型,会显著影响渲染性能,尤其是在表格或列表渲染器中广泛使用时。和 FXG 都是非常轻量级的组件,渲染器可以对它们进行显著的优化。FXG 有轻微的性能优势,因为它在应用构建时被编译成图形,而GraphicsElements则需要在运行时处理。

移动开发中的一个常见错误是专门在桌面模拟器中开发,并等到应用几乎完成时才开始在设备上测试。如果等到有了极其复杂的显示列表,就很难找出哪些元素导致了速度的下降。另一方面,如果您在构建应用时定期进行测试,那么就很容易诊断出哪些更改对性能影响最大。

场景位图缓存

另一种可以用来以牺牲内存为代价提高渲染性能的技术是场景位图缓存。Flash 通过cacheAsBitmapcacheAsBitmapMatrix属性提供内置支持,可以轻松捕捉和替换静态图像,以代替完整的场景层次。这在移动设备上尤其重要,因为在移动设备上,矢量图形操作要慢得多,并且会显著影响您的性能。

cacheAsBitmap

cacheAsBitmapDisplayObjectboolean属性,通过扩展,你在 Flash 和 Flex 中使用的所有视觉元素包括SpriteUIComponent都可以访问这个变量。当设置为 true 时,每次DisplayObject或它的一个子节点改变时,它将获取当前状态的快照并保存到屏幕外缓冲区。然后,对于未来的渲染操作,它将重新绘制保存的屏幕外缓冲区,这对于场景的复杂部分可以快几个数量级。

要在DisplayObject上启用cacheAsBitmap,您需要执行以下操作:

cacheAsBitmap = true;

Flex UIComponent有一个缓存策略,它会根据启发自动启用cacheAsBitmap。您可以通过执行以下操作来覆盖此行为并强制启用cacheAsBitmap:

cachePolicy = UIComponentCachePolicy.ON;

当您有不经常改变的复杂图形时,打开cacheAsBitmap是一项重要的技术,例如矢量渲染的背景。尽管背景是静态的,但是当围绕它移动的其他元素重叠并遮挡了部分背景时,也会触发更新。此外,简单的翻译,如滚动背景,将导致一个昂贵的重绘操作。

为了弄清楚应用重绘的每一帧中屏幕的哪些部分被重绘,可以使用下面的代码启用showRedrawRegions:

flash.profiler.showRedrawRegions(true);

这将在正在更新的屏幕区域周围绘制红色矩形,并且可以通过编程打开和关闭。图 10–4 显示了一个CheckBox控件的例子,它可以让你打开和关闭重绘区域。该控件最近被单击过,所以它周围有一个红色的矩形。

图 10–4。 重绘区域调试功能的例子

此选项仅在调试播放器中可用,因此在测试应用时,它将在 AIR 调试启动程序中工作,但在运行时播放器(如移动设备)中部署时,它将不起作用。Figure 10–4 还展示了一个非常简单的每秒帧数监视器,可用于在开发过程中对您的 Flex 应用性能进行基准测试。这两者的完整代码将在下一节构建 Flash Mobile Bench 应用中显示。

虽然cacheAsBitmap是一个非常强大的优化应用重绘的工具,但如果使用不当,它也是一把双刃剑。在cacheAsBitmap设置为 true 的情况下,为每个DisplayObject保留并刷新一个全尺寸的屏幕缓冲区,如果在图形加速模式下运行,这会消耗大量设备内存或耗尽有限的 GPU 内存。

此外,如果你有一个频繁更新的对象或者应用了一个转换,那么cacheAsBitmap只会用不必要的缓冲操作来降低你的应用的速度。幸运的是,对于转换的情况,有一个改进版本的cacheAsBitmap,叫做cacheAsBitmapMatrix,你可以利用它。

cacheasbitmap 矩阵

cacheAsBitmapMatrix也是DisplayObject上的一个属性,和cacheAsBitmap一起工作。为了使cacheAsBitmapMatrix生效,cacheAsBitmap也必须开启。

如前所述,cacheAsBitmap在对对象应用旋转或倾斜等变换时不起作用。这样做的原因是将这样的变换应用到保存的Bitmap会产生缩放伪像,这会降低最终图像的外观。因此,如果您希望将缓存应用于应用了变换的对象,Flash 要求您还为存储在cacheAsBitmapMatrix属性中的Bitmap指定一个变换矩阵。

在大多数情况下,将cacheAsBitmapMatrix设置为识别矩阵将会达到预期效果。屏幕外的Bitmap将被保存在未变换的位置,并且DisplayObject上的任何后续变换将被应用到那个Bitmap。以下代码显示了如何将cacheAsBitmapMatrix设置为识别转换:

cacheAsBitmap = true; cacheAsBitmapMatrix = new Matrix();

如果您利用一个cachePolicy在一个 Flex UIComponent上做同样的事情,您将做以下事情:

cachePolicy = UIComponentCachePolicy.ON; cacheAsBitmapMatrix = new Matrix();

**注意:**如果你计划在多个对象上设置cacheAsBitmapMatrix,你可以重用同一个矩阵来消除矩阵创建的开销。

这样做的缺点是,最终的图像可能会出现一些轻微的锯齿,尤其是在图像被放大或直线被旋转的情况下。为此,您可以指定一个变换矩阵,在缓冲图像之前放大图像。同样,如果您知道最终的图形将总是以缩小的尺寸呈现,您可以指定一个变换矩阵来缩小缓冲的图像以节省内存使用。

如果你使用cacheAsBitmapMatrix来缩小图像尺寸,你需要注意不要以原始尺寸显示DisplayObject。Figure 10–5 显示了一个例子,如果你设置一个先缩小并旋转图像的缓存矩阵,然后尝试以其原始大小渲染对象,会发生什么。

图 10–5。 演示误用cacheAsBitmapMatrix对图像质量的影响

请注意,由于放大,最终图像有很多锯齿。即使您使用原始图像的一对一转换来显示它,Flash 也会放大缓存的版本,从而产生低保真度的图像。

cacheAsBitmapMatrix的最佳用途是将其设置为比预期的变换稍大,这样您就有足够的像素信息来生成高质量的变换图像。

闪光移动工作台

Flash Mobile Bench 是一个简单的应用,可让您测试不同设置对您部署的移动应用的性能的影响。

它允许您测试的功能包括:

  • 向显示列表添加大量形状
  • 简单 x/y 平移的动画速度
  • 简单顺时针旋转的动画速度
  • cacheAsBitmap对性能的影响
  • cacheAsBitmapMatrix对性能的影响
  • 自动 Flex 缓存启发式算法对性能的影响

它还包括一个简单的 FPS 监控小部件,您可以在自己的应用中重用它。

为了强调运行该应用的设备的能力,我们必须做的第一件事是将帧速率从默认的 24fps 提高到更高的水平。根据对一些设备的测试,我们发现 240fps 是许多平台达到的上限,并选择它作为目标帧速率设置。请记住,这是一个测试理论性能的基准应用,但在大多数情况下,您不会希望将帧速率设置得这么高,因为您可能会处理比硬件所能显示的更多的帧。

为了改变帧速率,Application类中有一个名为frameRate的属性。清单 10–1 展示了如何在 Flex 移动应用中设置这一点。

清单 10–1。 闪光移动板凳ViewNavigatorApplication ( MobileBench.mxml )

<?xml version="1.0" encoding="utf-8"?> <s:ViewNavigatorApplication xmlns:fx="http://ns.adobe/mxml/2009"   xmlns:s="library://ns.adobe/flex/spark"   firstView="views.MobileBenchHomeView"   **frameRate="240"**> </s:ViewNavigatorApplication>

这遵循了用一个叫做MobileBenchHomeViewView构建 Flex 移动应用的ViewNavigatorApplication模式。这个View的布局在 MXML 完成,如清单 10–2 所示。

清单 10–2。 Flash 移动工作台View布局代码(MobileBenchHomeView.mxml )

<?xml version="1.0" encoding="utf-8"?> <s:View xmlns:fx="http://ns.adobe/mxml/2009"     xmlns:s="library://ns.adobe/flex/spark"     title="Flash Mobile Bench" initialize="init()">
  <fx:Script>     <![CDATA[       …     ]]>   </fx:Script>   <s:VGroup top="10" left="10" right="10">     <s:Label id="fps"/>     <s:CheckBox id="redraw" label="show redraw"                 click="{flash.profiler.showRedrawRegions(redraw.selected)}"/>     <s:HGroup verticalAlign="middle" gap="20">       <s:Label text="Cache:"/>       <s:VGroup>         <s:RadioButton label="Off" click="cacheOff()"/>         <s:RadioButton label="Auto" click="cacheAuto()"/>       </s:VGroup>       <s:VGroup>         <s:RadioButton label="Bitmap" click="cacheAsBitmapX()"/>         <s:RadioButton label="Matrix" click="cacheAsBitmapMatrixX()"/>       </s:VGroup>     </s:HGroup>     <s:TileGroup id="tiles" width="100%">       <s:Button label="Generate Rects" click="generateSquares()"/>       <s:Button label="Generate Circles" click="generateCircles()"/>       <s:Button label="Start Moving" click="moving = true"/>       <s:Button label="Stop Moving" click="moving = false"/>       <s:Button label="Start Rotating" click="rotating = true"/>       <s:Button id="stop" label="Stop Rotating" click="rotating=false"/>     </s:TileGroup>   </s:VGroup>   <s:Group id="bounds" left="20" top="{stop.y + tiles.y + stop.height + 20}">     <s:Group id="shapeGroup" transformX="{tiles.width/2 - 10}"              transformY="{(height - bounds.y)/2 - 10}"/>   </s:Group> </s:View>

这为应用创建了基本的 UI,包括一个填充 FPS 设置的地方,用于选择缓存策略的单选按钮,以及用于添加GraphicsElement以及开始和停止动画的按钮。

还有一个额外的复选框来显示重绘区域。该控件可以按原样放入您自己的应用中,并可以帮助您最小化重绘区域的大小,以优化渲染性能。请记住,此功能仅在 AIR 调试启动程序中有效,因此您不能在设备运行时使用它。

除了 UI 标签,FPS 监视器的代码是相当独立的。它由一个绑定到ENTER_FRAME事件的事件监听器和一些簿记变量组成,以跟踪平均帧速率。代码如清单 10–3 所示。

清单 10–3。 动作脚本导入,初始化,以及 FPS 处理程序的代码

import flash.profiler.showRedrawRegions; import flash.utils.getTimer; import mx.core.UIComponentCachePolicy; import mx.graphics.SolidColor; import mx.graphics.SolidColorStroke; import sparkponents.Group; import spark.primitives.Ellipse; `import spark.primitives.Rect;
import spark.primitives.supportClasses.FilledElement;

privatefunction init():void {
  addEventListener(Event.ENTER_FRAME, calculateFPS);
  addEventListener(Event.ENTER_FRAME, animateShapes);
}

// FPS handler

privatevar lastTime:int = getTimer();
privatevar frameAvg:Number = 0;
privatevar lastFPSUpdate:int = getTimer();

privatefunction calculateFPS(e:Event):void {
  var currentTime:int = getTimer();
  var duration:int = currentTime - lastTime;
  var weight:Number = (duration + 10) / 1000;
  frameAvg = frameAvg * (1 - weight) + duration * weight;
  lastTime = currentTime;
  if (currentTime - lastFPSUpdate > 200) {
    fps.text = "FPS: " + Math.round(1000.0 / frameAvg).toString();
    lastFPSUpdate = currentTime;
  }
}`

用于计算帧速率的算法针对以下特征进行了调整:

  • 每秒刷新不超过五次:过于频繁地刷新计数器会使其难以读取,并会对您的性能产生负面影响。该条件通过lastFPSUpdate与 200 毫秒阈值的比较来强制执行。
  • 提高慢速帧的权重:随着帧速率的降低,事件的数量也会减少。这要求每一帧的权重更高,以避免读数滞后。权重变量在 1000 毫秒(1 秒)的阈值内完成此操作。
  • 给快速帧赋予最小权重:随着帧速率的上升,权重趋近于零。因此,分配了 1%的最小权重,以防止读数滞后于另一个极端。

该算法中需要注意的另一点是整数和浮点运算的使用。前者更快,在可能的情况下更受欢迎(例如计算持续时间),而后者更准确,并且需要保持精确的平均值(frameAvg)。

代码的下一个关键部分是场景中GraphicsElement的填充。清单 10–4 中的代码实现了这一点。

清单 10–4。 创作GraphicsElement s 的动作脚本代码

`[Bindable]
private var shapes:Vector. = new Vector.();

private function populateRandomShape(shape:FilledElement):void {
  shape.width = shape.height = Math.random() * 20 + 20;
  shape.x = Math.random() * (tiles.width - 20) - shape.width/2;
  shape.y = Math.random() * (height - bounds.y - 20) - shape.width/2;
  shape.fill = new SolidColor(0xFFFFFF * Math.random());
  shape.stroke = new SolidColorStroke(0xFFFFFF * Math.random());
  shapes.push(shape);
  shapeGroup.addElement(shape);
}

private function generateCircles():void {
  for (var i:int=0; i<100; i++) {
    populateRandomShape(new Ellipse());
  }
}

private function generateSquares():void {
  for (var i:int=0; i<100; i++) {
    populateRandomShape(new Rect());
  }
}`

形状的所有属性都是随机的,从填充和描边的颜色到大小和位置。RectEllipse创建之间的重叠逻辑也被抽象成一个公共函数,以最大化代码重用。

为了制作形状的动画,我们使用了在清单 10–5 中找到的代码。

清单 10–5。RectEllipse形状动画的 ActionScript 代码

`privatevar moving:Boolean;
privatevar rotating:Boolean;
privatevar directionCounter:int;

privatefunction animateShapes(e:Event):void {
  if (moving) {
    shapeGroup.x += 1 - ((directionCounter + 200) / 400) % 2;
    shapeGroup.y += 1 - (directionCounter / 200) % 2;
    directionCounter++;
  }
  if (rotating) {
    shapeGroup.rotation += 1;
  }
}`

我们没有使用 Flex 动画类,而是选择通过一个简单的ENTER_FRAME事件监听器来实现。这使您可以灵活地扩展线束,以修改形状类上不是一级属性的变量。

最后,修改cacheAsBitmap设置的代码如清单 10–6 所示。

清单 10–6。 用于设置 renderMode 的应用描述符标签(加粗)

`privatevar identityMatrix:Matrix = new Matrix();

privatefunction cacheOff():void {
  shapeGroup.cachePolicy = UIComponentCachePolicy.OFF;
}

privatefunction cacheAuto():void {
  shapeGroup.cachePolicy = UIComponentCachePolicy.AUTO;
}

privatefunction cacheAsBitmapX():void {
  shapeGroup.cachePolicy = UIComponentCachePolicy.ON;
  shapeGroup.cacheAsBitmapMatrix = null;
}

privatefunction cacheAsBitmapMatrixX():void {
  shapeGroup.cachePolicy = UIComponentCachePolicy.ON;
  shapeGroup.cacheAsBitmapMatrix = identityMatrix;
}`

在阅读了上一节之后,这段代码应该看起来非常熟悉。尽管我们只有一个对象实例来应用cacheAsBitmapMatrix,我们还是遵循了重用公共单位矩阵的最佳实践,以避免额外的内存和垃圾收集开销。

运行 Flash Mobile Bench 后,您将立即看到指定设备上的 FPS 计数器最大值。单击按钮将一些形状添加到场景中,将缓存设置为所需的设置,并查看设备的性能。图 10–6 显示了在摩托罗拉 Droid 2 上运行的 Flash 移动工作台应用,使用cacheAsBitmapMatrix渲染了 300 个圆。

图 10–6。 运行在摩托罗拉 Droid 2 上的 Flash 移动工作台

你的设备性能如何?

GPU 渲染

另一种目前仅适用于移动设备的技术是将渲染卸载到图形处理单元(GPU)。虽然 GPU 是一个高度受限的芯片,不能做普通 CPU 能够做的一切,但它擅长做图形和渲染计算,这些计算在 CPU 上需要几个数量级的时间。同时,GPU 产生更少的电池消耗,允许移动设备循环降低 CPU 以节省电池寿命。

Flash 移动项目的默认设置是 renderMode 为“auto”,目前默认为cpu。您可以显式地将其更改为gpu渲染,看看您的应用是否获得了显著的性能提升。要在 Flash Professional 中更改渲染模式,请打开 AIR for Android 设置对话框,并从渲染模式下拉列表中选择 GPU,如图 Figure 10–7 所示。

**图 10–7。**Flash Professional 中的 GPU 渲染模式设置

要更改 Flash Builder 项目中的 renderMode,您需要编辑应用描述符文件,并在initialWindow下添加一个额外的 renderMode 标签,如清单 10–7 所示。

清单 10–7。 用于设置 renderMode 的应用描述符标签(加粗)

<application>   …   <initialWindow>     **<renderMode>gpu</renderMode>**     …   </initialWindow> </application>

gpu模式获得的结果会因您使用的应用特性和运行的硬件而有很大差异。在某些情况下,你会发现你的应用在gpu模式下比在cpu模式下运行得更慢。Table 10–3 列出了在 Motorola Droid 2 上运行 Flash Mobile Bench 的一些实验结果,在不同的缓存和 gpu 模式下运行 100 个圆和 100 个正方形。

正如您在这个特定设备上的这个场景的结果中所看到的,GPU 没有提供任何优势,并且在没有矩阵集的情况下启用cacheAsBitmap的情况下速度明显较慢。

这强调了在应用中进行设计决策之前,使用不同设备进行测试的重要性。在这个特定的例子中,性能下降很可能是由于 GPU 将数据发送回 CPU 的回写开销。大多数 GPU 设备都针对从 CPU 接收数据进行了优化,以便快速将其写入屏幕。在某些设备上,从另一个方向发回数据进行处理的成本高得惊人。

然而,随着摩托罗拉 ATRIX 和 XOOM 上的英特尔 Integra 特性等新芯片组的推出,这种情况正在迅速改变,这些芯片组为双向通信优化了管道。此外,Flash 团队正在开发一个优化的渲染管道,通过在处理器上做更多的工作来减少对 CPU 的写回需求。有关闪存团队正在进行的性能改进的更多信息,请参见本章后面的“闪存性能的未来”一节。

表演项目渲染器

性能在关键应用领域的环境中得到最佳调整,这将被用户注意到。对于 Flex 移动应用来说,通过列表组织内容是非常常见的,但也带来了巨大的性能挑战。

由于滚动列表涉及动画,如果在交互过程中帧速率下降,这是非常明显的。同时,项目渲染器代码中的任何性能问题都会因渲染器在每个单独的列表单元格中重用而被放大。

为了演示这些概念,我们将构建一个简单的示例,显示所有 Adobe 用户组的列表,并在单击某个项目时导航到用户组网站。

清单 10–8 展示了基本的View代码,用于创建一个 Flex 列表和连接一个将打开浏览器页面的 click 事件处理程序。我们还利用之前开发的FPSComponent来跟踪我们开发应用的速度。

清单 10–8。 Adobe 用户组应用View

<?xml version="1.0" encoding="utf-8"?> <s:View xmlns:fx="http://ns.adobe/mxml/2009"     xmlns:s="library://ns.adobe/flex/spark"     xmlns:renderers="renderers.*" xmlns:profile="profile.*"     title="Adobe User Groups (Original)">   <fx:Script>     <![CDATA[       import flash.navigateToURL;       privatefunction clickHandler(event:MouseEvent):void {         navigateToURL(new URLRequest(event.currentTarget.selectedItem.url));         }     ]]>   </fx:Script>   <s:VGroup width="100%" height="100%">     <profile:FPSDisplay/>     <s:List width="100%" height="100%" dataProvider="{data}"             click="clickHandler(event)">       <s:itemRenderer>         <fx:Component>           <renderers:UserGroupRendererOriginal/>         </fx:Component>       </s:itemRenderer>     </s:List>   </s:VGroup> </s:View>

**提示:**对于移动应用,总是使用itemRenderer属性而不是itemRendererFunction属性。后者会导致创建项目渲染器的多个实例,并会对性能产生负面影响。

这个类引用了一个显示列表项的UserGroupRenderer。该渲染器的创建包括组合以下组件:

  • 用户组徽标的图像组件
  • 用于显示用户组名称和描述的两个文本字段
  • 分隔不同视觉元素的水平线

清单 10–9 展示了满足这些需求的ItemRenderer的简单实现。

清单 10–9。 未优化的ItemRenderer代码

<?xml version="1.0" encoding="utf-8"?> <s:View xmlns:fx="http://ns.adobe/mxml/2009"     xmlns:s="library://ns.adobe/flex/spark"     xmlns:renderers="renderers.*" xmlns:profile="profile.*"     title="Adobe User Groups (Original)">   <fx:Script>     <![CDATA[       import flash.navigateToURL;       privatefunction clickHandler(event:MouseEvent):void {         navigateToURL(new URLRequest(event.currentTarget.selectedItem.url));       }     ]]>   </fx:Script>   <s:VGroup width="100%" height="100%">     <profile:FPSDisplay/>     <s:List width="100%" height="100%" dataProvider="{data}"             click="clickHandler(event)">       <s:itemRenderer>         <fx:Component>           <renderers:UserGroupRendererOriginal/>         </fx:Component>       </s:itemRenderer>     </s:List>   </s:VGroup> </s:View>

在运行这个例子时,我们有一个非常实用的滚动列表,如图 Figure 10–8 所示。

图 10-8。 使用自定义的 Adobe 用户组列表ItemRenderer

虽然功能和外观都很好,但这种实现的性能并不理想。对于正常的滚动,帧速率下降到大约 18fps,当通过在屏幕上滑动来长时间滚动列表时,你只能获得 7fps。在这样的速度下,滚动在视觉上会分散注意力,给人一种整个应用都很慢的印象。

灵活形象类

Flash 提供了几个不同的映像类,这些映像类提供不同的功能,并且具有非常不同的性能特征。根据您的应用需求使用正确的图像类可以带来巨大的性能差异。

可用的图像类别按性能升序排列如下:

  • mx.controls.Image:这是原始的 Flex 图像组件。它现在已经过时,不应该用于移动应用。
  • 这取代了以前的 image 类,应该在任何需要样式、进度指示器或其他高级功能的地方使用。
  • flash.display.Bitmap:这是核心的 Flash 镜像组件。它的功能有限,是在屏幕上显示图像的最高性能方式。

对于最初版本的ItemRenderer,我们使用了 Flex Image类。虽然这是一个不错的选择,但是我们也没有使用这个类的高级特性,所以我们可以通过使用Bitmap来提高性能。

此外,Flex 4.5 中添加的一个新特性是ContentCache类。当在一个Bitmap上设置为 contentLoader 时,它缓存远程获取的图像,在同一图像多次显示的情况下显著提高滚动性能。

清单 10–10 显示了项目渲染器类的更新版本,该类包含了这些改进以提高性能。

清单 10–10。 ItemRenderer【代码对图像进行了优化(粗体变化)

<?xml version="1.0" encoding="utf-8"?> <s:ItemRenderer xmlns:fx="http://ns.adobe/mxml/2009"         xmlns:s="library://ns.adobe/flex/spark">   <fx:Style>     .descriptionStyle {       fontSize: 15;       color: #606060;     }   </fx:Style>   **<fx:Script>**     **<![CDATA[**       **import spark.core.ContentCache;**       **static private const cache:ContentCache = new ContentCache();**     **]]>**   **</fx:Script>**   <s:Line left="0" right="0" bottom="0">     <s:stroke><s:SolidColorStroke color="gray"/></s:stroke>   </s:Line>   <s:HGroup left="15" right="15" top="12" bottom="12" gap="10" verticalAlign="middle">     **<s:BitmapImage source="{data.logo}" contentLoader="{cache}"/>**     <s:VGroup width="100%" gap="5">       <s:RichText width="100%" text="{data.groupName}"/>       <s:RichText width="100%" text="{data.description}" styleName="descriptionStyle"/>     </s:VGroup>   </s:HGroup> </s:ItemRenderer>

通过这些额外的改进,我们将滚动的帧速率提高到了 19fps,投掷的帧速率提高到了 12fps。后者仅用几行代码就提高了 70%以上,而且没有损失任何功能。

文本组件性能

您会注意到桌面和移动设备之间最显著的性能差异之一是文本的性能。当您能够使用映射到设备字体的文本组件和样式时,您将获得最佳性能。但是,使用自定义字体或组件来提供精确的文本控制和反走样会有很大的性能损失。

随着 Flash Player 10 的发布,Adobe 推出了一个新的低级文本引擎,称为 Flash 文本引擎(FTE)和一个建立在它之上的框架,称为文本布局框架(TLF)。与以前的文本引擎(通常称为经典文本)相比,TLF 具有显著的优势,例如支持双向文本和印刷质量的排版。然而,这对移动应用来说是一个巨大的性能损失。

Flash Player 获得高性能文本显示的最佳设置是将文本引擎设置为“经典文本”,并通过在文本属性窗格中选择“使用设备字体”来关闭抗锯齿,如 Figure 10–9 所示。

图 10–9。 Flash 专业优化手机文本设置

对于 Flex 应用,您有大量不同的文本组件,它们利用了从经典文本到 TLF 的所有内容,因此具有不同的性能特征。

表 10–4 中显示了可用的文本组件,以及它们所基于的文本框架和移动性能特征。

对于移动应用,使用LabelTextInputTextArea组件可以获得最佳性能,应该尽可能使用它们。由于它们不支持双向文本和其他高级特性和样式,在某些情况下,您可能仍然需要使用RichEditableTextRichText

由于用户组列表应用不需要任何高级文本特性,我们可以用Label代替RichText。更新后的代码如清单 10–11 所示。

清单 10–11。 ItemRenderer【代码对文本进行了优化(更改以粗体显示)

<?xml version="1.0" encoding="utf-8"?> <s:ItemRenderer xmlns:fx="http://ns.adobe/mxml/2009"         xmlns:s="library://ns.adobe/flex/spark">   <fx:Style>     .descriptionStyle {       fontSize: 15;       color: #606060;     }   </fx:Style>   <fx:Script>     <![CDATA[       import spark.core.ContentCache;       staticprivateconst cache:ContentCache = new ContentCache();     ]]>   </fx:Script>   <s:Line left="0" right="0" bottom="0">     <s:stroke<<s:SolidColorStroke color="gray"/></s:stroke>   </s:Line>   <s:HGroup left="15" right="15" top="12" bottom="12" gap="10" verticalAlign="middle">     <s:BitmapImage source="{data.logo}" contentLoader="{cache}"/>     <s:VGroup width="100%" gap="5">       **<s:Label width="100%" text="{data.groupName}"/>**       **<s:Label width="100%" text="{data.description}" styleName="descriptionStyle"/>**     </s:VGroup>   </s:HGroup> </s:ItemRenderer>

这次改动后,滚动速度为 20fps,投掷速度为 18fps,有了显著的提升。我们可以通过使用StyleableTextField来实现更高的速度,这正是 Flash 团队为他们的内置组件所做的。

内置项目渲染器

在过去的几节中,我们在测试设备上将自定义项目渲染器的性能从低于 10fps 的完全不可接受的速度提升到大约 20fps。我们可以通过进行以下一些额外的更改来继续优化渲染器:

  • 使用cacheAsBitmap保存最近的细胞图像。
  • 在 ActionScript 中重写以利用StyleableTextField
  • 移除组并使用绝对布局。

但是,已经有一个组件包含了这些优化,并且可以开箱即用。

Flex 团队提供了一个LabelItemRendererIconItemRenderer的默认实现,您可以使用和扩展。这些类中已经包含了很多您可以利用的功能,包括对样式、图标和装饰器的支持。它们也是高度优化的,利用了本章讨论的所有最佳实践。

清单 10–12 展示了您将使用内置IconItemRenderer替换我们的自定义项目渲染器的代码更改。

清单 10–12。 View代码利用内置的IconItemRenderer

<?xml version="1.0" encoding="utf-8"?> <s:View xmlns:fx="http://ns.adobe/mxml/2009"     xmlns:s="library://ns.adobe/flex/spark"     xmlns:views="views.*"     title="Adobe User Groups (Built-in)" xmlns:profile="profile.*">   <fx:Script>     <![CDATA[       import flash.navigateToURL;       privatefunction clickHandler(event:MouseEvent):void {         navigateToURL(new URLRequest(event.currentTarget.selectedItem.url));       }     ]]>   </fx:Script>   **<fx:Style>**     **.descriptionStyle {**       **fontSize: 15;**       **color: #606060;**     **}**   **</fx:Style>**   <s:VGroup width="100%" height="100%">     <profile:FPSDisplay/>     <s:List width="100%" height="100%" dataProvider="{data}"               click="clickHandler(event)">       <s:itemRenderer>         <fx:Component>           **<s:IconItemRenderer labelField="groupName"**                     **fontSize="25"**                     **messageField="description"**                     **messageStyleName="descriptionStyle"**                     **iconField="logo"/>**         </fx:Component>       </s:itemRenderer>     </s:List>   </s:VGroup> </s:View>

运行这段代码的结果非常接近我们最初的项目渲染器,如图图 10–10 所示。如果您并排比较这些图像,您会注意到由于使用了StyleableTextComponent,文本中有细微的差异,但是没有显著的差异会影响应用的可用性。

图 10-10。 Adobe 用户组列表使用内置的IconItemRenderer

在摩托罗拉 Droid 2 上,使用内置组件的最终性能是滚动 24fps,投掷 27fps。这超过了 Flex 应用的默认帧速率,表明您可以用很少的代码在 Flash 中构建功能丰富、性能卓越的应用。

性能监控 API 和工具

构建高性能移动应用的最佳秘诀是尽早并经常测试性能。通过在构建应用时识别性能问题,您将能够快速识别代码中对性能至关重要的部分,并在开发过程中对它们进行调优。

拥有正确的工具来获得绩效反馈会使这项工作变得更加容易。本节重点介绍了几个免费提供的工具,或者您的系统中可能已经有了这些工具,您可以从今天开始利用它们。

高分辨率!统计

获得关于应用的帧速率、内存使用和整体性能的实时反馈对于确保在开发过程中不会出现性能倒退至关重要。虽然您可以滚动您自己的性能度量,但是如果您不小心的话,您可能会因为使用您自己的工具降低应用的速度而扭曲您的结果。

幸运的是,一个臭名昭著的网络黑客,化名为 Doob 先生,创建了一个开源统计小部件,您可以很容易地将它合并到您的项目中。您可以从以下网址下载源代码:[github/mrdoob/Hi-ReS-Stats](https://github/mrdoob/Hi-ReS-Stats)

杜布先生的高分辨率!Stats 为您提供了以下工具:

  • 每秒帧数:显示当前 FPS 加上播放器中设置的目标 FPS(越高越好)。
  • 帧持续时间:每秒帧数的倒数,这让你知道渲染一帧需要多少毫秒(越低越好)。
  • 内存使用量:应用当前使用的内存量(以兆字节为单位)
  • 内存使用峰值:该应用达到的最高内存使用阈值(也以兆字节为单位)

添加高分辨率!Stats 添加到 ActionScript 项目中,可以使用以下代码:

import net.hires.debug.Stats; addChild(newStats());

因为它是一个纯 ActionScript 组件,所以您需要做更多的工作来将其添加到 Flex 项目中,具体操作如下:

import mx.core.IVisualElementContainer; import mx.core.UIComponent; import net.hires.debug.Stats; private function addStats(parent:IVisualElementContainer):void {   var comp:UIComponent = new UIComponent();   parent.addElement(comp);   comp.addChild(new Stats()); }

然后,要将它附加到一个View,只需用一个自引用从initialize方法调用它:

<s:View … initialize="addStats(this)"> … </View>

在统计数据下面,绘制了这些值的图表,让您了解应用的趋势。您还可以通过单击读数的顶部或底部来增加或减少应用帧速率。图 10–11 显示了高分辨率的放大版本!统计用户界面。

图 10–11。 高清放大截图!统计数据

效能测试 v2 Beta

一旦您确定了您的性能问题,追踪根本原因并确保一旦您修复了它,行为不会随着将来的变化而倒退是非常棘手的。

Grant Skinner 采用了一种科学的方法来解决 PerformanceTest 的问题,为您提供纯 ActionScript APIs 来计时方法、分析内存使用情况,并创建可重复的性能测试场景。运行 PerformanceTest 工具的示例输出如图图 10–12 所示。

图 10–12。 运行性能测试工具的输出

由于输出是 XML 格式的,您可以轻松地将其与其他工具或报告集成,包括在编写代码时进行性能测试的 TDD 框架。有关 PerformanceTest v2 的更多信息,请参见以下 URL:

http://gskinner/blog/archives/2010/02/performancetest.html.

Flash Builder 分析器

对于堆和内存分析,最好的可用工具之一是内置于 Flash Professional 中的探查器。Flash Builder profiler 为您提供了内存使用情况的实时图表,允许您拍摄堆快照并将其与基准进行比较,还可以捕获应用的方法级性能计时。

虽然这在直接运行在移动设备上时目前不工作,但它可以用于在 AIR Debug Launcher 中运行时分析您的移动应用。要在 profiler 中启动应用,请从运行菜单中选择 Profile。执行时,您将看到应用的实时视图,如图 Figure 10–13 所示。

图 10–13。 在调试模式下针对 Flash 移动项目运行的 Flash Builder 探查器

闪存性能的未来

Adobe 的 Flash 运行时团队一直在寻找新的方法来提高 Flash 应用在桌面和移动设备上的性能。这包括对您的应用透明的 Flash 和 AIR 运行时的性能增强,以及可让您在应用内更高效地工作的新 API 和功能。

**注意:**本节中的所有改进和更改都是针对闪存路线图提出的,但不是承诺的功能。最终的实现可能与所讨论的有很大的不同。

更快的垃圾收集

随着应用规模的增长,垃圾收集暂停会对应用的响应能力产生越来越大的影响。虽然垃圾收集的摊余成本非常低,但由于它提供了所有的好处,所以由全内存清理引起的偶然命中可能会对应用的感知性能造成毁灭性的影响。

从 Flash Player 8 开始,Flash 运行时就使用了标记和清除垃圾收集器。标记和清除垃圾收集器的工作方式是在从根对象遍历所有活动引用之前暂停应用,标记活动对象,如图 Figure 10–14 所示。在该阶段未被标记的对象在算法的扫描阶段被标记为删除。最后一步是释放已释放的内存,这并不保证会立即发生。

图 10–14。 标记和清扫垃圾收集算法的可视化表示

标记和清除算法的好处是很少涉及簿记,而且执行起来相当快。然而,随着堆大小的增长,垃圾收集暂停的持续时间也会增长。这可能会对动画或其他对时间要求严格的操作造成严重破坏,这些操作在收集过程中似乎会挂起。

Flash runtime 团队正在考虑对垃圾收集算法进行多项改进,以提高性能:

  • 增量 GC
  • GC 提示 API
  • 分代垃圾收集

增量垃圾收集将允许垃圾收集器将标记和清扫工作拆分到几个帧上。在这种情况下,垃圾收集的总成本会稍微高一些;然而,对任何特定帧持续时间的影响被最小化,允许应用在收集期间维持高帧速率。

垃圾收集器对于何时触发收集是相当天真的,并且总是会选择最糟糕的时间进行标记和清扫。GC 提示 API 可以让开发人员向垃圾收集器提示不需要垃圾收集的性能关键时刻。如果内存足够低,垃圾收集可能仍然会被触发,但这将有助于防止虚假的垃圾收集在错误的时刻降低应用的速度。

虽然还不太为人所知,但反过来已经是可能的了。Flash 已经有了手动触发垃圾收集的机制。要立即触发垃圾收集循环,需要调用两次System.gc()方法,一次强制标记,第二次强制清扫,如清单 10–13 所示。

清单 10–13。 代码强制垃圾回收(有意重复调用)

flash.system.System.gc(); flash.system.System.gc();

**提示:**以前这个 API 只能从 AIR 获得,并且只能在调试模式下运行,但是现在它完全支持所有模式。

虽然标记和清扫收集器相当有效且易于实现,但它们不太适合交互式应用,并且倾向于破坏新创建的对象。实际上,长寿命对象很少需要收集,而新创建的对象经常被丢弃。分代垃圾收集器认识到了这种趋势,并根据对象的年龄将它们分成不同的代。这使得更频繁地在年轻一代上触发收集成为可能,允许以更少的工作量回收更大量的内存。

拥有一个高效的分代式垃圾收集器将极大地改变 ActionScript 的使用模式,不再需要过多的对象池和缓存策略,而这些策略目前通常用于提高性能。

更快的 ActionScript 性能

您编写的 Flash 应用甚至平台本身中的库都是使用 ActionScript 编写的,因此 ActionScript 性能的增量改进可以对实际性能产生巨大影响。

闪存团队正在研究的一些将惠及所有应用的改进包括:

  • 实时(JIT)编译器优化
  • Float数字型

Flash 利用所谓的实时(JIT)编译器来动态优化 Flash 字节码。JIT 编译器将性能关键的代码段翻译成可以直接在设备上运行的机器代码,以获得更高的性能。同时,它拥有关于代码执行路径的信息,可以利用这些信息来执行优化,从而加速应用。

计划中的一些新的 JIT 优化包括:

  • 基于类型的优化 : ActionScript 是一种动态语言,因此类型信息是可选的。在显式指定类型或者可以通过检查调用链隐式发现类型的地方,可以生成更高效的机器码。
  • 数值优化:目前在 Flash 运行时中,所有的数值运算,包括像加法和乘法这样的重载运算符,都是针对数值对象而不是原始数字和整数的。因此,生成的代码包含额外的指令来检查数字的类型,并从对象中取出值,这在紧循环中是非常昂贵的。通过检查代码来确定原始值可以被替换的位置,这些操作的性能可以得到显著提高。
  • 可空性 : ActionScript 是一种空安全的语言,这对于 UI 编程来说非常方便,但是这意味着产生了许多额外的检查来缩短调用,否则这些调用会取消对空指针的引用。对于在创建时初始化并且从不设置为 null 的变量也是如此。在这些情况下,JIT 有足够的信息来安全地跳过空检查,减少生成代码中的分支数量。

这些 JIT 优化的最终结果是,在不改变应用代码的情况下,您将受益于更快的性能。一般来说,您的应用受 CPU 限制越多,您获得的好处就越大。

此外,Flash 团队还提议增加一个显式的float数字类型和匹配的Vector.<float>。根据定义,Flash 中的数字类型是 64 位精度值,改变它的语义会破坏与现有应用的向后兼容性。然而,许多移动设备已经对硬件进行了优化,可以对 32 位值进行浮点运算。通过让程序员选择指定数值的精度,他们可以决定在有意义的地方牺牲精度来换取性能。

并发

现代计算机拥有多个处理器和内核,可用于并行执行操作以提高效率。这一趋势还扩展到了移动应用,摩托罗拉 ATRIX 等现代设备能够将双核处理器封装在一个非常小的封装中。这意味着为了充分利用硬件,您的应用需要能够在多个线程上并行执行代码。

即使在多个处理器不可用的情况下,考虑在多个线程上并行执行的代码仍然是一个有用的抽象。这使您可以增量处理长期运行的任务,而不会影响需要频繁更新的操作,如渲染管道。

许多内置闪存操作已经在幕后多线程化,可以有效利用多个内核。这包括在后台执行 I/O 操作的网络代码,以及利用在不同线程中运行的本机代码的 Stage Video。通过使用这些 API,您可以隐式地利用并行性。

为了让您能够利用显式线程,Flash 团队正在考虑两种不同的机制向开发人员公开这一点:

  • SWF 委托:代码被编译成两个不同的独立的 SWF 文件。要生成一个新线程,您可以使用主 SWF 文件中的 worker API 来创建子 SWF 的新实例。
  • 入口点类:多线程代码被分成一个不同的类,使用代码注释来指定它是一个唯一的应用入口点。

在这两种场景中,都使用了无共享并发模型。这意味着您不能访问变量或在不同线程中执行的代码之间更改状态,除非使用显式消息传递。无共享模型的优点是它可以防止竞争情况、死锁和其他难以诊断的线程问题。

通过在平台中内置显式并发机制,您的应用将受益于多核处理器的更高效使用,并可以在执行 CPU 密集型操作时避免动画和渲染暂停。

线程渲染流水线

如今,Flash 渲染管道是单线程的,这意味着它不能在较新的移动设备上利用多核,如摩托罗拉 ATRIX。这在渲染图形和视频时尤其成问题,因为它们最终会被顺序处理,如图 Figure 10–15 所示。

图 10–15。 单线程渲染流水线

当 ActionScript 代码的执行时间比预期长时,这可能会导致视频帧被丢弃。Flash 将通过跳过舞台渲染并优先处理后续帧上的视频来进行补偿。结果是,当其中一个处理器处于空闲状态时,您的视频和动画性能都会显著下降。

线程渲染管道将视频处理卸载到第二个 CPU,从而使视频能够流畅运行,而不管 ActionScript 执行或舞台渲染中的延迟。这使得多核系统上的可用资源得到了最佳利用,如图 Figure 10–16 所示。

图 10–16。 多线程渲染流水线

我们可以更进一步,利用 Stage Video 将视频解码和合成卸载到图形处理器,这为您提供了优化的渲染管道,如图图 10–17 所示。

图 10–17。 多线程渲染流水线配合舞台视频

最终结果是,您能够在 ActionScript 代码中进行更多处理,而不会影响帧速率或视频回放。

3d 阶段

闪存路线图中另一个备受关注的项目是 Stage3D。这项技术的代号是 Molehill,对于需要一个非常接近底层图形硬件的跨平台 3D 库的游戏开发者来说,这是特别感兴趣的。Stage3D 使一些应用成为可能,如图 10–18 所示。

图 10–18。 来自 Away3D 的 Molehill 演示(右上和右下)和 Adobe Max(左下)

这些示例是使用名为 Away3D 的第三方 3D 工具包在 Stage3D 的预发布版本之上构建的。其他一些可以利用 Stage3D 的工具包包括 Alternative3D、Flare3D、Sophie3D、Unity、Yogurt3D 和 M2D。

除了对游戏开发者有用之外,Stage3D 还开启了拥有高度优化的 2D UI 工具包的可能性。正如前面讨论的 GPU 加速支持,图形处理器可以比 CPU 更快地完成许多操作,同时消耗更少的功率并延长电池寿命。通过将 UI 工具包完全卸载到图形处理器,CPU 可以专用于应用和业务逻辑,而通过现有的 3D 场景图将显示列表管理、合成和渲染留给 GPU。

总结

正如您在本章中了解到的,通过遵循一些移动调优最佳实践,可以构建具有高级图形、高帧速率和流畅动画的高性能 Flex 应用。您已经获得性能调优知识的一些特定领域包括:

  • 加速图形渲染
  • 将场景图的部分缓存为Bitmap s
  • 构建高性能项目渲染器
  • 文本和项目组件的最佳使用

此外,您还了解了 Flash 运行时和图形处理能力的未来改进,您将能够在未来利用这些改进,而无需更改代码。

所有这些性能调整技术也适用于我们的最后一个主题,即将您的 Flash 和 Flex 应用扩展到平板电脑、电视等领域。

十一、超越手机:平板电脑和电视

谷歌和 Adobe 正在努力分别扩展 Android 平台和 AIR 运行时的覆盖范围。Android 已经扩展到摩托罗拉 XOOM 和三星 Galaxy Tab 等平板电脑上,甚至通过谷歌电视进入你的客厅。这为您的 for Android 应用打开了更多潜在的平台!此外,以黑莓手机闻名的 Research In Motion 也发布了自己的平板电脑 PlayBook。该行动手册完全兼容 Flash,因此为您的 Flex 和 Flash 应用赢得新受众提供了又一个机会。

本章将探讨将移动应用转移到平板电脑和电视的大屏幕上时需要考虑的一些特殊因素。

缩放屏幕

屏幕越大,界面设计就越自由。更多的自由带来更多的责任。平板电脑用户希望你的应用能充分利用大屏幕提供的额外空间。图 11–1 显示了来自第八章的 MusicPlayer 应用在一台 10.1 英寸屏幕的摩托罗拉 XOOM 上运行。虽然该应用是可用的,但低像素密度和大屏幕的结合导致了小而长的控件和大量浪费的空间。我们能够并且将会做得更好。

这样做的动机来自这样一个事实,即自 Android 3.0 推出以来,Android 平板电脑领域正在爆炸式增长,Android 3.0 是专为平板电脑和电视的大屏幕而设计的 Android 版本。除了现有的 Android 2.2 平板电脑——戴尔 Streak 和三星 Galaxy Tab——现在还有摩托罗拉 XOOM 和三星 Galaxy Tab 10.1,它们都运行最新版本的 Honeycomb(Android 3 . x 的代号)。此外,东芝、索尼、华硕和亚马逊都有望在 2011 年发布蜂巢平板电脑。

显然,这是一个任何应用开发人员都想认真对待的细分市场。专门为支持这些更大的平板电脑屏幕而修改的应用将比那些不支持的应用有相当大的优势。

图 11–1。 运行在摩托罗拉 XOOM 平板电脑上的音乐播放器应用

第一步是让你熟悉硬件。大多数平板电脑拥有比普通智能手机更强大的处理器和更大的内存。Table 11–1 显示了目前市场上流行的 Android 平板电脑的显示屏对比。该表显示,大多数平板电脑的分辨率在 160 dpi 左右,屏幕更大、分辨率更高。随着更强大的处理器和大屏幕的结合,您可能会认为您的应用会比在手机上运行得更快。这不是一个好的假设,尤其是如果你的应用是图形受限的而不是 CPU 受限的。除非他们利用硬件加速,否则图形密集型应用在平板电脑上的运行速度通常会更慢,因为更大的屏幕必须进行大量的像素计算。像往常一样,运行性能测试并根据需要进行优化。

每当您考虑将您的应用迁移到一个新的平台时,您都应该花时间研究现有的应用,以确定正在使用的设计模式和约定。图 11–2 显示了一些现有的 Android 平板电脑应用。从左上方按顺时针方向,我们看到:Locomo Labs 的 Flixster、Newsr 和 TweetComb、??、?? 和谷歌的电影工作室。你看到了哪些常见的模式和惯例?

请注意,尤其是在横向模式下(如图所示),应用都利用额外的屏幕空间来显示多个视图。与类似的手机应用不同,Flixster 和 Newsr 在一个屏幕上同时显示主视图和详细视图,而不必转换到单独的详细视图。TweetComb 利用额外的空间来显示多列推文,而 Movie Studio 为您提供了更大、更易于使用的控件。还要注意标题栏中包含了更多的动作(Flex 应用中的ActionBar)。我们可以对我们的 MusicPlayer 应用进行类似的修改,从而将其转换为一个成熟的平板电脑界面,类似于图 11–2 中的图片。

当考虑对音乐播放器的平板版本进行修改时,立即想到的一件事是使用歌曲视图中的额外空间来显示额外的元数据,这在应用的手机版本中根本没有空间。这种简单的修改是第一种技术的理想选择,我们将研究如何将应用扩展到新的屏幕:基于状态的定制。


从技术上来说,HTC Flyer 运行的是 Android 2.3(代号 Gingerbread)而不是 Android 3.x,但你的 AIR for Android 程序也将运行在 Gingerbread 上。

2http://locomolabs/2

图 11–2。 运行在安卓平板电脑上的热门应用

基于状态的定制

我们已经展示了如何使用landscapeportraitView状态定制应用的 UI 布局。这种技术采用了这种思想并加以扩展。不仅仅是portraitlandscape,你需要定义四种状态组合来支持手机和平板电脑的每个方向。因此,您假设的 MXML 代码看起来类似于清单 11–1。

清单 11–1。 首次尝试为手机和平板电脑添加独立状态

`<s:states>
  <s:State name=“portraitPhone”/>
  <s:State name=“landscapePhone”/>
  <s:State name=“portraitTablet”/>
  <s:State name=“landscapeTablet”/>
</s:states>

<s:Group width=“100%” height=“100%”>
  <s:layout.landscapePhone>
    <s:HorizontalLayout verticalAlign=“middle” paddingLeft=“10”/>
  </s:layout.landscapePhone>

<s:layout.landscapeTablet>
    <s:HorizontalLayout verticalAlign=“middle” paddingLeft=“10”/>
  </s:layout.landscapeTablet>

<s:layout.portraitPhone>     <s:VerticalLayout horizontalAlign=“center” paddingTop=“10”/>
  </s:layout.portraitPhone>

<s:layout.portraitTablet>
  <s:VerticalLayout horizontalAlign=“center” paddingTop=“10”/>
</s:layout.portraitTablet>

<s:Group width.portraitPhone=“{height0.4}" height.portraitPhone="{height0.4}”
               width.landscapePhone=“{width0.4}"
               height.landscapePhone="{width
0.4}”
               width.portraitTablet=“{height0.3}"
               height.portraitTablet="{height
0.3}”
               width.landscapeTablet=“{width0.3}"
               height.landscapeTablet="{width
0.3}”>
  
</s:Group>`

我们的View中现在有四种状态:手机和平板电脑的横向和纵向版本。这些都在<s:states>部分使用<s:State>元素进行了枚举。一旦定义了状态,就可以使用 Flex 的特定于状态的属性声明,比如width.portraitPhone,来定制布局、间距,甚至是View用户界面中任何组件的可见性。作为一个例子,在我们假设的代码清单中定义的Group包括一个为我们每个可能的状态定制的widthheight

如您所见,这种技术的主要缺点是特定于状态的属性声明激增。你现在什么都需要四个!幸运的是,有一种方法可以缓解这个问题。

使用状态组

状态组是一种将多个状态(一组状态)分配给一个状态声明的方法。以下面的州声明为例:

<s:State name="portraitPhone" stateGroups="portrait,phone"/>

这意味着当我们将ViewcurrentState设置为portraitPhone时,我们将激活任何被portraitPhoneportraitphone状态修改的属性声明。这允许我们使用这些状态的组合来定义 MXML 属性:

  • attributeName.portraitPhone:这只适用于纵向手机。
  • 这将适用于纵向的手机或平板电脑。
  • 这将适用于横向或纵向手机。

这使您在声明属性时更加灵活,并消除了大量代码重复。既然我们不再定义标准的横向和纵向状态,Flex 将不再自动设置我们的View状态。这是我们将手动处理的事情,通过覆盖getCurrentViewState方法来返回一个基于屏幕大小和当前方向的新状态,如清单 11–2 所示。

清单 11–2。 返回定制View状态

`override public function getCurrentViewState():String {
  var isPortrait:Boolean = height > width;
  var isTablet:Boolean = … // A calculation based on screen size or resolution.

var newState:String = (isPortrait ? “portrait” : “landscape”) +
            (isTablet ? “Tablet” : “Phone”);

return hasState(newState) ? newState : currentState;
}`

新状态由两个布尔变量决定。通过比较View的宽度和高度,很容易确定isPortrait变量。isTablet这个变量稍微复杂一点。您可以通过测试来使用屏幕的分辨率,看看 x 或 y 维度是否大于 960,这是目前手机上使用的最大分辨率。更可靠的方法是使用屏幕分辨率和像素密度来确定屏幕的物理尺寸。那么你可以假设任何超过 5.5 英寸的都是平板设备。这种计算的一个例子显示在清单 11–4 中的onViewActivate函数中。

现在我们可以回到从歌曲的元数据向 UI 添加更多信息的想法。有四样东西可以添加到平板电脑界面上:专辑名称、艺术家姓名、专辑出版年份以及专辑所属的流派。我们已经将albumTitleartistName定义为SongViewModel类中的属性。这意味着我们只需要添加yeargenres属性。清单 11–3 展示了实现这一点的代码。

清单 11–3。?? 向SongViewModel?? 添加yeargenre属性

`package viewmodels
{
  [Bindable]
  public class SongViewModel extends EventDispatcher {
    public var albumCover:BitmapData;
    public var albumTitle:String = “”;
    public var songTitle:String = “”;
    public var artistName:String = “”;    
    public var year:String = “”;
    public var genres:String = “”;

// …

/**
     * Called when the song’s metadata has been loaded by the Metaphile
     * library.
     */
    privatefunction onMetaData(metaData:IMetaData):void {
      var songFile:MusicEntry = songList[currentIndex];
      var id3:ID3Data = ID3Data(metaData);       artistName = id3.performer ? id3.performer.text : “Unknown”;
      albumTitle = id3.albumTitle ? id3.albumTitle.text : "Album by " +
          artistName;
      songTitle = id3.songTitle ? id3.songTitle.text : songFile.name;
      year = id3.year ? id3.year.text : “Unknown”;
      genres = id3.genres ? id3.genres.text : “Unknown”;

if (id3.image) {
        var loader:Loader = new Loader();
        loader.contentLoaderInfo.addEventListener(Event.COMPLETE,
                                                  onLoadComplete)
        loader.loadBytes(id3.image);
      } else {
        albumCover = null;
      }
    }

// …
  }
}`

粗体突出显示的代码显示了需要进行的更改:声明新的可绑定变量来保存yeargenres字符串,然后从 Metaphile 库返回的ID3Data中加载它们。

我们的注意力现在转向如何将这些信息添加到我们的界面上。图 11–3 显示了新界面的两个模型,一个横向,一个纵向。手机界面将保持不变,但当我们检测到我们正在平板电脑上运行时,我们将进行以下更改:

  • ActionBar中的歌曲名称将被替换为专辑名称。
  • 在纵向模式下,四个新的元数据将被放置在专辑封面和播放控件之间。
  • 在横向模式下,新的元数据将放在屏幕的左侧,专辑封面在中间,播放控制在右侧。

根据设备的方向,新歌曲信息会出现在不同的位置,但这可以使用我们的自定义状态名称和组件的includeIn属性轻松实现。

图 11–3。 显示附加信息的设计模型,显示在平板电脑界面上

清单 11–4 中的代码显示了需要对原始View代码进行的第一次修改,以实现如图图 11–3 所示的新设计。

**清单 11–4。**修改后的开始SongView MXML

`<?xml version="1.0" encoding="utf-8"?>
<s:View xmlns:fx=“http://ns.adobe/mxml/2009”
        xmlns:s=“library://ns.adobe/flex/spark”
        xmlns:assets=“assets."
        xmlns:views="views.

        initialize=“onInitialize()”
        viewActivate=“onViewActivate()”
        viewDeactivate=“onViewDeactivate()”
        resize=“onResize()”
        title=“{isTablet ? model.albumTitle : model.songTitle}”>

<s:states>
    <s:State name=“portraitPhone” stateGroups=“portrait,phone”/>
    <s:State name=“landscapePhone” stateGroups=“landscape,phone”/>
    <s:State name=“portraitTablet” stateGroups=“portrait,tablet”/>
    <s:State name=“landscapeTablet” stateGroups=“landscape,tablet”/>
  </s:states>

fx:Script
    <![CDATA[
      import viewmodels.SongViewModel;

[Bindable]
      private var isTablet:Boolean;

[Bindable]
      private var model:SongViewModel;

override public function getCurrentViewState():String {         var isPortrait:Boolean = height > width;
        var newState:String = (isPortrait ? “portrait” : “landscape”) +
            (isTablet ? “Tablet” : “Phone”);

return hasState(newState) ? newState : currentState;
      }

private function onViewActivate():void {
        var w:Number = Capabilities.screenResolutionX/Capabilities.screenDPI;
        var h:Number = Capabilities.screenResolutionY/Capabilities.screenDPI;
        isTablet = Math.max(w, h) > 5.5;

setCurrentState(getCurrentViewState());
      }

privatefunction onResize():void {
        setCurrentState(getCurrentViewState());
      }

private function onInitialize():void { /* same as before / }
      private function onViewDeactivate():void { /
same as before / }
      private function onSongEnded(event:Event):void { /
same as before */ }
    ]]>
  </fx:Script>`

View的 title 属性使用一个到isTablet变量的绑定来决定是在ActionBar中显示歌曲标题还是专辑标题。记住,在较小的手机屏幕上,我们在ActionBar的标题区域显示歌曲标题,以避免SongView界面过度拥挤。如果使用更大的平板电脑屏幕,将专辑名称放在ActionBar中更有意义,并且在从一首歌曲转到下一首歌曲时更改歌曲信息。

如本节前面所述,我们的每个状态都定义了相关的状态组。出现在<fx:Script>部分顶部的被覆盖的getCurrentViewState函数负责根据屏幕大小和方向确定View应该处于哪个状态。如果Viewheight大于其width,则设备被标记为纵向。否则我们知道我们处于风景模式。使用这些信息和isTablet标志,该函数构建并返回一个描述View当前状态的字符串。

ViewviewActivate事件的处理程序中设置了isTablet标志。当View激活时,onViewActivate处理器以英寸为单位计算设备屏幕的宽度和高度。如果其中任何一个尺寸超过 5.5 英寸,那么我们可以假设该应用正在平板设备上运行。然后,该函数调用我们被覆盖的getCurrentViewState方法来获取View的初始状态,并将结果传递给setCurrentState函数。

我们还为Viewresize事件附加了一个处理程序来检测方向变化。onResize处理器将通过调用我们的getCurrentViewState函数来设置View的当前状态,并使用返回值来设置当前的View状态。

**注意:**覆盖getCurrentViewState函数来提供自定义状态确实有一个缺点,那就是它使得 Flash Builder 的设计视图实际上毫无用处。

是时候将这种状态管理代码用于我们的 MXML 宣言了。清单 11–5 显示了根Group容器以及一组标签,它们组成了横向方向的歌曲信息部分。

清单 11–5。View的根容器Group和景观元数据显示

`<s:Group width=“100%” height=“100%”>
  <s:layout.portrait>
    <s:VerticalLayout paddingTop=“10” horizontalAlign=“center”/>
  </s:layout.portrait>

<s:layout.landscape>
    <s:HorizontalLayout verticalAlign=“middle” paddingLeft=“10”/>
  </s:layout.landscape>

<s:VGroup width=“30%” horizontalCenter=“0” gap=“20” paddingTop=“40”
            paddingBottom=“40” includeIn=“landscapeTablet”>
    <s:VGroup width=“100%”>
      <s:Label styleName=“albumInfoLabel” text=“Song”/>
      <s:Label styleName=“albumInfo” text=“{model.songTitle}”
               maxWidth=“{width*.3}” maxDisplayedLines=“1”/>
    </s:VGroup>
    
  </s:VGroup>

<s:Group width.portrait=“{height0.4}" height.portrait="{height0.4}”
           width.landscape=“{width0.4}" height.landscape="{width0.4}”>
    <s:BitmapImage width=“100%” height=“100%” source=“{model.albumCover}”
                   visible=“{model.albumCover != null}”/>
    <assets:DefaultAlbum id=“placeHolder” width=“100%” height=“100%”                          visible=“{!model.albumCover}” />
  </s:Group>`

如同第八章中的一样,我们在纵向模式下使用VerticalLayout作为根Group,在横向模式下使用HorizontalLayout。由于之前声明的状态组,这些布局将用于手机和平板电脑版本的界面。根Group容器的第一个子容器是VGroup,它包含界面风景版本的歌曲信息——回想一下,它在屏幕的最左边。此外,该组应仅出现在平板电脑显示屏上。这就是在其includeIn属性中使用完全指定的landscapeTablet状态的原因。下一个Group是相册封面图片的容器。由于先前的VGroup仅包含在landscapeTablet状态中,相册封面Group将首先出现在任何方向的手机和纵向模式的平板电脑的布局中。

清单 11–6 显示了歌曲信息显示的肖像模式版本以及其余的控件。

清单 11–6。 肖像歌曲信息组和回放控件

`<s:VGroup width=“80%” horizontalCenter=“0” gap=“40” paddingTop=“40”
              paddingBottom=“40” includeIn=“portraitTablet”>
      <s:HGroup width=“100%”>
        <s:VGroup width=“50%”>
          <s:Label styleName=“albumInfoLabel” text=“Song”/>
          <s:Label styleName=“albumInfo” text=“{model.songTitle}”
                   maxWidth=“{width*.4}” maxDisplayedLines=“1”/>
        </s:VGroup>
        <s:VGroup horizontalAlign=“right” width=“50%”>
          <s:Label styleName=“albumInfoLabel” text=“Artist”/>
          <s:Label styleName=“albumInfo” text=“{model.artistName}”                    maxWidth=“{width*.4}” maxDisplayedLines=“1”/>
        </s:VGroup>
      </s:HGroup>
      
    </s:VGroup>

<s:VGroup horizontalAlign=“center” paddingTop=“20” gap=“40”
              width.portrait=“100%” width.landscape=“50%”>
      <s:HGroup width=“90%”>
        <s:Button label=“<<” height=“40” click=“model.previousSong()”/>
        <views:ProgressButton id=“progressButton” width=“100%” height=“40”
                              click=“model.onPlayPause()”
                              percentComplete=“@{model.percentComplete}”                               skinClass=“views.ProgressButtonSkin”/>
        <s:Button label=“>>” height=“40” click=“model.nextSong()”/>
      </s:HGroup>

<s:HGroup verticalAlign=“middle” width=“90%”>
        <assets:VolLow id=“volLow” width=“32” height=“32”/>
        <s:HSlider width=“100%” maximum=“1.0” minimum=“0.0” stepSize=“0.01”
                 snapInterval=“0.01” value=“@{model.volume}” showDataTip=“false”/>
        <assets:VolHigh id=“volHigh” width=“32” height=“32”/>
      </s:HGroup>

<s:HGroup verticalAlign=“middle” width=“90%” >
        <s:Label text=“L” width=“32” height=“32” verticalAlign=“middle”
                 textAlign=“center”/>
        <s:HSlider width=“100%” maximum=“1.0” minimum=“-1.0” stepSize=“0.01”
                 snapInterval=“0.01” value=“@{model.pan}” showDataTip=“false”/>
        <s:Label text=“R” width=“32” height=“32” verticalAlign=“middle”
                 textAlign=“center”/>
      </s:HGroup>
    </s:VGroup>
  </s:Group>
</s:View>`

在纵向模式下,歌曲信息VGroup显示在专辑封面和播放控件之间——因此它在 MXML 文件中的位置是这样的,其includeIn属性指定了portraitTablet州。

作为点睛之笔,我们在歌曲信息组件的ViewNavigatorApplication MXML 文件中添加了一点 CSS 样式。我们现在来看看图 11–4 中的应用。我们的应用现在能够适应运行在最小和最大的移动设备上。这是定制的一个简单例子,通过明智地使用状态可以实现这一点。该应用的代码可以在 MusicPlayerWithStates 项目中找到,该项目位于本书示例代码的examples/chapter-11目录中。

图 11–4。 音乐播放器支持在小屏幕和大屏幕上运行的应用

这种基于状态的定制技术的主要优点是,它允许您将所有应用代码保存在一个项目中。这使得维护代码更容易,并简化了构建过程。然而,当您考虑当您想要开始支持其他平台时需要做什么时,缺点就变得很明显了。如果你想把你的市场扩大到包括 iPhone、iPad 和 PlayBook,那么你需要开始调整用户界面,以适应这些平台上使用的所有不同的惯例。你将突然面临状态的组合爆炸。如果不同设备类别或平台的接口彼此差异太大,您也会遇到问题。在你拥有一份冗长、难读、难维护的 MXML 档案之前,各州只能带你走这么远。

如果你发现自己处于这个位置,你可以转向界面定制的第二个选择:基于项目的定制。

基于项目的定制

基于项目的定制背后的想法是将应用的所有共享代码放入一个库项目,然后创建单独的项目来实现每个不同平台或设备类别的定制用户界面(例如,手机与平板电脑)。为不同类别的设备或不同平台的应用的每个版本创建单独的项目,可以为您提供配置界面的最大灵活性。这种设置对于跨越两个或多个 web、桌面、电话、平板电脑和电视的项目来说非常常见。为了避免不必要的代码重复,创建了一个库项目来包含所有共享的源文件和图形资源。

让我们假设我们的设计师已经看过了图 11–2 中显示的一些应用,并决定为我们的音乐播放器尝试一种新的外观。他们想出了一种新的横向模式下的平板电脑界面,看起来有点像图 11–5。他们希望将歌曲信息移到屏幕的右侧,将播放控件放在专辑封面下,并将歌曲列表添加到屏幕的左侧。从列表中选择一首歌应该跳到那首歌。列表的选择高亮应该总是反映当前正在播放的歌曲。我们还将假装我们已经开始听到营销部门关于扩展以支持其他移动平台的传言。将所有这些放在一起,我们将决定是时候选择完全定制的能力了,通过将我们的代码库分割成单独的项目,其中一个公共库项目将被其余的共享。

图 11–5。 在平板电脑上以风景模式运行的音乐播放器的新界面原型

创建库项目

首先要做的是创建共享库项目。在 Flash Builder 4.5(或更高版本)中,使用应用菜单,点击文件新建 Flex 库项目。Flash Builder 将显示如图图 11–6 所示的对话框。

图 11–6。 在 Flash Builder 4.5 中创建新库项目

您必须为库项目指定一个名称(如 MusicPlayerLib ),正如我们在 Figure 11–6 中所做的那样。因为我们并不关心在这个项目中支持 web 和桌面(还没有!),我们还在配置部分选择了“移动库”选项。

我们知道我们的展示模型将被放入这个项目。我们也知道其中一个依赖于中期库。因此,我们必须将Metaphile.swc文件添加到这个项目中,以便对其进行编译。我们创建了一个libs目录并将Metaphile.swc放在里面。然后,我们通过右键单击项目并选择 Properties,将libs目录添加到构建路径中。将显示项目的属性对话框,它看起来类似于图 11–7 中所示。点按“Flex 库构建路径”,然后点按“添加 SWC 文件夹…”按钮。在出现的对话框的文本字段中键入目录名“libs ”,然后单击 OK。你的对话框现在应该看起来像图 11–7 中的那样,这表明Metaphile.swc文件已经被添加到你的构建路径中。

图 11–7。 Metaphile.swc文件添加到我们的库项目

创建我们的库项目的最后一步是从原始的 MusicPlayer 应用中复制必要的包结构,并将源代码和图形素材复制到正确的位置。Table 11–2 显示了已经添加的包以及每个包中的文件。

请注意,我们已经从原来的 MusicPlayer 项目的views包中取出了自定义的ProgressButton控件,并把它放到了共享库项目的一个新的components包中。库项目现在应该可以编译了,我们已经准备好创建新的项目,我们将使用这些项目来构建将在手机和平板电脑上运行的应用版本。

创建手机和平板电脑项目

我们将通过使用应用菜单并点击文件 新建 ** Flex 移动项目来创建一个新的 Flex 移动项目。**当“新建 Flex Mobile 项目”对话框出现时,将项目命名为 MusicPlayerPhone,单击“下一步”按钮,选择一个基于视图的应用,然后单击“完成”。必须执行以下步骤来填充新项目:

  1. 将原始 MusicPlayer 项目的assets包中的图形资源复制到新项目的assets包中。这包括闪屏、音量图标和默认专辑封面。
  2. 从原 MusicPlayer 项目的views包中复制源代码,并将它们放入新项目的views包中。这将包括SongListView.mxmlSongView.mxml文件。
  3. 修改SongView.mxml中的代码,以考虑到ProgressButton控件的新包。
  4. 将代码从原始项目的默认包中的主ViewNavigatorApplication MXML 文件复制到新项目的主 MXML 文件中。
  5. 通过右键单击项目并选择“属性”,单击“Flex 构建路径”,单击“添加项目…”按钮,然后选择 MusicPlayerLib 项目,将 MusicPlayerLib 项目添加到该项目的构建路径中。

新项目现在应该可以编译和运行了,结果看起来和第八章中的原始音乐播放器一模一样。如果您有任何问题,可以查看本书示例代码的examples/chapter-11目录中的 MusicPlayerPhone 项目中的源代码。通过重复这些步骤来创建一个 MusicPlayerTablet 项目,您就可以开始使用 MusicPlayer 应用的新的自定义平板电脑界面了。

但是在我们开始之前,这是向您介绍 Eclipse 的工作集特性的好时机,如果您还不知道的话。定义一个工作集将允许您将包资源管理器中列出的项目数量限制为您在任何给定时间正在处理的项目。一旦定义了工作集,就可以轻松地在它们之间切换。您可以使用 Package Explorer 选项卡右侧的 View 菜单来访问 Working Sets 特性。视图菜单的图标是倒置的三角形。图 11–8 显示了它的位置。

图 11–8。??【包浏览器】的查看菜单图标

通过单击查看菜单图标并选择“选择工作集…”选项,可以定义新的工作集。将显示“选择工作集”对话框。单击“新建”按钮将显示“新建工作集”对话框。选择“资源”作为工作集类型,然后单击“下一步”。在最后一个对话框中,键入工作集的名称,并选择希望成为工作集一部分的项目。然后单击完成。图 11–9 显示了对话框的顺序。

图 11–9。 创建新的工作集

要选择一个工作集,点击查看菜单并再次选择工作集。您定义的工作集将出现在列表中。选中要激活的工作集旁边的复选框,然后单击“确定”。一旦您选择了一个工作集,它的名称将直接出现在视图菜单上,使您只需点击两次就可以在工作集之间切换。当您的 Package Explorer 视图开始被您正在处理的所有不同项目塞满时,能够快速定义工作集并在它们之间切换是一个巨大的好处。

实现自定义平板电脑界面

在新的SongView界面中,歌曲列表会出现在屏幕的左侧。列表中的当前选择应该反映当前正在播放的歌曲。点击列表中的新条目应该会切换到该歌曲。我们在这里描述的是两个绑定:一个在模型中的歌曲列表和列表中的项目之间,另一个在列表的当前选择和模型中的当前歌曲索引之间。

我们将从需要对模型进行的修改开始。将创建一个新的songListArrayCollection作为 UI 中List的绑定源。我们还需要使模型的currentIndex变量可绑定,以作为ListselectedIndex属性的来源,以及可设置的,以便新的列表选择将导致模型采取行动播放一首新歌。清单 11–7 展示了模型的第一个变化。

清单 11–7。 改为SongViewModel是因为没有代码的六页实在是太长了!

`[Bindable]
public class SongViewModel extends EventDispatcher {
    // Some variables removed for brevity…

public var year:String = “”;
    public var genres:String = “”;
    public var songList:ArrayCollection;

private var _currentIndex:Number = 0;

/** A collection of MusicEntry objects. */
    private var musicEntries:ArrayCollection;

public function SongViewModel(entries:ArrayCollection, index:Number) {
      this.musicEntries = entries;
      this.currentIndex = index;

timer = new Timer(500, 0);
      timer.addEventListener(TimerEvent.TIMER, onTimer);

loadCurrentSong();
      filterEntriesBySongs();
    }

/**
     *** Takes all songs in musicEntries and puts them in songList.**
     */     private function filterEntriesBySongs():void {
      songList = new ArrayCollection();

for (var i:int = 0; i<musicEntries.length; ++i) {
        var entry:MusicEntry = MusicEntry(musicEntries.getItemAt(i));
        if (entry.isSong)
          songList.addItem(entry);
      }
    }`

在清单 11–7 中,我们添加了名为songList的新ArrayCollection,并将currentIndex变量重命名为_currentIndex,以表明它现在将具有与其相关联的publicgetset函数。songList集合在filterEntriesBySong函数中初始化,该函数在类的构造函数的末尾被调用。该函数循环遍历musicEntries集合,并将每首歌曲条目复制到songList集合。

清单 11–8 显示了 model 类中的代码,该类提供对currentIndex属性的访问,并处理对应于currentIndex的歌曲的播放。currentIndexget函数为View提供了对素材价值的访问。set函数存储新值并调用playSongAtCurrentIndex函数。

清单 11–8。??SongViewModel与在当前索引播放歌曲相关的代码

`public function get currentIndex():Number {
      return _currentIndex;
    }

public function set currentIndex(value:Number):void {
      _currentIndex = value;
      playSongAtCurrentIndex();
    }

/**
     * Jump to the beginning of the next song in the list.  Will wrap to
     * the beginning of the song list if needed.
     */
    publicfunction nextSong():void {
      incrementCurrentSongIndex();
      playSongAtCurrentIndex();
    }

/**
     * Moves the play position back to the beginning of the current song
     * unless we are within 3 seconds of the beginning already.  In that
     * case, we jump back to the beginning of the previous song.  Will
     * wrap to the end of the song list if needed.
     */
    publicfunction previousSong():void {
      if (channel && channel.position < 3000) {
        decrementCurrentSongIndex();
        playSongAtCurrentIndex();
      } else {
        percentComplete = 0;       }
    }

/**
     *** Will load and play the song indicated by the currentIndex variable.**
     */
    public function playSongAtCurrentIndex():void {
      loadCurrentSong();

if (isPlaying) {
        pauseSong();
        playSong();
      } else {
        percentComplete = 0;
      }
    }`

playSongAtCurrentIndex功能将歌曲加载到内存中,如果模型处于“播放”模式,则停止当前歌曲并播放这首新歌。如果模型被暂停,那么percentComplete变量将被重置,这样下次调用模型的onPlayPause函数时,播放将从歌曲的开始处继续。我们还回到了模型的previousSongnextSong功能,并将其更改为使用新的playSongAtCurrentIndex功能,以消除不必要的代码重复。边走边打扫!

切换到视图,我们知道当我们在横向模式下将歌曲列表添加到屏幕左侧时,纵向模式 UI 应该保持不变。同时,歌曲信息从界面上一次化身的屏幕左侧迁移到最新设计的屏幕右侧。因为我们不再需要额外的状态,现在这是一个特定于平板电脑的用户界面,MXML 文件的开头现在回到了它的原始形式,除了ActionBar显示专辑名称而不是歌曲名称,就像它在电话界面上一样。所有额外的状态声明以及设置和获取View状态的函数都不见了。

我们需要为List添加声明,作为View的根Group容器的第一个子容器,并确保它只包含在景观状态中。现在,我们还将把专辑封面、纵向模式歌曲信息和播放控件放入一个VGroup中,因为这些部分在纵向和横向状态下总是显示为一个垂直组。最后,一个标签的VGroup将被添加到横向状态,以在屏幕的右侧以那个方向显示歌曲信息。清单 11–9 展示了对SongView MXML 文件的这些更改。

清单 11–9。 修改为SongView MXML 支持新景观界面设计

`<s:Group width=“100%” height=“100%”>
    <s:layout.portrait>
      <s:VerticalLayout paddingTop=“10” horizontalAlign=“center”/>
    </s:layout.portrait>

<s:layout.landscape>
      <s:HorizontalLayout verticalAlign=“top”/>
    </s:layout.landscape>     <s:List id=“songList” styleName=“songList” includeIn=“landscape” width=“30%”
            height=“100%” dataProvider=“{model.songList}” labelField=“name”
            selectedIndex=“{model.currentIndex}”
            change=“model.currentIndex = songList.selectedIndex”/>

<s:VGroup horizontalAlign=“center” width.portrait=“100%”
              width.landscape=“40%” paddingTop=“20 “>
      <s:Group width.portrait=”{height0.4}" height.portrait="{height0.4}”
               width.landscape=“{width0.35}" height.landscape="{width0.35}”>
        <s:BitmapImage width=“100%” height=“100%” source=“{model.albumCover}”
                       visible=“{model.albumCover != null}”/>

<assets:DefaultAlbum id=“placeHolder” width=“100%” height=“100%”
                             visible=“{!model.albumCover}” />
      </s:Group>

<!-- The groups defining the portrait mode song info and controls are unchanged –

</s:VGroup>

<s:VGroup width=“30%” gap=“60” includeIn=“landscape” paddingRight=“10”
              paddingTop=“20”>
      <s:VGroup width=“100%” horizontalAlign=“right”>
        <s:Label styleName=“albumInfoLabel” text=“Song”/>
        <s:Label styleName=“albumInfo” text=“{model.songTitle}”
                 maxWidth="{width.3}" maxDisplayedLines=“2”/>*
      </s:VGroup>
      
    </s:VGroup>`

List使用模型的新songList作为它的dataProvider,并使用它来显示歌曲名称。它的selectedIndex属性被绑定到模型的currentIndex属性,以确保当前播放的歌曲也是列表中高亮显示的那首。每当List的选择改变时,新的selectedIndex用于设置模型的currentIndex属性。这允许用户点击列表中的项目来改变当前正在播放的歌曲。

实现这些更改后,应用现在如图 11–10 所示。该图显示了在摩托罗拉 XOOM 上横向运行的应用,并在屏幕左侧展示了新的歌曲列表。该图右侧的图像显示了在三星 Galaxy Tab 上以纵向模式运行的应用。将平板电脑从纵向旋转到横向,歌曲列表将无缝显示。当然,我们在 MusicPlayerPhone 项目中安全地隐藏了我们最初的手机版本界面,它不受平板电脑版本中这些新功能的影响。当然,对共享库中的SongViewModel的更新将会出现在手机版本中,但是它们在那个应用中没有被使用,因此没有效果。

在某些方面,为每个平台拥有单独的项目简化了构建过程,尤其是当您开始处理多个平台时,因为您可以为每个项目拥有一个应用 XML 描述符文件,而不是在构建时交换它们。

图 11–10。 新的平板电脑界面在摩托罗拉 XOOM 上以横向模式运行,在三星 Galaxy Tab 上以纵向模式运行

过渡到电视

成为 Adobe Flash 生态系统的一员,这是一个激动人心的时刻。除了 web、桌面和 Android 平台,AIR 也正在成为 iOS 设备、BlackBerry 平板电脑,甚至电视机、蓝光播放器和机顶盒的可行编程环境!这是一个真正让你在生活中的所有屏幕上利用现有编程和设计技能的环境——甚至是大屏幕。

在 2011 年 5 月的谷歌 I/O 大会上,谷歌宣布将 Android 3.1,即所谓的蜂巢版本,引入其谷歌电视平台。通过此次更新,谷歌电视用户将可以使用 Android market。通过一些限制,您现有的 Android 应用 AIR 应该可以很容易地移植到 Google TV 平台。此外,所有零售的新谷歌电视设备都将包括 Android 调试器,这意味着你应该能够在客厅的谷歌电视上运行和测试你的应用。

另一条通往客厅的道路是 Adobe 的 AIR for TV 平台。这是电视、机顶盒和蓝光播放器的运行时。它目前处于预发布阶段,在 AIR 2.5 上运行。为电视平台开发时要注意的一件事是,它们通常位于 CPU 马力谱的低端。电视中的 CPU 通常比普通智能手机中的 CPU 要慢得多。这并不一定意味着你用于电视应用的 AIR 会很慢,但确实意味着你要注重性能。第十章中给出的许多建议也适用于电视平台。鉴于电视中常见的较慢的 CPU,你应该特别注意那一章“减少代码执行时间”一节中给出的建议。

Adobe AIR for TV 预计将在三星的智能电视平台上首次亮相,在撰写本文时,预计将于 2011 年推出。

如果你决定为这些电视平台中的一个开发,有一些事情你需要记住。第一,一台电视的输入法不一样。即使电视有触摸屏,也没有人愿意经常起身走到电视前触摸屏幕,以便与他们的应用进行交互。因此,电视可能会使用小触摸板或方向按钮板进行导航和交互。其次,正如谷歌提醒我们的,电视实际上是一种“10 英尺的体验”屏幕更大,所以控件和字体也应该更大。处理电视几乎肯定需要一个新的设计通过你的应用。

移植到行动手册

尽管 Research In Motion 是平板电脑市场的新来者,但黑莓 PlayBook 是一个有趣的入口。PlayBook 外形小巧,只有 7.6 英寸宽和 5.4 英寸高,这使它成为一款极其便携的设备。它配备了 7 英寸触摸屏,1 GHz 双核处理器和 1 GB 内存。它与 QNX 中微子实时操作系统配对。这种基于微内核架构的操作系统因其在关键任务系统中的应用而闻名。

PlayBook 的一个优点是它对开发人员非常友好。它为开发人员提供了不少于四种开发应用的环境:native C/C++、Java、HTML5 和相关技术,当然还有 Adobe AIR。再者,AIR 在这个平台上不是二等公民。AIR 应用可以利用视频和图形的硬件加速。虽然存在常见的 Flash 平台组件,但 AIR 程序员可以使用一些特殊的包,这些包使 ActionScript 程序能够在其用户界面中使用本机高性能 QNX 组件。AIR 应用甚至可以访问该平台的原生通知功能。简而言之,AIR 程序得到了很好的支持,并且很好地集成到了平台中。平板电脑唯一真正的缺点是,由于它是一个全新的平台,其市场渗透率相当低。

那么,作为一名 Flash/Flex/AIR 开发人员,你该如何进入这个新市场呢?一个很好的起点是 BlackBerry table t OS SDK for Adobe Air 开发资源网站。 3 从那里你会找到“入门指南” 4 的链接以及安装开发环境的步骤。您首先需要下载并解压缩 SDK 安装程序。安装程序将在您当前安装的 Flash Builder 4.5 的sdks目录中创建一个新的PlayBook目录。这个目录将包含开发剧本应用所需的一切。行动手册模拟器是一个与 VMware 兼容的虚拟机映像,可以在您的 Windows、Mac 或 Linux 桌面上运行行动手册运行时环境。该映像包含在 PlayBook SDK 文件中,这些文件位于您的 Flash Builder 安装目录中。只需在 VMware 中打开这个虚拟机映像,行动手册环境就会启动。启动时,它会要求输入密码。键入“剧本”,您应该会看到剧本 UI 出现。


3

4

您可以在 Flash Builder 4.5 中为您的剧本应用创建一个新项目,方法是从应用的菜单中选择文件 新建 ** ActionScript 移动项目**。您使用默认的 SDK,并选择 BlackBerry Tablet OS 作为目标平台。

**注意:**在撰写本文时,官方的 BlackBerry 平板电脑操作系统支持预计将于 2011 年夏季在 Flash Builder 更新中发布。这可能会改变您为此平台创建移动应用项目的方式。

您可以在桌面上的模拟器中运行和测试 AIR 应用。您只需使用虚拟机中运行的剧本环境的 IP 地址,在 Flash Builder 中为您的项目创建一个运行配置。您可以通过点击位于剧本屏幕右上角的胸前带着装备的人的图标来获得剧本的 IP 地址。刚刚提到的“入门指南”为所有这些步骤提供了简单易懂的说明,这将使您在一个小时内开始在模拟器上进行开发。

这是足够的序言;清单 11–10 显示了一个简单的黑莓 PlayBook Hello World 程序。

清单 11–10。 黑莓 PlayBook 的 Hello World ActionScript 程序

`import flash.display.Bitmap;
  import flash.display.GradientType;
  import flash.display.Graphics;
  import flash.display.SpreadMethod;
  import flash.display.Sprite;
  import flash.events.MouseEvent;
  import flash.geom.Matrix;
  import flash.text.TextFormat;
  import qnx.ui.buttons.LabelButton;
  import qnx.ui.text.Label;

[SWF(width=“1024”, height=“600”, frameRate=“30”)]
  publicclass PlayBookHelloWorld extends Sprite
  {
    [Embed(source=“splash.png”)]
    privatevar imageClass:Class;

publicfunction PlayBookHelloWorld()
    {
      var bitmap:Bitmap = new imageClass();
      bitmap.x = 10;
      bitmap.y = 10;

var goodByeButton:LabelButton = new LabelButton();
      goodByeButton.label = “Good Bye”;       goodByeButton.x = stage.stageWidth - goodByeButton.width;
      goodByeButton.y = stage.stageHeight - goodByeButton.height;
      goodByeButton.addEventListener(MouseEvent.CLICK, onClick);

var myFormat:TextFormat = new TextFormat();
      myFormat.color = 0xf0f0f0;
      myFormat.size = 48;
      myFormat.italic = true;

var label:Label = new Label();
      label.text = “Hello Pro Android Flash!”;
      label.x = bitmap.width + 20;
      label.y = 10;
      label.width = stage.stageWidth - bitmap.width - 10;
      label.height = 100;
      label.format = myFormat;

addChild(createBackground());
      addChild(bitmap);
      addChild(goodByeButton);
      addChild(label);

stage.nativeWindow.visible = true;
    }

privatefunction onClick(event:MouseEvent):void{
      stage.nativeWindow.close();
    }

privatefunction createBackground():Sprite {
      var type:String = GradientType.LINEAR;
      var colors:Array = [ 0x808080, 0x404040 ];
      var alphas:Array = [ 1, 1 ];
      var ratios:Array = [ 0, 255 ];
      var spread:String = SpreadMethod.PAD;

var matrix:Matrix = new Matrix();
      matrix.createGradientBox( 100, 100, (90 * Math.PI/180), 0, 0 );

var sprite:Sprite = new Sprite();
      var g:Graphics = sprite.graphics;
      g.beginGradientFill( type, colors, alphas, ratios, matrix, spread );
      g.drawRect( 0, 0, 1024, 600 );

return sprite;      
    }
  }`

正如你所看到的,它看起来很像任何其他的 Flash 程序。我们使用了几个基本的 QNX 控件,只是为了展示在你的程序中包含它们的样子。他们有一个熟悉 Flash 编程的人都非常熟悉的 API。图 11–11 显示了剧本环境和 Hello World 程序在模拟器中运行时的样子。

图 11–11。 一个运行在黑莓 PlayBook 模拟器上的简单 Hello World ActionScript 程序

如果您想在实际的 PlayBook 硬件上运行您的应用,您将需要一个“调试令牌”。获得这样的令牌是免费的,但是您需要注册剧本开发计划。如果您希望最终将应用部署到 BlackBerry 应用商店,您还需要申请一个密钥来签署您的应用。

如果你决定将你的 Android 应用移植到 PlayBook,你应该遵循我们之前在从手机移植到平板电脑时使用的建议:了解你的目标平台。比如 PlayBook 没有硬件后退按钮;因此,就像 iPhone 或 iPad 一样,大多数应用屏幕的左上角通常都有一个后退按钮。和往常一样,了解您的目标平台的一个好方法是研究该平台的流行应用。PlayBook 上有一个脸书应用和大量预装的应用供您查看。

调查 iOS

就全球受欢迎程度而言,Android 和 iPhone 设备目前主导着智能手机市场。这使得在将你的应用移植到其他平台时,苹果的 iOS 作为一个潜在的目标很有吸引力。但当你加上苹果的 iPad 是平板电脑市场无可争议的王者这一事实时,这个决定突然变得非常明显。

设置苹果的开发环境有点类似于 PlayBook 所需的过程,尽管苹果没有在实际设备上测试软件的免费选项。你必须加入它的开发者计划 5 (目前每年 99 美元)才能在真正的硬件上运行和测试你的应用。

然而,一旦你获得了会员资格和开发密钥,为 iOS 编写基于 ActionScript 的应用就和为 Android 和 PlayBook 编写应用一样了。

**注意:**与剧本一样,在撰写本文时,预计将在 2011 年夏天更新对 Flash Builder is 开发的支持。

同样,您需要花一些时间来熟悉这个平台上使用的常见设计模式。例如,就像 PlayBook 一样,iOS 设备没有硬件后退按钮。幸运的是,Adobe 平台团队中的优秀人员在这方面让开发人员的生活变得更加轻松。ActionBardefaultButtonAppearance样式属性可以设置为“斜角”以近似原生 iOS ActionBar按钮的外观。此外,ActionBar中的标题倾向于居中,而不是像在 Android 上那样右对齐。可以将ActionBartitleAlign属性设置为“中心”,以在您的 AIR 应用中实现这种效果。参见第三章中的列表 3-8 ,了解在应用中使用这些样式的示例。

您甚至可以在运行时动态应用这些样式,方法是使用@media (os-platform:“ios”) CSS 选择器,或者确保Capabilities.cpuArchitecture返回字符串“ARM”并且Capabilities.os返回包含术语“iPhone”的字符串。


5

总结

本章向您展示了如何使用您的移动 AIR 应用,并使它们适应 Android 平板电脑、电视,甚至苹果和黑莓设备。您已经了解了以下内容:

  • 如何使用状态和状态组为不同的设备定制您的界面,同时保持单一的代码库
  • 如何将您的应用拆分成多个项目,这些项目包括共享库代码以及针对您想要的每个不同平台的完全定制的用户界面
  • 你有什么选择来扩大你在电视屏幕上的影响力,以及这样做时你需要考虑的一些事情
  • 如何使用 Adobe AIR 的 BlackBerry PlayBook 开发环境
  • 将你的 Android 应用移植到苹果 iOS 平台的一些技巧

你成功了!欢迎来到书的结尾!我们希望你能像我们喜欢写它一样喜欢阅读它。如果你已经学会了一两件事,让你作为 Android 开发者的 AIR 生活变得更轻松,那么你的旅程就值得了。这是科技界激动人心的时刻。这个由智能手机、平板电脑、智能电视和超快的无线带宽组成的现代世界为全世界的软件开发人员带来了新的机遇和新的挑战。祝你好运,祝你编程成功!

本文标签: 高级教程flash