Quick Guide For Firefox OS App Development
Quick Guide For Firefox OS App Development
Mozilla中国社区 and Andre Garzia
Buy on Leanpub

致谢辞

感谢我的太太Lili,她是世上最好的太太!

感谢Mozilla对我们的长久信任,感谢他们维持着网络的开放和自由,永远把用户放在首位!

感谢了不起的巴西Mozilla社区,感谢他们对我的完全接纳!

感谢棒得不能再棒的GSoC导师Marcos Caceres、Mozilla WebAPI团队、Mozilla技术布道团队以及Mozilla开发者团队!

感谢2013年的Google编程之夏!这个活动非常精彩!

我还要对Ryuno-Ki、chisophugis、ghost、dholbert、marcoscaceres表示衷心的感谢,他们花费了时间努力提交贡献,使得本书更加完善。

本书永不停更

我打算经常更新本书,扩充内容,修复读者发现的问题。由于Firefox OS的一些API还处于实施阶段,你一定想确保以后阅读到的是最新版本。

关于作者

在本书能多处看到我的个人意见和决定,尤其是我觉得此举能更容易解释我想法的时候,虽然其他程序员可能不会这么做。在表达个人意见时,我会尽量清晰地论证我的观点。总之,如果我的表述不当,我会校正文本,更新本书。更多信息请看“反馈与贡献”部分。

写这本书的缘由

起初,我一直在利用业余时间写作。后来由于我的Google Summer of Code (GSoC)导师Marcos Caceres的鼎力相助,本书成为了我GSoC项目的一部分,此项目的目标是创造有用的Firefox OS开发者资源。因此,非常感谢Google对活动的赞助,以及感谢Mozilla WebAPI团队能让我整夏参与其中。

获取最新动态

本书免费发布在Leanpub

如果是在Leanpub的本书页面下载的,可以通过注册邮箱自动获取更新。本书计划每月多更。如果书是从朋友或其他网站获得的,你应该考虑去上述页面下载,并订阅邮件,以确保接收到更新通知。

捐助

写书需要耗费大量工夫,在2013年的Google编程之夏闭幕后,我愿在余生倾注更多的精力投身于此类活动。如果你觉得本书有帮助(或很酷),可以在Leanpub下载页面,拖动价格滑块,选择0到任意金额进行捐助。喜欢用PayPal的同学,可捐助到agarzia@mac.com账号。

不管捐助与否,你都应该在下载页面填上你的邮箱地址,以便第一时间接收更新通知!

联系作者

评论和反馈可发送邮件至fxosquickguide@andregarzia.com。我的个人主页是http://andregarzia.com。Twitter账号是@soapdog

如果你想帮忙完善本书内容,请看“反馈与贡献”部分。

封面插图

封面由巴西的一位设计师和插画师,Raphael Eckhardt,创作。你可以在http://raphaeleckhardt.com/查看他的作品和联系方式(他是自由职业者)。

面向人群

本书面向已有一定的HTML, CSS和JavaScript基础,想为Firefox OS开发应用的读者。HTML, CSS和JavaScript的教程不在本书讨论范围。不过我会提供一些不错的参考书籍链接。

最佳实践 vs 新手友好

有经验的开发者会留意到,本书的示例代码并不都是最佳写法。虽然我一直在避免反模式,但在本书中,对于立即执行函数和其他类似的最佳实践,我会尽量少用。主要原因是,我想让代码对新手友好,毕竟本书是入门用书。这样一来,老手自然懂得何时何法修改代码,新手也能看得懂。本书的所有代码都能正常运行,以后的更新中,我或许会根据读者的反馈来重写代码,使用更多的最佳实践。

假如你想深入研究如何编写高质量Javascript代码,这里推荐几本好书:

反馈和贡献

本书是免费且开放的,我很兴奋收到任何反馈。本书的全部内容都放在GitHub仓库,并使用Markdown生成(借助Leanpub的某些扩展)。如果你有任何反馈、bug修复和改进,请随时提交合并请求。在此我先对所有的贡献表示感谢。

本书的Git仓库地址是https://github.com/soapdog/firefoxos-quick-guide

翻译

本书最初由我用葡萄牙语编写,然后翻译成英文。两种语言的版本都可以在网上免费获取:

欢迎各方人士帮忙把本书翻译成更多语言(顺便帮我改善蹩脚的英语)。

版本历史

版本 0.3

新增应用管理器章节。由于现在大部分设备运行的仍是Firefox OS 1.1,所以我会保留旧版模拟器的章节。

此版本对应用管理器章节的更新有些仓促。我们将在下次更新完善它。如果你发现新章节(或任何章节)表述有误,请到issue tracker on GitHub报告问题。

版本 0.2

本书由Mozilla’s WebAPI团队的Marcos Caceres修订。每一章内容都进行了技术性的检查,同时修正了许多语法和拼写错误。

版本 0.1

这是本书的首个版本。我尚未在编辑器运行过,同时,拼写错误、语法错误还有其他错误都没有修正。英语不是我的母语,如果表述不当请更正我。本书作为一本快速指南,于2013年8月20号开始编写,发布于22号和23号举行的BrazilJS大会。因此这更像是一本在两天内快速写成的草稿。

我使用了Leanpub系统写这本书。此系统有助于快速迭代,同时让我保持冷静的头脑管理项目。此版本系根据葡萄牙语版,半直译而成。

介绍

Firefox OS

Firefox OS

Firefox OS

Firefox OS是一个由Mozilla及其合作伙伴共同开发的全新的移动操作系统。可运行Firefox OS的设备已经在许多国家上市,并且将在2013年末前拓展到更多地方。

Firefox OS的目标是新兴市场,为这数亿人群带来互联网体验是它的使命。为了达成使命,Firefox OS设备一开始就为担任 好用的第一部智能手机 而生,并辅以有竞争力的价格。Firefox OS设备不与高端智能手机竞争,如Apple iPhone 5S和Samsung Galaxy S4。它们可以为使用上述手机的用户多提供一种选择,除了功能手机外,他们还能以可承受的价格买部Firefox OS设备,享受 完整的智能手机体验

在像巴西和哥伦比亚这样的新兴市场,性能不错的智能手机的价格普遍太贵,普通消费者买不起。人们可以去买部便宜的手机,但由于这些手机的操作系统是为高端设备而开发,于是乎,在便宜手机的硬件上表现不佳,导致用户体验非常糟糕。Firefox OS经过专门设计,能够在有限的硬件下提供良好的用户体验。

另一个让Firefox OS与众不同的特点是它的开放性。考虑到当前的主流移动操作系统都是独占的,系统供应商拥有特权,他们可以不顾开发者和用户的意愿,强行推行自己的做法(还记得Apple曾禁止开发者在iTunes App Store使用除Objective-C以外的语言这件事吗?)。处于如此独占的生态系统,你只能在授权的渠道下发布应用,而且应用所得的收入,永远是这些供应商拿大头。

开发者除了受限于被独占的发布渠道,还被各个系统的软件开发工具(SDKs)限制住了。如果你想用官方开发工具来开发一个iOS原生应用和一个Android原生应用,那么你不得不分别用Objective-C和Java两种语言来写。这意味着,就编程代码而言,开发者几乎不能重用项目间的代码(也许有一些媒体资源可以重用)。想要完成这个任务,开发者得学会两门语言,开发同一个软件两次。

Firefox OS有个不同的地方是,它使用“HTML5”作为开发平台。HTML5是一个市场用语,它用来指代时刻演进着的网络标准集合,包括HTML, CSS和JavaScript。这些免专利费的标准被主流浏览器所采纳,使得开发Web应用变得可能。凭借以HTML5为中心的技术,数以百万计的Web开发者已经能够进行Firefox OS的开发了。依靠Phonegap此类封装工具,Firefox OS上的应用很容易就可以转化到其他平台。

为HTML5而生的平台

互联网无处不在,在你的电脑、手机、智能电视,甚至游戏主机。JavaScript,互联网的开发语言,是世上最受欢迎的编程语言之一。上面已经提到,我们在谈论HTML5的时候,通常指的是这三种技术的集合:HTML, CSS和JavaScript。最近的改进带来了一系列新特性 - 高级表单控件,Web sockets,还有就是,跟XHTML 1.0和HTML 4.01相比,增加了更多语义标记。近期CSS方面也引入了许多新特性,包括Flexbox和CSS动画,这使得创建美观的响应式网页变得更容易了。Javascript最新的发展包括性能的显著提高和新功能的增加,所有的改进都一如既往地易用,不管是对初学者还是经验丰富者。

Firefox OS本质其实是移动互联网的延伸。Mozilla把HTML5奉为一等公民,为数以百万计的Web开发者开放了一个平台。虽然有些浏览器厂商的移动版浏览器也支持HTML5,但是Firefox OS能做的可不止如此,它还提供了一系列能够通过Javascript访问底层硬件和系统的接口。这些接口统称为WebAPIs。

使用WebAPI访问硬件

早前的一些平台也曾尝试过创建一个操作系统,然后在这个操作系统上使用Web技术来开发应用。例如,iPhone刚面世时,唯一能开发应用的方法就是使用Web技术。但是这些Web应用有个局限性,它们不能访问硬件或设备,这意味着你能开发的应用很有限。后来当Apple允许开发者使用Objective-C语言,并且能够访问设备的功能时,它掀起了一股巨大的创新浪潮。很遗憾,Web应用当时没有获得访问设备功能的权限,于是沦为了“二等公民”,这让它们吸引不了用户和开发者,无法与原生应用竞争。

当提及设备功能,我们实际指的是访问硬件和系统级别的功能服务,比如更新电话簿、发送短信以及浏览相机和媒体的相册。在Firefox OS上,你能通过WebAPI来访问这些功能。

早些时候还有一个平台,WebOS,它也允许通过Javascript来访问硬件,但是它却从没想过将APIs标准化。Mozilla连同W3C和其他参与方,一起努力确保WebAPIs是一个开放的标准,让其他浏览器也能采纳它们。当其他浏览器支持这些APIs后,你的应用只需越来越少的修改就能跨平台运行了。

有一点很重要需要强调的是,WebAPIs不仅仅能运行在Firefox OS设备上。Mozilla正在让能运行Firefox的平台支持WebAPIs,例如电脑和Android。这样的话,你的 Web开放应用 就能在Firefox OS、电脑版Firefox和Android版Firefox上运行了。

开发和发布的权利

正如Mozilla的所有项目,Firefox OS是开源且免费的。你可以在Mozilla B2G repository关注项目的开发。你可以关注系统的发展,为项目做贡献,你还有权在你自己的渠道或The Firefox Marketplace下发布应用。更棒的是,所有的系统应用都是用HTML5写的,所以你可以查看代码看它们是如何组织的。

总而言之你不会在任何方面受限于Mozilla。假如你想获取系统的源代码,然后根据你的需求修改它,尽管去做吧。抑或你想开发一些应用仅供公司内部使用,或者只想通过自己的网站来发布作品,这一切都随你。一般来说,其他平台都有自己的官方应用市场,你只能在这个市场发布应用。Firefox OS也有个官方市场叫Marketplace,它也有一个审批流程,但是只要你愿意,你完全可以不通过这个市场发布你的应用。正如在互联网,你可以把你的网站托管到任意地方,在Firefox OS上,你也可以把你的应用放在任何地方。

这里有个小小的注意事项,遗憾的是,某些与安全相关的WebAPIs太过敏感,不允许随便使用。如果你发布的应用用到了Privileged APIs,你需要Mozilla的审查和认证签名。

总结

HTML5已被广泛接受,将来只会变得越来越好。Firefox OS是由Mozilla开发的,完全基于Web技术的全新开源移动操作系统。此系统是完全开放的,它构建了一个稳健的HTML5实施平台,提供了一系列 用Javascript访问操作系统服务 的WebAPI,这点远非其他平台能比。这些新的APIs正通过万维网联盟(W3C)被标准化,并且将来非常有希望被其他浏览器采纳。

下一章我们快速地帮你搭建好Firefox OS的开发环境。

搭建Firefox OS开发环境

Gecko引擎

不同的浏览器使用不同的引擎渲染网页:Google Chrome和Opera用的是Blink(WebKit的一个分支),IE的是Trident,Safari的是WebKit。Mozilla也有自己的引擎,名为Gecko,它被应用于桌面版Firefox、Android版Firefox和Firefox OS中。这些项目都使用了相同的引擎,因此在桌面版Firefox浏览器上进行Firefox OS开发也是可行的。(不过有几个注意事项1

你需要安装哪些程序?

为了开发和测试Firefox OS应用,你需要:

注意:Firefox OS模拟器是用来测试Firefox OS 1.0至1.1的设备的,如果要测试Firefox OS 1.2+,请使用应用管理器(App Manager),我们稍后会详述。

最初写这本书时,Firefox OS 1.1是主流,官方的测试方式是使用Firefox OS 1.1模拟器。现在Mozilla已经改用新的应用管理器,比旧版模拟器好用多了,不过很遗憾,它不能连接Firefox OS 1.1设备。

由于现在有很多人在使用Firefox OS 1.1,且零售市场销售的设备,大部分运行的还是1.1版本,所以我们仍会保留Firefox OS 1.1模拟器章节,并沿用以前的内容。同时我们还将演示如何使用应用管理器来完成同样的任务。应用管理器是当前的主流方式,因此我先讲解应用管理器的内容,再讲解旧版模拟器。

Firefox 29及以上版本已经内置了应用管理器。我们将在应用管理器章节详述。

应用管理器设置

如果你的Firefox版本是29或以上,那么应用管理器已经内置了。当然,光有应用管理器是不够的。你还需通过应用管理器安装个模拟器,这样当你测试的时候,就不需要拿一部真实的设备连接电脑了。关于应用管理器,Mozilla提供了很全面的文档,要是你想深入了解一点可去看看。

应用管理器能够管理多个Firefox OS版本,因此你可以同时安装版本1.3, 1.4和2.0。请记住版本号越高,bug也越多。不过既然允许安装多个版本,那最好都装上吧,这样一来我们就能在不同版本的Firefox OS上测试应用了。

让我们看看这个全新的应用管理器,并把接下来要用到的东西都装上。要打开应用管理器,在菜单栏依次展开工具 -> Web开发者 -> 应用管理器

Where you can find the App Manager

Where you can find the App Manager

打开应用管理器后,你会看到类似下图所示的界面

The App Manager Help

The App Manager Help

点击启动模拟器按钮,选择想要安装的版本。

应用管理器通过ADB与连接的设备进行交互。安装ADB Helper扩展,能帮你处理一切与ADB相关的操作。

我的建议是,把模拟器的全部版本和ADB Helper都装上。

The page to download Simulators and the ADB Helper

The page to download Simulators and the ADB Helper

安装Firefox OS 1.1模拟器

假如你的设备运行的是Firefox OS 1.1,你得安装Firefox OS 1.1模拟器,因为这个新应用管理器不支持你的设备。

安装完Firefox后,下一步是安装Firefox OS 1.1模拟器,我们将用它来测试应用。运行Firefox,展开工具菜单,选择附加组件

*Tools* menu with *Add-ons** menu selected

Tools menu with *Add-ons** menu selected

在右上角的搜索框,输入Firefox OS Simulator进行搜索,点击安装按钮开始安装。

Add-on manager showing the simulator add-on

Add-on manager showing the simulator add-on

注意:如果你的Firefox版本为29或以上,且你的设备运行的是Firefox OS 1.1或更早版本,那么你需要另一个版本的Firefox OS 1.1模拟器,此版本在附加组件商店是找不到的。它尚处于BETA阶段,但已经是现在能下载到的最好的版本了。点击链接下载,Mac OS X, Linux or Windows。只需把下载到的xpi文件拖进Firefox,然后根据提示进行安装。要是你想研究一下怎么在Firefox 29上安装Firefox OS 1.1模拟器,可以参考bug request #1001590 it

安装完附加组件,可以在菜单栏的工具 -> Web开发者 -> Firefox OS Simulator找到它。

Where you can find the simulator after is installed

Where you can find the simulator after is installed

此外,你还能浏览Firefox OS模拟器附加组件页面,在那里下载模拟器。

总结

在这章我们得知,Firefox浏览器、应用管理器和Firefox OS模拟器(还有一个好用的文本编辑器),就是开发Firefox OS应用所需的全部工具了。

所需的全部工具已经搭建好,在开始首次开发前,让我们先掌握一些基本概念。

基本内容

在着手建立第一个应用之前,我们先来了解一下关于Firefox OS开发的各类基础。在说明这一小节我们将了解到,如同网页一样,Firefox OS应用完全基于HTML5。然而,我们不再详述是什么造成两者之间的差别。

我们可以看到当前的App应用通常包含:

  • 名称和图标,用户可以通过点击来加载相关应用。
  • 应用具备与系统服务和底层硬件交互的能力。

简言之,一个Firefox OS应用就像是一个带图标和名称的网页界面,并且通常可以离线运行(视其实际用途而定)。应用中诸如名称,图标以及其他数据都在一个名为mainfest的文件中进行定义,相关内容将在下节详细介绍。

应用主菜单

主菜单文件是一个JSON格式的文件,用以描述hosted网页应用的各方面内容。通常该文件被命名为mainfest.webapp且位于名为index.html的主HTML文件下方。

Mainfest示例


 1 {
 2   "name": "Memos",
 3   "version": "1.1",
 4   "description": "A simple memo taking app",
 5   "launch_path": "/index.html",
 6   "permissions": {
 7     "storage": {
 8       "description": "Required for storing and retrieving notes."
 9       }
10   },
11   "developer": {
12     "name": "Andre Garzia",
13     "url": "http://andregarzia.com"
14   },
15   "icons": {
16     "60": "/style/icons/icon_60.png",
17     "128": "/style/icons/icon_128.png"
18   }
19 }

在上例中我们可以看到一个名为memos3的应用。其内容还描述了应用的作者,图标,名称,通过哪个文件来加载该应用(在本例中是通过index.html文件加载),你的应用对各类硬件设备的访问权限等。如下图所示,这个mainfest文件会被Firefox OS用来将应用加载到设备的主界面,Firefox应用市场也会从该文件提取分类信息。

Memos app shown at the Firefox Marketplace

Memos app shown at the Firefox Marketplace

注意系统是如何利用mainfest文件中的信息将应用加载到主界面,如以下截图所示。

Memos on the simulator

Memos on the simulator

将你的HTML,CSS,JavaScript,和一个mainfest文件放到一起,你就获得了一个可以在Firefox OS上运行的应用。让我们继续基础知识的探讨,认识当前应用的类别。

应用的类别

Firefox OS应用目前可以分为两大类:在线应用和本地应用-在不久的将来可能会有更多的类别(比如:用户自定义键盘和能够创建其他的系统服务)。

  • 在线应用:和通常的网页应用一样运行在网页服务器上。这意味着当用户加载一个在线应用时,该应用的内容将通过从远程服务器上加载(如果有cache可用的话,也可以从中加载)。
  • 本地应用:在安装时复制到设备中的zip格式文件。当用户加载一个本地应用时,该应用的相关内容直接从其zip文件中加载,无需访问服务器。

应用采用这两种格式各有利弊。一方面,在线应用便于管理,开发者只需理保存在服务器上的文件。然而,在线应用很难在离线模式下运行。因为其在离线运行时极大程度地依赖于appcache。在线应用在使用WebAPI方面也会有所受限,导致其不能完成一些离线应用可以完成的任务。

另一方面,离线应用将其所有的内容保存在本地设备中,这样在没有网络的情况下应用也可以正常运行(避免了在线应用会遇到的appcache问题)。离线应用相对在线应用更是具备了调用一些对设备安全较敏感的WebAPI函数的能力。更新离线应用可能会让人比较头疼,开发者需要将每次更新的版本上传到Firefox应用市场,这样就意味着需要花费一些时间来通过审核。

你在选择模式时,要考虑到如果需要调用较高级的WebAPI函数,那就应当采用离线应用模式。然而,要是你的应用可以在无需访问任何高级系统底层服务和硬件的情况下正常运行,那么在线应用模式是更好的选择。如果你没有自己的远程服务器,就选离线模式。

上面我提到过appcache会是一个棘手的问题(有时候在线应用运行时需要appcache)。但是不要太担心,因为我们可以利用很多工具使appcache的生成和开发变得更加简单4

在本书中为了展示WebAPI的各方面的应用,我们将会建立离线应用。然而,我们学习的大部分mainfest方面的内容将会应用到在线应用中。如果你想更多地了解分布式在线应用方面的内容,参见the hosted applications link at the developer hub页面。

既然已经对Firefox OS支持的两类应用有了初步的涉略,现在让我们了解一下这两类应用在获取系统权限方面的差别。

安全权限级别

Firefox OS中的安全级别分为三级,级别越高,能调用的API函数也越多。

  • 基础级(a.k.a. web):是所有应用的默认安全级别。这类在线和离线应用不会在其mainfest文件中声明类型这一属性。这类应用具有调用浏览器中普通API函数的权限-但是不能调用任何Mozilla’s WebAPI。
  • 用户级:这类级别的应用可以调用Firefox浏览器中所有普通的API函数和一些额外的函数,如联系人,系统警告等。只有离线应用才可以成为用户级应用,并且该软件安装包需要获得Firefox OS应用市场的电子签名。
  • 认证级:出于安全方面考虑,只有Mozilla和其合作伙伴(如:手机生产商,电信服务商等)可以使用这个安全级别。认证级应用可以调用所有的API函数,如用于拨打电话的函数等。Firefox OS中的拨号应用就是一个认证级的应用。

在开发中,我们可以直接调用用户级API函数而无需取得Mozilla的许可。但是当在发布一个用户级应用之前,需要先将其提交到Firefox应用市场。代码审查也是严格审核的一部分,如果没有问题的话,应用就会获得电子签名-即告诉Firefox OS用户该应用对那些敏感API函数的调用是获得官方许可的。

在MDN上的WebAPI函数手册页面我们可以看到各类平台对应的API函数以及各API函数需要的权限级别。

Access levels for the APIs

Access levels for the APIs

如上图所示,所有的应用都可以调用IndexedDB API和FileHandle API函数,但是只有用户级应用可以调用Contacts API 和 Device Storage API函数。

Mozilla’s WebAPIs

Firefox OS 为开发者提供各类API函数来使开发出的应用具有较高的平台兼容性。现在通过WebAPI已经可以获得设备硬件和系统服务的访问权限。想要了解当前Firefox OS版本可以的API函数有哪些,可以参见Mozilla维基百科中的WebAPI页面

通过重温一些代码样例我们就可以发现调用这些API函数是多么的简单。不要将本例作为WebAPI函数的官方完全使用手册。我们只是通过示例来讲解通过JavaScript来调用硬件设备的相关功能。

例 #1:打电话

假设你的应用需要在已经输入号码的情况下调用手机的拨号器。你可以使用以下代码来完成这个功能:

发送电话号码到拨号器


1 var call = new MozActivity({
2   name: "dial",
3   data: {
4     number: "5555-9999"
5   }
6 });

这段代码通过向拨号应用发送请求来实现对指定号码的呼叫。需要注意的是,该操作并没有播出电话,用户仍需要通过点击应用下方的拨号按钮来完成拨号。在运行程序前需要用户执行明确的操作是一个不错的安全机制:因为在运行之前首先要需要获得用户的许可。更高安全级别的API函数可以在无需用户的交互操作的情况下独立拨打电话。比如最高安全级别的认证级应用就可以实现这项功能。示例代码中使用的是所有安全级别的应用都可以调用的,名为”Web Activities”的API函数。

该函数更多内容参见more information about Web Activites

例 #2:存号码

假设你的公司有一个内网,你想将内网上同事的联系电话存到你的手机里。那么你可以利用联系人API实现这个操作。

存电话


 1 var contact = new mozContact();
 2 contact.init({name: "Odin"});
 3 
 4 var request = navigator.mozContacts.save(contact);
 5 request.onsuccess = function() {
 6   // contact saved successfully
 7 };
 8 request.onerror = function() {
 9   // there was an error while trying to save the contact
10 };

该API函数会新建一个含联系人数据的对象并无需用户的交互操作直接将其保存到手机的号码簿中。由于访问联系人可能涉及到用户隐私,所以该API函数只能被用户级应用调用。很多WebAPI函数都会像示例中一样建立一个具有成功和失败回调方法的对象。

该API函数的更多参见the page about the Contacts API on the Mozilla Wiki.

例 #3:拍照片

假设你建立了一个可以为照片添加各种神奇滤镜的应用。然后为了方便你想要在应用中添加一个按钮,用户通过点击该按钮就可以直接拍照或者从相册中选取图片。

拍照片


 1 var getphoto = new MozActivity({
 2   name: "pick",
 3   data: {
 4     type: ["image/png", "image/jpg", "image/jpeg"]
 5   }
 6 });
 7 
 8 getphoto.onsuccess = function () {
 9   var img = document.createElement("img");
10   if (this.result.blob.type.indexOf("image") != -1) {
11     img.src = window.URL.createObjectURL(this.result.blob);
12   }
13 };
14 
15 getphoto.onerror = function () { 
16 	// error!
17 };

我们再来看WebActivity的另一个例子。这些WebActivity可以被到各类级别的应用调用。在本例中我们采用离线应用模式并通过检索文件后缀(MIME Types)来获取我们想要的文件。代码运行时,系统通过一个界面和用户交互。用户可以从摄像头拍摄,图片文件夹,壁纸文件夹三个选项中任选一个作为图片来源。执行完选择操作后,就会触发成功回调方法。反之如果用户点击取消按钮的话,就会触发失败回调方法。如下图所示,系统给出一个交互界面让用户选择图片来源:

Example of the *pick activity*

Example of the pick activity

总结

在本章中我们了解到,和传统的网页不同,Firefox OS中的在线和离线应用依赖于mainfest文件。我们还了解到, 出于安全方面考虑,离线应用可以再细分为“用户级”和“认证级”。只有这两类安全级别的应用才可以调用Mozilla家那些功能更为强大的,在线应用和传统网页所无法调用的WebAPI函数。

现在就让我们奋袖出臂,着手建立自己的应用!

我们的第一个App

Memos, a minimalist notepad app

Memos, a minimalist notepad app

在本章中,我们将着手建立一个名为Memos的简易记事本应用。在开工之前,让我们先来回顾下该应用的运行方式。

该应用具有三个界面。主界面列出记事条目。当你点击一条记事(或者添加一条记事)的时候,就会跳转到可以对相关标题和内容进行编辑的详细界面。如下图所示:

Memos, editing screen

Memos, editing screen

在上示界面中,我们可以通过点击垃圾桶图标来删除这项记事。点击之后会弹出一个请求确认的对话框。

Memos, note removal confirmation screen

Memos, note removal confirmation screen

该记事本的源码在这里:the Memos Github Repo(或zip格式:the Memos.zip)。推荐大家下载简单快捷的zip压缩文件。在本书的code文件夹里也有备份:github repository for this book

Memos采用IndexedDB作为数据库来保存条目;采用Gaia Building Blocks来建立交互界面。再版时我会和大家更加深入细致地探讨Gaia Building Blocks,现在我们只管去用它。读者可以通过上述链接了解交互界面开发工具等各类详细内容。

首先我们为要开发的应用建立一个名为 memos的文件夹。

创建应用的manifest文件

Memos的manifest文件相当简洁。先在memos文件下创建一个名为manifest.webapp 的文件。manifest是一种用来描述应用属性的JSON格式文件。该文件通常包含应用名称,应用图标源地址,从哪个文件开始加载应用,该应用会调用哪些用户级API函数等各项信息。

以下我们可以看到Memos应用中manifest文件的详细内容。在复制这些数据的时候要注意避免在文本中添加额外的逗号导致JSON格式出错。当前有很多帮助开发者合法化JSON文件格式的工具,不过这里推荐一款专门用来生成manifest文件的在线工具。详情参见http://appmanifest.org/(译注:这个链接有问题)。关于manifest的更多内容参见this page on MDN about them

Memos manifest file (manifest.webapp)


 1 {
 2   "name": "Memos",
 3   "version": "1.1",
 4   "description": "A simple memo taking app",
 5   "launch_path": "/index.html",
 6   "permissions": {
 7     "storage": {
 8       "description": "Required for storing and retrieving notes."
 9       }
10   },
11   "developer": {
12     "name": "Andre Garzia",
13     "url": "http://andregarzia.com"
14   },
15   "icons": {
16     "60": "/style/icons/icon_60.png",
17     "128": "/style/icons/icon_128.png"
18   }
19 }

我们来看下以上manifest文件中包含了哪些字段。

Field Description
name App的名字.
version App的当前版本.
launch_path 你的App的启动文件.
permissions 你的App请求的 API 权限. 下面是详细信息.
developer 谁开发了这个App
icons 这个App使用的图标的不同尺寸.

这些字段中比较重要的是授权许可字段,通过在该字段中声明对存储设备的读写权限,应用才能运用IndexedDB无限制地存储数据。[^存储限制](由于权限许可应用软件才可以随心所欲地存储-不过要注意不要过多占用设备的存储空间)。

[^存储权限]:相关权限的更多内容参见the page on MDN about app permissions

既然已经建好manifest文件,接下来我们开始着手创建HTML文件。

创建HTML文件

创建HTML文件之前,我们简略地探讨一些关于Gaia Building Blocks的内容。Gaia Building Blocks中包含了很多可以重用到开发者自己的应用上,Firefox OS风格的交互界面代码模板。

就像网页上那样,开发者并没有被强制要求采用以上Firefox OS风格的交互界面模块。是否采用Gaia模块完全取决于开发者自身抉择。并且,一个好的应用本来就应当具有别具一格的自身特点和用户体验。还要说明一点,开发者的应用并不会因为没有采用Gaia模板而遭受偏见或惩罚。在本书中采用Gaia界面模板是因为本人UI设计不行(也没钱另聘平面设计师)。

Gaia模块中的HTML布局将应用的每一屏幕界面定义为一个<section>,其他的元素也遵循一定的默认布局格式。可以从位于github上的memos仓库下载Memos应用源文件(包含Building Blocks)。要是对git和github不熟悉的话,可以下载压缩包:memos.zip

注意:我使用的这一Gaia模块并不是Mozilla发布的最新版本。更新到最新版本会导致Memos应用崩溃。但是在建立你自己的应用时还是应该尽量使用最新版本的Gaia模块。

将Gaia模块包含进来

先将已下载的源码中的 sharedstyle 文件夹复制到之前新建的 memos 文件夹里。这样我们就可以在开发应用时使用Gaia模块了。

然后在 index.html 文件中声明要调用的各个css文件。

 1 <!DOCTYPE html>
 2 <html>
 3 <head>
 4     <meta charset="utf-8">
 5     <link rel="stylesheet" type="text/css" href="style/base.css" />
 6     <link rel="stylesheet" type="text/css" href="style/ui.css" />
 7     <link rel="stylesheet" type="text/css" href="style/building_blocks.css" />
 8     <link rel="stylesheet" type="text/css"
 9           href="shared/style/headers.css" />
10     <link rel="stylesheet" type="text/css"
11           href="shared/style_unstable/lists.css" />
12     <link rel="stylesheet" type="text/css"
13           href="shared/style_unstable/toolbars.css" />
14     <link rel="stylesheet" type="text/css"
15           href="shared/style/input_areas.css" />
16     <link rel="stylesheet" type="text/css"
17           href="shared/style/confirm.css" />
18     <title>Memos</title>
19 </head>

在第1行,我们将文件的DOCTYPE声明为HTML5。第5~15行中,我们对在应用中用作标题,列表,输入框等各种布局的css文件进行调用声明。

创建主界面

现在我们可以开始着手创建各类界面了。像前文提到的那样,应用所用的每个界面被定义为包含在HTML <body> 主体中的一个 <section> 部件。在body标签之后需要将role 的属性设置成 application ,以便CSS选择器据此创建相应界面。所以我们这样定义body标签:<body role="application">。现在让创建第一个屏幕界面并定义相关的body标签。

 1 <body role="application">
 2 
 3 <section role="region" id="memo-list">
 4     <header>
 5         <menu type="toolbar">
 6             <a id="new-memo" href="#"><span class="icon icon-add">add</span></a>
 7         </menu>
 8         <h1>Memos</h1>
 9     </header>
10     <article id="memoList" data-type="list"></article>
11 </section>

以上屏幕界面代码中的 <header> 部件包含了该应用的名称和一个用来添加新记事及其的按钮。这个屏幕也包含一个<article>部件用来容纳记事列表。在后面的JavaScript应用环节中我们将利用按钮和记事ID来捕获事件(capture events)。

记住,每个屏幕界面都是直接由一大段HTML代码定义布局。用其他编程语言构建同样外观的界面的过程通常更加繁杂。在HTML中,我们只需声明各类容器,并赋以对应的ID值以便引用。

主屏幕已建好,接下来我们开始构建编辑界面。

构建编辑界面

因为在用户尝试删除一条记事时需要弹出一个确认删除的对话框,所以编辑界面的代码更加复杂。

 1 <section role="region" id="memo-detail" class="skin-dark hidden">
 2   <header>
 3     <button id="back-to-list"><span class="icon icon-back">back</span>
 4     </button>
 5     <menu type="toolbar">
 6       <a id="share-memo" href="#"><span class="icon icon-share">edit</span>
 7             </a>
 8     </menu>
 9     <form action="#">
10       <input id="memo-title" placeholder="Memo Title" required="required" 
11       type="text">
12       <button type="reset">Remove text</button>
13     </form>
14   </header>
15   <p id="memo-area">
16     <textarea placeholder="Memo content" id="memo-content"></textarea>
17   </p>
18   <div role="toolbar">
19     <ul>
20       <li>
21         <button id="delete-memo" class="icon-delete">Delete</button>
22       </li>
23     </ul>
24   </div>
25   <form id="delete-memo-dialog" role="dialog" data-type="confirm" 
26   class="hidden">
27     <section>
28       <h1>Confirmation</h1>
29       <p>Are you sure you want to delete this memo?</p>
30     </section>
31     <menu>
32       <button id="cancel-delete-action">Cancel</button>
33       <button id="confirm-delete-action" class="danger">Delete</button>
34     </menu>
35   </form>
36 </section>

在编辑界面的顶端,即代码中的<header>部件中包含了:
* 一个可以返回主界面的按钮,
* 一个用来包含记事标题的文本框
* 一个可以通过邮件分享记事的按钮

在顶部菜单栏下方,我们用一个<textarea>部件来容纳记事主内容,用一个带垃圾桶图标的菜单栏来删除当前记事。

编辑界面由以上三个部件以及它们的子节点一起组成。此外我们采用一个<form>部件作为用户试图删除记事时的交互部件。这个简单的提示框仅包含提示文本和两个按钮:一个确认,一个删除。

完成这个<section>后,我们已经实现了所有的屏幕界面。剩下的任务就是声明JavaScript文件的引用位置,结尾添加</html>呼应前文。

1 <script src="/js/model.js"></script>
2 <script src="/js/app.js"></script>
3 </body>
4 </html>

编写Javascript脚本

现在我们开始着手编写可以使应用变得更加生动的Javascript脚本。我们一共创建了两个脚本文件,以便管理和组织代码。

  • model.js:包含存储路径和记事检索路径,但是不含任何应用运行逻辑,用户界面,数据项等信息。理论上,我们可以在其他需要记事功能的应用中重用这个文件。
  • app.js: 将HTML元素和其对应的事件句柄联系起来,并且包含应用的运行逻辑。

以上两个文件都应该放在一个名为js的文件夹中,该文件夹位于styleshared文件夹之后。

model.js

我们将采用IndexedDB存储记事条目。由于事先在应用的manifest文件中对存储权限进行声明,所以存储条目不会受到限制。但是,我们不能因此而滥用权限。Firefox OS设备的存储空间通常有限。所以开发者需要时刻注意你的应用存储了什么数据(如果你的应用占用了设备过多存储空间的话,用户就会卸载应用并给你的应用差评!)。并且存储过量数据的话会对应用的流畅运行产生影响,使应用在运行时有卡顿的感觉。注意,当你向Firefox OS应用市场提交应用,审查者会问你为什么要声明无限大的存储空间 – 如果你不能做出合理解释的话,你的应用将不能通过审核。

以下的这段js代码主要用来打开链接和创建存储空间。

注意:这部分代码在编写方面以通俗易懂为主,没有体现出JS脚本在性能方面的特点。一些全局变量在各种代码段中都有使用(我要开始抱怨了啊)。但是这些。本书的主要目的是为了教授开发Firefox OS应用的主要流程,而不是JS脚本的最佳结构。此外,我会根据读者的后续反馈,在不影响初学者理解的前提下优化代码。

 1 var dbName = "memos";
 2 var dbVersion = 1;
 3 
 4 var db;
 5 var request = indexedDB.open(dbName, dbVersion);
 6 
 7 request.onerror = function (event) {
 8     console.error("Can't open indexedDB!!!", event);
 9 };
10 request.onsuccess = function (event) {
11     console.log("Database opened ok");
12     db = event.target.result;
13 };
14 
15 request.onupgradeneeded = function (event) {
16 
17     console.log("Running onUpgradeNeeded");
18 
19     db = event.target.result;
20 
21     if (!db.objectStoreNames.contains("memos")) {
22 
23         console.log("Creating objectStore for memos");
24 
25         var objectStore = db.createObjectStore("memos", {
26             keyPath: "id",
27             autoIncrement: true
28         });
29         objectStore.createIndex("title", "title", {
30             unique: false
31         });
32 
33         console.log("Adding sample memo");
34         var sampleMemo1 = new Memo();
35         sampleMemo1.title = "Welcome Memo";
36         sampleMemo1.content = "This is a note taking app. Use the plus sign " +
37 	                      "in the topleft corner of the main screen to " +
38 			      "add a new memo. Click a memo to edit it. All " +
39 			      "your changes are automatically saved.";
40 
41         objectStore.add(sampleMemo1);
42     }
43 }

注:源码中的全局变量只是作为教学之用。原谅我在书本中为了节约排版空间而删除了全局变量的相关注释。Github上的源码中任然包含所有注释。以上代码创建了一个db对象和一个request对象。db对象会被源码中的其他方法调用以管理记事的存储。

在调用request.onupgradeneeded方法的同时应用会创建一个新的类似hello worid的记事。该方法会在第一次运行应用(或数据库更新版本)的时候调用。即当应用被首次加载时,数据会初始化并保存一条包含“欢迎使用”内容的记事。

在打开链接和初始化数据库之后,就可以开始编写管理记事内容的相关方法了。

 1 function Memo() {
 2     this.title = "Untitled Memo";
 3     this.content = "";
 4     this.created = Date.now();
 5     this.modified = Date.now();
 6 }
 7 
 8 function listAllMemoTitles(inCallback) {
 9     var objectStore = db.transaction("memos").objectStore("memos");
10     console.log("Listing memos...");
11 
12     objectStore.openCursor().onsuccess = function (event) {
13         var cursor = event.target.result;
14         if (cursor) {
15             console.log("Found memo #" + cursor.value.id +
16 	                         " - " + cursor.value.title);
17             inCallback(null, cursor.value);
18             cursor.continue();
19         }
20     };
21 }
22 
23 function saveMemo(inMemo, inCallback) {
24     var transaction = db.transaction(["memos"], "readwrite");
25     console.log("Saving memo");
26 
27     transaction.oncomplete = function (event) {
28         console.log("All done");
29     };
30 
31     transaction.onerror = function (event) {
32         console.error("Error saving memo:", event);
33         inCallback({
34             error: event
35         }, null);
36 
37     };
38 
39     var objectStore = transaction.objectStore("memos");
40 
41     inMemo.modified = Date.now();
42 
43     var request = objectStore.put(inMemo);
44     request.onsuccess = function (event) {
45         console.log("Memo saved with id: " + request.result);
46         inCallback(null, request.result);
47 
48     };
49 }
50 
51 function deleteMemo(inId, inCallback) {
52     console.log("Deleting memo...");
53     var request = db.transaction(["memos"],
54                   "readwrite").objectStore("memos").delete(inId);
55 
56     request.onsuccess = function (event) {
57         console.log("Memo deleted!");
58         inCallback();
59     };
60 }

在以上代码中我们创建了一个构造器,在必须的字段已经初始化的情况下就可以创建新的应用实例。之后我们调用相关方法来列出记事列表,存储和删除记事条目。其中的部分函数接受一个称为inCallback的回调参数,这个参数会在方法运行完成后返回。之所以采用这种机制主要由IndexedDB的异步同步机制决定。所有的回调函数具备类似callback(error, value)的结构。有些方法的回调参数value值为零。

注:由于本书面向初学者,我不主张调用涉及Promises的API函数。推荐使用那些容易实现相同效果且易于阅读的方法。

既然条目存储和方法调用已经完备,接下来就可以将app.js中的运行逻辑应用到app中。

app.js

该文件包含应用的逻辑。在本书中由于源码冗杂限于篇幅以致不可能全部照搬。我会将其分为几个部分然后分别讲解。

 1 var listView, detailView, currentMemo, deleteMemoDialog;
 2 
 3 function showMemoDetail(inMemo) {
 4     currentMemo = inMemo;
 5     displayMemo();
 6     listView.classList.add("hidden");
 7     detailView.classList.remove("hidden");
 8 }
 9 
10 
11 function displayMemo() {
12     document.getElementById("memo-title").value = currentMemo.title;
13     document.getElementById("memo-content").value = currentMemo.content;
14 }
15 
16 function shareMemo() {
17     var shareActivity = new MozActivity({
18         name: "new",
19         data: {
20             type: "mail",
21             body: currentMemo.content,
22             url: "mailto:?body=" + encodeURIComponent(currentMemo.content) +
23 	                "&subject=" + encodeURIComponent(currentMemo.title)
24 
25         }
26     });
27     shareActivity.onerror = function (e) {
28         console.log("can't share memo", e);
29     };
30 }
31 
32 function textChanged(e) {
33     currentMemo.title = document.getElementById("memo-title").value;
34     currentMemo.content = document.getElementById("memo-content").value;
35     saveMemo(currentMemo, function (err, succ) {
36         console.log("save memo callback ", err, succ);
37         if (!err) {
38             currentMemo.id = succ;
39         }
40     });
41 }
42 
43 function newMemo() {
44     var theMemo = new Memo();
45     showMemoDetail(theMemo);
46 }

在程序开头,我们先声明几个全局变量(哇!!!)来存储后面会用到的指向DOM元素的指针(reference)。其中currentMemo全局变量存储用户当前阅读的记事对象。

showMemoDetail()displayMemo()两个方法一起协同工作。前者将选中的记事加载到后一个方法中,并控制元素的CSS布局生成一个可编辑的记事界面。后者加载记事内容并将其显示在屏幕上。我们可以用一个方法同时实现这两个功能,但是分开的话在调用时更易于experiment。

shareMemo()方法调用调用WebActivity函数来打另一个邮件应用,并将当前的记事内容预先填写到邮件正文中。

textChanged()方法会将所有字段中的数据保存到currentMemo对象中然后保存当前记事。这样的话当记事中的内容发生变化的时候,该方法就会自动将变更的内容同步到IndexedDB数据库中。

newMemo()方法会创建一个新记事并打开一个相关的编辑界面。

 1 function requestDeleteConfirmation() {
 2     deleteMemoDialog.classList.remove("hidden");
 3 }
 4 
 5 function closeDeleteMemoDialog() {
 6     deleteMemoDialog.classList.add("hidden");
 7 }
 8 
 9 function deleteCurrentMemo() {
10     closeDeleteMemoDialog();
11     deleteMemo(currentMemo.id, function (err, succ) {
12         console.log("callback from delete", err, succ);
13         if (!err) {
14             showMemoList();
15         }
16     });
17 }
18 
19 function showMemoList() {
20     currentMemo = null;
21     refreshMemoList();
22     listView.classList.remove("hidden");
23     detailView.classList.add("hidden");
24 }

`requestDeleteConfirmation()方法负责弹出确认删除对话框。

closeDeleteMemoDialog()deleteCurrentMemo()方法会在用户确认删除时触发。

showMemoList()方法在显示记事列表前将先执行一次清除。比如,会将currentMemo方法中的内容清除掉,因为在显示记事列表时我们还没有读取任何具体记事条目。

 1 function refreshMemoList() {
 2     if (!db) {
 3         // HACK:
 4         // this condition may happen upon first time use when the
 5         // indexDB storage is under creation and refreshMemoList()
 6         // is called. Simply waiting for a bit longer before trying again
 7         // will make it work.
 8         console.warn("Database is not ready yet");
 9         setTimeout(refreshMemoList, 1000);
10         return;
11     }
12     console.log("Refreshing memo list");
13 
14     var memoListContainer = document.getElementById("memoList");
15 
16 
17     while (memoListContainer.hasChildNodes()) {
18         memoListContainer.removeChild(memoListContainer.lastChild);
19     }
20 
21     var memoList = document.createElement("ul");
22     memoListContainer.appendChild(memoList);
23 
24     listAllMemoTitles(function (err, value) {
25         var memoItem = document.createElement("li");
26         var memoP = document.createElement("p");
27         var memoTitle = document.createTextNode(value.title);
28 
29         memoItem.addEventListener("click", function (e) {
30             console.log("clicked memo #" + value.id);
31             showMemoDetail(value);
32 
33         });
34 
35         memoP.appendChild(memoTitle);
36         memoItem.appendChild(memoP);
37         memoList.appendChild(memoItem);
38 
39 
40     });
41 }

refreshMemoList()方法通过逐条建立记事列表来调整DOM。你可以使用诸如handlebars 或者 underscore之类的模板来简化编写过程。但是本文中的应用仅仅使用vanilla javascript手工编写完成。 该功能通过上文中的showMemoList()方法调用。

以上就是应用中用到的所有函数方法。现在唯独缺少事件句柄的初始化和refreshMemoList()方法的初始调用代码。

 1 window.onload = function () {
 2     // elements that we're going to reuse in the code
 3     listView = document.getElementById("memo-list");
 4     detailView = document.getElementById("memo-detail");
 5     deleteMemoDialog = document.getElementById("delete-memo-dialog");
 6 
 7     // All the listeners for the interface buttons and for the input changes
 8     document.getElementById("back-to-list")
 9             .addEventListener("click", showMemoList);
10     document.getElementById("new-memo")
11             .addEventListener("click", newMemo);
12     document.getElementById("share-memo")
13             .addEventListener("click", shareMemo);
14     document.getElementById("delete-memo")
15             .addEventListener("click", requestDeleteConfirmation);
16     document.getElementById("confirm-delete-action")
17             .addEventListener("click", deleteCurrentMemo);
18     document.getElementById("cancel-delete-action")
19             .addEventListener("click", closeDeleteMemoDialog);
20     document.getElementById("memo-content")
21             .addEventListener("input", textChanged);
22     document.getElementById("memo-title")
23             .addEventListener("input", textChanged);
24 
25     // the entry point for the app is the following command
26     refreshMemoList();
27 
28 };

所有代码准备好后,我们就可以开始在模拟器中测试应用。我们可以分别通过App Manager或者较旧的Firefox OS 1.1 simulator两种方法进行测试。接下来分别详细讲解这两种技术。

如果你的火狐浏览器版本在29及以上,那么你可以安装 App Manager插件。其他较低版本浏览器可以安装旧版本的模拟器。需要注意的是,App Manager只能连接Firefox OS 1.2以上版本的设备。

如果你有一个Firefox OS 1.1版本的设备,然后你的浏览版本是29,那么你可以根据需要安装对应平台的Firefox OS 1.1 simulator version 5.0 Mac OS X, Linux or Windows。安装完成后你就可以按照指导说明连接上你的设备。

如果你的设备系统是Firefox OS 1.1版本并且是无锁的,那么你还可以选择更新到1.2或更高的系统版本。选择更新会使你之后的工作变得更加顺手。 Geeksphone Keon,Geeksphone Peak和Geeksphone Revolution上面每天都会推出Firefox OS的改进版本,参见http://downloads.geeksphone.com/。 我国的ZTE Open详细更新步骤参见Upgrading your ZTE Open to Firefox 1.1 or 1.2 (fastboot enabled)。LG Fireweb暂时不能更新。如果你们觉得我人不错,然后又觉得LG不能更新很讨厌的话,那么快去他们的官方Twitter上提建议。Alcatel One Touch Fire可以更新,但是其详细步骤已经超出了本书的讨论范畴。

注:bugzilla上的这个bug request #1001590补丁可以修正当前Firefox 29不能运行Firefox OS 1.1模拟器的问题。

用App Manager测试应用

在测试之前,我们最好事先确认下所有的文件都在正确的位置上。你的memos应用的文件布局应当如下图所示:

List of files used by Memos

List of files used by Memos

如果出现不同的文件层次的话,那么你肯定在某些地方写错了。出错的话请将你的源码和这份源码进行比对the memos github repository 。本书附录book repository中的code文件夹内也包含了一份相同的源码。

打开Simulator Dashboard,依次选择Tools -> Web Developer -> App Manager

Where you can find the App Manager

Where you can find the App Manager

打开App Manager后,点击Apps tab中的Add Packaged App选项,然后浏览并选中你的应用所在的文件夹目录。

Adding a new app

Adding a new app

一切顺利的话接下来你就可以在应用列表中看到你的应用。

Memos showing on the App Manager

Memos showing on the App Manager

添加完应用之后,点击Start Simulator按钮并启动一个已安装好的设备模拟器。如果你还没有安装任何模拟器的话,建议你按照屏幕上的说明步骤把它们全部安装上去。

当模拟器开始运行时,在App Manager的memos应用详细界面中点击Update按钮来将memos应用安装到模拟器中。安装完成后memos应用的图标就会出现在模拟器的主屏幕上。可以通过点击图标来运行该应用。

Memos installed on the Simulator

Memos installed on the Simulator

干得漂亮!现在你已经创建并测试完成了你的第一个应用。一个没有新意的简单应用–但是我希望通这个示例让你对Firefox OS应用的创建流程有更加深刻的了解。如你所见,这和基本的Web开发没有很大的区别。

注意,更新应用源文件中任何内容之后,都需要通过点击Update按钮来更新模拟器中存储的对应文件。

在模拟器上测试应用

在测试之前,我们最好事先确认下所有的文件都在正确的位置上。你的memos应用的文件布局应当如下图所示:

List of files used by Memos

List of files used by Memos

如果出现不同的文件层次结构缩进的话那么你肯定在某些地方写错了。出错的话请将你的源码和这份源码进行比对the memos github repository 。本书附录book repository中的code文件夹内也包含了一份相同的源码。

打开Simulator Dashboard,依次选择Tools -> Web Developer -> Firefox OS Simulator

How to open simulator dashboard

How to open simulator dashboard

点击Add Directory按钮,然后浏览并选中你的应用所在的文件夹目录。

Adding a new app

Adding a new app

一切顺利的话接下来你就可以在应用列表中看到你的应用

Memos showing on the dashboard

Memos showing on the dashboard

当你添加一个新的应用时,模拟器会加载并运行你的新应用。接下来你就可以测试应用的各项功能。

干得漂亮!现在你已经创建并测试完成了你的第一个应用。一个没有新意的简单应用–但是我希望通这个示例让你对Firefox OS应用的创建流程有更加深刻的了解。如你所见,这和基本的Web开发没有很大的区别。

注意,更新应用源文件中任何内容之后,都需要通过点击Refresh按钮来更新模拟器中存储的对应文件。

总结

在本章中我们建立了第一个自己的Firefox OS应用并在模拟器上测试运行。下一章我们将涉略开发者工具,那些套件将在开发中助你一臂之力。

开发者工具

Firefox提供了很多工具协助开发者。许多人一直在用FireBug,还没发现其实Firefox已经有内置的开发者工具箱了。在本章,我们将介绍几个对Firefox OS应用开发者最有用的工具。

如果你想多了解这些工具,想知道还有哪些好用的开发者工具会登陆Firefox,可以去Mozilla开发者网络(Mozilla’s Developer Network)查看开发者工具页面(说真的,去看看吧!等你的好消息。)

响应式设计视图介绍

Web开发过程中有个常见的流程:修改HTML文件,刷新页面,观察变化。除非你的项目已经用到了类似Grunt或Volo这样的自动化构建工具,否则,一般来说,没必要专门将这个流程自动化。在Firefox OS模拟器上也可以执行同样的流程,但模拟器的分辨率目前被限制在了480x320。如果你还想应用能运行在平板、平板电话、大尺寸电视或其他平台,这个分辨率远不能满足要求。

想查看你的应用在任意分辨率下的情况,可以使用Firefox的响应式设计视图工具来调整屏幕(以及视区,也译作视口)。如下图所示,依次展开工具 -> Web开发者 -> 响应式设计视图便可开启。开启后,窗口马上发生了变化,这时你可以通过拖拽或选择框来改变视区尺寸。

Activating Responsive Design View

Activating Responsive Design View

测试media queries时,响应式设计视图非常有用。因为它可以让你在调整分辨率后,实时查看网页布局的变化。响应式设计视图还有个很棒的功能:保存预设尺寸。假设你知道目标分辨率,你无需调整浏览器的尺寸就能查看不同分辨率下的页面情况了。

Responsive Design View Sample

Responsive Design View Sample

在本书编写之际,市场上大多数Firefox OS手机的分辨率都是480x320,约96 PPI(pixels per inch,每英寸所拥有的像素数目)。但是,你应该预想到,将来随着Firefox OS设备硬件的发展,这些都会改变:屏幕分辨率越来越高,PPI也随之越来越高(就如Apple的retina视网膜显示技术)。

为了让应用能够适应未来的发展,不要把CSS值固定在某一分辨率或PPI,而是应该运用media queries和响应式设计思想,开发出适应任何屏幕尺寸的应用。想学习更多响应式设计技巧的话,我推荐两本书:Responsive Web DesignMobile First

总之,响应式设计视图使得我们无需调整浏览器窗口,就能在各种不同分辨率下测试Web应用。在我看来,这是最有用的Web开发者工具之一。不过它有个很大的局限性:目前还不能用它测试不同的PPI(例如,查看网站在retina或更高PPI屏幕下的显示情况)。

开发者工具

Firefox的开发者工具和Firebug以及其他现代浏览器内置的工具相似。依靠这些工具,你可以在控制台执行和调试Javascript,操作当前页面的DOM和CSS。

要打开控制台,可以:

  • 依次展开“工具 > Web 开发者 > Web 控制台”
  • 在需要调试的页面右击,选择“查看元素”,然后点击“控制台”标签。
JavaScript Console

JavaScript Console

除了控制台,还有很多其他工具供开发者使用,包括但不限于样式编辑器, 网络, 分析器, 调试器, 查看器

在上一章我们开发应用时,用到了console来查看应用的进展。这是一种非常强大的调试方法,然而部分开发者还在用“alert()”来调试他们的Javascript代码。

使用“alert()”真的不是个好主意,假如开发者忘记把它们移除,最终遭罪的还是用户。而console就能避免这个问题,它会无痛无害(无声无息)地把所有调试信息输出到一个用户平常不会打开的地方,用户体验得以保障。使用console还有个好处,你不必删除代码里的调试信息了,当然删不删都随你。当程序出错的时候(这种事再常见不过了),这对代码维护和调试很有用。

要成为一名称职的开发者,学会正确使用Firefox(或其他浏览器)内置的开发者工具是必不可缺的一步。这就是为何我建议每个人都去看看前面提到的链接,熟悉Firefox现有的各种工具。

有一个特殊工具我上面没提到,远程调试。它允许我们连接Android或Firefox OS手机,然后使用开发者工具实时调试手机的应用。

总结

本章我们简单介绍了Firefox内置的开发者工具。这些工具能简化你的开发过程,尤其是结合Firefox OS模拟器使用。它们是开发应用密不可分的一体。下一章,我们将进一步了解模拟器,学习如何充分利用它。

应用管理器

Firefox OS Simulator Dashboard

Firefox OS Simulator Dashboard

我们在搭建Firefox OS开发环境章节设置好了模拟器,并在开发首个应用章节使用了它。现在让我们进一步研究应用管理器,学会用它来完成常见任务。

若想深入了解应用管理器,请移步MDN查看Firefox OS: Using the App Manager文档。

注意: 如果设备运行的Firefox OS 1.1或更早版本,你需要使用Firefox OS 1.1模拟器扩展,而不是应用管理器。Firefox OS 1.1模拟器的内容在下一章会详述。

添加应用

可以在应用管理器上添加托管应用和打包应用。让我们分别看看添加方法。

添加打包应用

开发首个应用章节,我们已经学习了如何添加打包应用,但为了方便对比,我决定重述一遍要点。

如下图所示,在应用管理器面板点击添加打包应用进行添加。

Showing the *Add Packaged App* option that adds a packaged app to the App Manager

Showing the Add Packaged App option that adds a packaged app to the App Manager

按照图中箭头的指示,点击按钮,Firefox会弹出文件选择框。浏览硬盘文件,选择要添加的包含应用manifest的文件夹。如果manifest文件配置正确,你会在应用列表看到刚添加的应用。

添加托管应用

如果你的应用属于托管应用,你得在Web服务器上测试。别用之前的方法来测试托管应用,因为某些错误只在服务器环境产生,模拟器是不会提示的,例如识别不出manifest的MIME类型。在把应用提交到Mozilla Marketplace前,必须解决掉这些错误。

大部分托管应用并不是为Firefox OS单独开发的应用,而是基于响应式设计的网站,它们能够适配不同的设备和分辨率。为了能作为应用正常运行,它们的后台通常都十分复杂,这就是为什么要在真实的Web服务器环境测试的原因。

要想在模拟器运行托管应用,在文本框填上应用的URL,点击添加托管应用按钮。

Adding a hosted app to the App Manager

Adding a hosted app to the App Manager

点击后,manifest会被验证,如果配置正确,应用将被添加到应用管理器。

运行你的应用

点击启动模拟器按钮,从已安装的模拟器中选择一个。

模拟器启动后,在应用列表点击更新,你的应用将被安装进模拟器。

一旦安装完毕,应用图标会出现在模拟器的主屏幕,直接点击运行即可。

更新你的应用

每次在文件改动后,再次测试前,都需要先点击更新按钮以更新模拟器上的应用。

调试

安装完毕后,可以点击应用列表上的调试按钮进行调试。此时你的应用将会在模拟器中运行,并且调试器也会打开。

What button to press

What button to press

点击按钮后出现的界面如下图所示:

Developer Tools connected to the app running on the simulator

Developer Tools connected to the app running on the simulator

应用连接上开发者工具后,你就可以进行测试Javascript,调试DOM,编辑CSS等操作了。套用一句创业公司常挂嘴边的话:不断调整直到好用为止

如果应用在模拟器上运行良好,那么是时候在真实设备上测试了。

在真实设备上测试应用

真实设备测试的重要性无可取代。在模拟器上,你对着电脑屏幕靠点击鼠标进行测试;在真实设备上,则要用手指在触摸屏滑动以及按压物理按钮。两者的用户体验和开发体验都相差甚远。

我给大家讲个简单的故事,这个故事体现了在真实设备测试的重要性。几年前,我和Raphael Eckhardt(本书封面的设计者)开发了一个类似 宝石迷情的智力游戏。游戏涉及到一些拖放操作,要把棋子拖放到棋盘。它在模拟器上完美运行。

然后我们把游戏装到了手机上,此时我们才察觉,这个游戏在触摸屏上根本没法玩。手在屏幕操控时,棋盘被手遮住了。更糟的是,棋子太小了,比指尖还小,用户完全看不到他们在拖动什么。一句话,用户体验烂成渣。究其原因,我们一直在凭借小小的鼠标指针在模拟器测试。当使用比指针粗得多的手指去操控时,我们意识到,整个UI不得不推倒重做。

为了避免惨剧重演,记住一定要找部真实设备来测试,哦不,两部,哦不,有多少测多少,越多越好。要经常做简单的原型测试,否则,你会浪费大量宝贵的时间金钱在重做资源文件上。

连接Firefox OS设备

假如你有一部Firefox OS设备(所有驱动都已装好),并连接好电脑了,那么你可以通过应用管理器直接把应用安装进设备。应用管理器检测到插入的Firefox OS设备后,它会在底部的启动模拟器按钮旁边显示设备ID。

Device Connected!

Device Connected!

点击ID,设备会请求你的许可,点击同意后才能建立远程调试连接。一旦连接建立,就能通过应用列表的更新调试按钮进行相应操作了,使用方法和模拟器没两样。

总结

总而言之,应用管理器太棒了。它比旧版Firefox OS 1.1模拟器好用太多,因为它提供了更好的开发者工具,而且能够运行多个Firefox OS版本。可以预想到,依靠内置的manifest编辑器以及更多的功能,应用管理器将变得越来越棒。

学到现在,除了感叹和鼓舞,但愿你还掌握了开发Firefox OS应用的流程。

下一章我们来谈谈旧版Firefox OS 1.1模拟器扩展。这是唯一一种连接Firefox OS 1.1设备的方法。下章内容和本章极其相似,实际上,只是界面不同而已。

讲完模拟器后,我们接着会讨论如何发布你的应用。

Firefox OS模拟器

Firefox OS Simulator Dashboard

Firefox OS Simulator Dashboard

注意:本章内容适用于运行Firefox OS 1.1的设备。当前的主流测试和调试方法是使用应用管理器,我们在上一章已详细介绍。本章仅供需要在Firefox OS 1.1设备测试的人参考。

注意:如果你的Firefox版本为29或以上,且设备运行的是Firefox OS 1.1或更早版本,那么你需要另一个版本的Firefox OS 1.1模拟器,此版本在附加组件商店是找不到的。它尚处于BETA阶段,但已经是现在能下载到的最好的版本了。点击链接下载,Mac OS X, Linux or Windows。只需把下载到的xpi文件拖进Firefox,然后根据提示进行安装。要是你想研究一下怎么在Firefox 29上安装Firefox OS 1.1模拟器,可以参考bug request #1001590 it

我们在搭建Firefox OS开发环境章节安装好了模拟器,并在开发首个应用章节使用了它。现在让我们进一步研究模拟器的功能,学会用它来完成常见任务。

若想深入了解模拟器,请移步MDN查看Firefox OS Simulator文档。

请谨记,如果设备运行的是Firefox OS 1.2+版本,你需要使用应用管理器,而不是Firefox OS模拟器。应用管理器的内容已在上一章详述。

添加应用

可以在应用管理器上添加托管应用和打包应用。让我们分别看看添加方法。

添加打包应用

开发首个应用章节,我们已经学习了如何添加打包应用,但为了方便对比,我决定重述一遍要点。

如下图所示,在模拟器面板点击添加文件夹来添加打包应用。

Showing the *Add Directory* button that adds a packaged app to the simulator

Showing the Add Directory button that adds a packaged app to the simulator

按照图中箭头的指示,点击按钮,Firefox会弹出文件选择框。浏览硬盘文件,选择要添加的应用manifest。如果manifest文件和起始页的配置正确无误,应用将被成功添加,同时模拟器会开始运行它。假如manifest文件或其他地方出错,面板上会显示错误报告。

Example of an invalid manifest

Example of an invalid manifest

每次应用改动后,都需要先点击刷新按钮以更新模拟器上的应用版本(也可在模拟器内按下CMD/CTRL+R来刷新)。

添加托管应用

如果你的应用属于托管应用,你得在Web服务器上测试。别用之前的方法来测试托管应用,因为某些错误只在服务器环境产生,模拟器是不会提示的,例如识别不出manifest的MIME类型。在把应用提交到Mozilla Marketplace前,必须解决掉这些错误。

大部分托管应用并不是为Firefox OS单独开发的应用,而是基于响应式设计的网站,它们能够适配不同的设备和分辨率。为了能作为应用正常运行,它们的后台通常都十分复杂,这就是为什么要在真实的Web服务器环境测试的原因。

要想在模拟器运行托管应用,在顶部文本框填上应用的URL,点击添加URL按钮。

Adding a hosted app to the simulator

Adding a hosted app to the simulator

点击后,manifest会被验证,如果配置正确,应用将被成功添加,同时模拟器会开始运行它。跟打包应用一样,假如manifest配置不正确,面板会显示错误报告(比如“提交到marketplace的应用要求至少有一个128x128图标”)。

每次应用改动后,都需要先点击刷新按钮以更新模拟器上的应用版本(也可在模拟器内按下CMD/CTRL+R来刷新),这点也跟打包应用一样。

调试

安装完毕后,可以点击应用旁边的连接按钮进行调试。此时你的应用将会在模拟器中运行,并且调试器也会打开,与应用连接。

What button to press

What button to press

点击按钮后出现的界面如下图所示:

Developer Tools connected to the app running on the simulator

Developer Tools connected to the app running on the simulator

应用连接上开发者工具后,你就可以进行测试Javascript,调试DOM,编辑CSS等操作了。套用一句创业公司常挂嘴边的话:不断调整直到好用为止

如果应用在模拟器上运行良好,那么是时候在真实设备上测试了。

在真实设备上测试应用

真实设备测试的重要性无可取代。在模拟器上,你对着电脑屏幕靠点击鼠标进行测试;在真实设备上,则要用手指在触摸屏滑动以及按压物理按钮。两者的用户体验和开发体验都相差甚远。

我给大家讲个简单的故事,这个故事体现了在真实设备测试的重要性。几年前,我和Raphael Eckhardt(本书封面的设计者)开发了一个类似 宝石迷情的智力游戏。游戏涉及到一些拖放操作,要把棋子拖放到棋盘。它在模拟器上完美运行。

然后我们把游戏装到了手机上,此时我们才察觉,这个游戏在触摸屏上根本没法玩。手在屏幕操控时,棋盘被手遮住了。更糟的是,棋子太小了,比指尖还小,用户完全看不到他们在拖动什么。一句话,用户体验烂成渣。究其原因,我们一直在凭借小小的鼠标指针在模拟器测试。当使用比指针粗得多的手指去操控时,我们意识到,整个UI不得不推倒重做。

为了避免惨剧重演,记住一定要找部真实设备来测试,哦不,两部,哦不,有多少测多少,越多越好。要经常做简单的原型测试,否则,你会浪费大量宝贵的时间金钱在重做资源文件上。

你可以到Geeksphone Shop买部Firefox OS开发者预览版手机。我推荐Geeksphone Keon,因为它的硬件配置和Mozilla合作伙伴推出的设备相近。

面向消费者的Firefox OS设备已经在多个国家推出,假如你身处的国家刚好是其中之一,也可通过这种途径购买。还有个办法,把Android机刷成Firefox OS(只有几部特定型号才能刷,变砖了可别怪我!)。我不推荐此法,除非你是个喜欢折腾的高手。

连接Firefox OS设备

假如你有一部Firefox OS设备(所有驱动都已装好),并连接好电脑了,那么你可以通过模拟器直接把应用安装进手机。模拟器检测到插入的Firefox OS手机后,它会提示设备已连接

Device Connected!

Device Connected!

如果手机已经连接好,模拟器会在刷新连接旁边新增一个推送按钮。点击它,设备屏幕出现个许可请求窗口,询问是否确定安装此应用。

Which button to press to push apps to the connected device

Which button to press to push apps to the connected device

下图是许可请求界面。

Not the best picture in the world but shows the permission screen (sorry for the face it was 4:25 AM)

Not the best picture in the world but shows the permission screen (sorry for the face it was 4:25 AM)

你可以通过远程调试连接调试器来调试应用。

总结

总而言之,Firefox OS模拟器在开发某些应用时很好用。但假如你想用模拟器开发一个适配多种设备的应用,它的局限性暴露无遗(例如,目前它模拟不了Firefox OS在平板上的使用情景)。

学到现在,除了感叹和鼓舞,但愿你还掌握了开发Firefox OS应用的流程。

下一章我们来谈谈全新的应用管理器,它是Mozilla新引入的应用调试系统,相比Firefox OS模拟器而言,它拥有更多功能。

发布你的应用

应用已经开发完毕,我们得考虑一下怎样给用户使用。我在介绍章节提到过,Mozilla不会强迫我们使用官方发布渠道,在哪里发布作品完全由我们自己做主。本章将介绍如何Firefox Marketplace发布应用。

个人愚见,在以下两个场景你可能会选择在Mozilla Marketplace外发布。

  1. 你的应用仅供公司内部或指定用户使用。放到Marketplace的话,所有人都能下载了。若想限制使用人群,你需要在服务器后台增加身份验证或类似的模块。举个例子,Evernote首次运行时,它会要求用户先登录到它的服务器。
  2. 你已经拥有庞大的用户群,可直接向他们发布。以Financial Times这类新闻报纸为例,他们在自己网站上发布就能覆盖大部分用户。请记住,你的应用可以同时在Firefox Marketplace发布,这样不但能利用自己的发布渠道,还能借助Firefox Marketplace开拓新用户。

托管应用和打包应用的发布流程差不多,但所用函数不同,因此我会分开讨论。不管是托管应用还是打包应用,流程大体相同:在主页放一个类似点击安装的按钮(超链接),或者提供一个特殊URL,用户点开后自动开始安装。两种方法都会弹出提示框,询问用户是否确定安装此应用。

托管应用

托管应用的安装代码


1 var installapp = navigator.mozApps.install(manifestURL);
2 installapp.onsuccess = function(data) {
3   // An App was installed.
4 };
5 installapp.onerror = function() {
6  // An App was not installed, more information at 
7  // installapp.error.name
8 };

在上面的示例代码中,“manifestURL”的值是manifest文件的地址。代码运行时,系统会询问用户是否确定安装此应用,然后根据用户选择返回success或error事件。

若想深入了解这个API,请移步MDN查看安装应用页面。

打包应用

打包应用的安装代码和托管应用差不多,不过不是调用“mozApps.install()”,而是“mozApps.installPackage()”,示例代码如下:

打包应用的安装代码


1 // Absolute url to package:
2 var packageURL = "http://myapp.example.com/myapp.zip";
3 var installapp = navigator.mozApps.installPackage(packageURL);

注意:如果打包应用是在Marketplace外发布的话,估计Firefox OS 1.0.1设备安装不了。虽然提供了API,但我没试过。有试过的同学,请把结果反馈给我,我会更新本书内容。

总结

本章讨论了如何使用Open Web Apps的安装管理API在Firefox Marketplace外发布应用。这些API还可以做很多事,例如检测应用是否已安装,如果是,你可以隐藏点击安装按钮。 若想进一步了解API的用法,可移步MDN查看安装应用页面(没错,我上面提过了,但这一次,点开它吧,里面有重要内容)。

下一章我们将学习如何在Firefox Marketplace发布应用。

Firefox Marketplace

Firefox Marketplace

Firefox Marketplace

Firefox Marketplace是一个在线商店,用户可在上面购买或下载Firefox OS、Firefox和Android版Firefox的应用。这是发布Firefox OS应用的主要渠道,但不是唯一渠道。如果你想在Firefox Marketplace外发布应用,请参考上一章

要提交应用到Marketplace,首先需要通过Mozilla Persona的验证。点击登录,按照指示操作即可。验证成功后,你就可以准备提交了。

产生提交应用的想法之前就应该核对好的清单

所有提交到Marketplace的应用都有个审批流程(听起来吓人而已!)。托管应用的审批没privileged应用那么严格,因为前者没有涉及敏感API。提交前,先看看Marketplace的审查准则。最重要的部分是(个人愚见):

  • Firefox OS设备没有Android和桌面浏览器那种返回按钮。假如用户点进了应用的某个地方,但没办法返回上处(即被困住了),你的应用将被驳回。
  • 应用需要提供一个60x60的图标和清晰的描述。
  • 应用的功能应和描述一致。描述的功能若和实际提供的功能不一致,应用将被驳回。
  • 如果应用申请了某权限,那么你必须在代码使用它。把你的应用标记为privileged应用,但是代码却没有使用任何privileged API的话,应用会被驳回,并要求你重新提交为普通应用。
  • 应用必须包含隐私政策说明。
  • Manifest文件的MIME类型必须能被服务器正确识别,并和托管应用放在同一域下。

上述链接还提到了其他准则 - Mozilla有权随时修改,不会另行通知。该页面内容值得你花时间阅读。要是因为一些容易修复的小问题而被驳回,那可真是太浪费时间了。最好第一次就把全部东西准备妥当(好的应用很受审核员青睐!)

准备提交应用

托管应用和打包应用的提交步骤是不同的。就托管应用而言,只需确保拥有正确的MIME类型和manifest文件,并能被在线访问到。打包应用则需要以zip格式压缩,且还有些注意事项。

很多开发者会错把包含应用文件的文件夹打包了。这导致压缩包包含一个文件夹,该文件夹包含应用。这是错误的打包方式。正确做法是:打包后,manifest文件位于压缩包的根目录。Mac OS X和Linux用户可使用terminal导航到应用文件夹,运行命令,如“zip -r myapp.zip *”,来正确打包应用,如下图所示:

Correctly zipping the files

Correctly zipping the files

这个zip压缩包就是我们要提交到Marketplace的。

提交应用到Marketplace

应用已经准备就绪,并且你确定它是遵循审查准则的,那么是时候提交到Marketplace了。点击Marketplace页面顶部的齿轮,选择“我的应用”。

My Submissions

My Submissions

在应用管理页面,点击顶部菜单的提交一个应用

Submit An App

Submit An App

这个链接会把你带到提交新应用的表单,如下图所示。

Submit New App

Submit New App

在此页面有以下选项可选:

  • 托管应用还是打包应用
  • 免费还是付费(或者应用内购买)
  • 支持哪些平台(Firefox OS, Firefox桌面版, Firefox手机版, Firefox平板版)

选择完后自动进入下一屏。本书着重讲解打包应用的发布,但其实托管应用的流程大体相同。

在本章剩余的内容里,我们假设要发布的是免费的Firefox OS打包应用。这种情况下,我们需要上传前面准备好的zip压缩包。

上传完毕,它会开始自动处理,生成一个包含很多选项的报告。

After the zip upload

After the zip upload

如上图所示,刚才提交的应用没出错,但包含了6个警告。为了继续演示,我们忽略掉,接着来勾选应用最低需求。在这里,最后的智能手机尺寸的屏幕(有qHD分辨率)不要勾选,因为我们的应用可适配各种屏幕尺寸。

下一步是步骤 #3: 详情,你需要补充完整应用的信息,例如分类、描述、截图等。

Filling details

Filling details

填写完详情后,整个提交过程结束。现在只需等待Marketplace审核员的通过了。恭喜你提交了一个Firefox OS应用!

你可以到应用管理页面查看提交状态,有需要的话还可以修改详情。

若想深入了解如何提交应用到Firefox Marketplace,可参考Firefox OS developer hub里的文档。

总结

恭喜!!!你在Firefox Marketplace发布了一个新应用,这是对一个全新市场的探索。

我希望你喜欢这本快速指南。我打算经常更新和扩展该指南,所以请持续关注,订阅更新吧。如果你的书是在Leanpub下载的,很好,每次更新你都会收到邮件。如果是在别处下载的,请考虑在Leanpub的官方页面获取更新,以及用邮件订阅。本书完全免费,而且你绝对不会收到任何垃圾邮件,我保证。

欢迎反馈。这本书是我在一个科技大会召开前,抽出两晚通宵赶出来的,所以你能体会我是有多享受这个项目,多想看它成功。如有反馈,可去Twitter@soapdog,或者发送邮件到fxosquickguide@andregarzia.com。我的个人主页是http://andregarzia.com

现在你是Firefox OS应用开发者的一员了,来成为我大Mozilla社区的一员吧,Mozilla社区致力于打造自由的网络,对用户完全开放的网络。来http://www.mozilla.org/contribute/加入我们,帮助Firefox OS成长!

附录 1: 相关链接

  1. 虽然Mozilla的这些项目使用的都是同一个引擎,但是Firefox OS的引擎版本一般都落后于桌面版。因为目前Firefox OS的发布周期比桌面版Firefox浏览器的要长。在实际应用中,这意味着在Firefox OS设备上运行的时候,某些功能不能被支持(或者不能按预期正常运作)。所以一定要确保在Firefox OS设备上测试你的应用。还有一点请谨记,用户设备的Firefox OS版本不尽相同,不是每个用户都能用上最前沿的特性。当设备不支持某些特性时,一定要提供备选方案。
  2. 市面上有许多很棒的编辑器,它们难易不同,功能各异。假如你心尚未有所属,我推荐一个很流行的编辑器,SublimeText。我个人则比较喜欢WebStorm,一个用来开发Web应用并具有完整功能的IDE。
  3. 一个简单的Firefox OS应用在Firefox应用市场中的示例 以及该应用在GitHub中的源码。
  4. 在以下链接中可以找到很多有用的工具,参见GulpGruntVoloYeomanBower这些链接。这些工具功能上可能会产生较大的重叠,如何抉择完全取决于开发者的个人喜好。(相对Grunt本人更加喜欢Volo因为它的文件对我来说更加容易阅读)。