在 Worklight 现有的架构中,混合模式的手机应用可以通过创建各自的平台代码来实现对不同操作系统的支持;如果更进一步需要支持相同操作系统的不同版本或者不同分辨率,还需要在相同操作系统下创建不同的皮肤(skin)来完成这个任务。
但是在实际的项目开发中,开发方不但希望应用可以支持单个客户的需求,还希望已经开发的模块可以复用到其他客户需求相似的应用中,这就要求应用不但可以支持不同的操作系统,还可以支持不同客户在主要功能相同或者相似的情况下,个性化定制的情况。这时 Worklight 框架下的项目管理就显得力不从心。于是开发不同客户代码时开发人员需要复制多套基础代码,然后在各自的代码下进行编程。这样的方法不利于代码的整体维护,具体有以下几个缺点:
在代码管理服务器上,有很多冗余的代码,彼此没有任何的联系; 一旦基础部分发生了问题或者有任何的变化,就会影响到所有使用这些基础部分的客户,相应的修改量是巨大而且不容易维护的; 随着客户的增多,如果发现一种功能有很多用户使用,需要进行公共部分的抽取,这时所需要的努力又是很大的; 当一个客户的应用发生某种错误时,就需要判定到底是基础代码的问题还是公共代码的问题,是否会对其他客户有功能的影响; Worklight 提供的面向文件类型的项目结构不利于系统的模块化;
问题分析和解决方案
为了解决上述问题,文章将从维护不同客户代码需要考虑的维度作为起点,观察影响一个应用的参数到底有哪些?图 1 中展示了相应的维度:
图 1. 考虑的维度
从图中可以看到一个应用所有内容的确定由客户、平台、模块和环境决定
客户:不同的客户需要维护其不同的特殊内容,比如客户有他们特定的 LOGO,对于具体的页面可能内容会有所不同; 平台:需要支持的操作系统,因为同一功能在不同的操作系统上实现可能是不同的,比如 iPhone 需要在界面上显示回退按钮,
但是 Android 需要监听系统的回退按钮; 模块:应用需要使用的模块,一个整体的应用可能支持多个模块,但是一个具体的客户,可能只需要其中某几个模块,在 Worklight 项目中如何更好的模块化是 JavaScript 编程需要考虑的内容; 环境:一个应用在开发周期中,可能需要构建不同的版本,比如测试,用户体验和产品,后台部属的服务器和连接的企业环境都可能不同;
从以上的叙述可以看出,Worklight 平台比较欠缺的是对于不同客户和不同模块的支持。对于客户因为没有构建相应的文件系统,所以平台不可能同时支持多个客户;对于模块,平台采取的文件系统是面向文件类型的,比如在一个应用下,所有内容被分为四部分:HTML,JavaScript,CSS 和 IMAGE,但是在面向模块的结构中,内容应该是根据模块划分的,明显的例子就是在 Dojo 架构下,项目被分为 dojo,dijit 和 dojox;在 jQuery 架构下,项目被分为基础和不同的插件。
所以本文将提出一种新的 Worklight 项目组织方式,通过这种方式系统可以同时管理不同客户的代码,并且尽量消除代码的冗余,而且只需要维护一份基础代码,这样在基础部分需要修改时,修改量就大大减少了;并且当不同客户端的代码需要被抽取到公共部分时,也更加容易维护。它的架构图如图 2 所示:
图 2. 系统架构图
从架构图可以看到整个系统分为三部分:第一部分是在代码管理器上的内容,系统会创建一个 Worklight 项目的模板以及不同客户端和公共部分的 web 代码;第二部分是一个具体的 Worklight 项目,它将由自动化配置脚本根据模板项目、web 代码和配置文件生成,也就是目前一个标准的 Worklight 项目;第三部分是部属在 Worklight 服务器上的应用内容和发布在类似 App Store 或者 Android Market 上的手机应用。在下面的内容中,文章将根据模拟的客户来描述上面内容生成的过程。
实例分析
在接下来的内容中,文章将对各部分的内容,部分之间的转化过程和各自的原因进行详细的描述。
文件架构介绍
这个系统将由三个假想的客户组成,分别是奥迪、奔驰和宝马;需要支持在 iPhone 和 Android 上的应用部署;系统一共支持三个模块,分别是新车浏览模块、车辆自配模块和车辆维修点查询模块。那么根据上面的内容,整体项目在代码管理器上的文件路径将如图 3 所示:
图 3. 系统架构图
从图 3 可以看到,系统使用 core 文件夹保存所有公共部分的代码,然后将不同客户的代码放入到各自的文件夹中。在不同客户(core 本身也可以看作是一个客户)的文件夹中根据模块来管理代码。其中的 base 文件夹有三个作用:一、存放系统用到的 JavaScript 框架,比如 jQuery 或者 Dojo;二、基础的 util 功能模块,比如对国际化的支持,或者创建一个基本页面;三、基本的模块,比如登录模块、关于我们等等。
在不同的模块中(也包括 base 模块),内容由四部分组成:HTML,JavaScript,IMAGE 和 CSS,其中 IMAGE 包括两部分图片:一、网页呈现需要的图片;二、在本地应用中需要用到的图片。这样的文件结构解决了 Worklight 框架中原来存在的问题:把系统根据文件类型划分的方式。根据文件类型划分,模块的增减需要复杂的配置信息,而且容易出错;根据解决方案中的内容,模块高度内聚化,模块的增减只需要识别最高层文件夹信息就可以完成。
在 template 文件夹中,将保存一个 Worklight 项目的初始化内容,并且初始化 iPhone 和 Android 环境,然后在各自的配置内容中添加一些基础的信息,包括 iPhone 是否支持横屏,启动画面的名称和应用版本的编号。这样做的原因在于,根据平台规定的一些内容,跟项目的整体功能关系较少,但是如果每次构建的时候都手动创建这些内容既比较繁琐也容易出错,所以单独存放在代码管理器中,如果 Worklight 的版本变更,只需要重新创建一个模板项目就可以,不需要去修改相应的 web 内容,实现了彼此的分离。
配置文件描述
为了根据客户的不同需求,系统会使用自动化脚本动态生成 Worklight 项目(根据上面定义的 web 内容和模板内容),为了根据不同生成需求提取不同的内容就需要不同的配置文件,每个客户的配置文件样例如清单 1 所示:
清单 1. 配置文件示例
test.worklight.url = https://www.test.com test.sever.url = https://www.server.com version.1_0 = newcar, DIY version.1_1 = newcar, DIY, 4S Bundle.id = com.customer.mycar android.vcode = 1 android.package = com.customer.mycar
从清单上可以看到,配置文件包括三部分的内容:Worklight 和后台服务器的 URL,这部分内容可能根据客户不同的测试阶段会有不同的值;版本信息,这部分内容,主要根据用户不同版本来决定需要读取的内容,从清单中可以看到,在 1.0 版本中应用只支持新车浏览和车辆自配模块,但是到了 1.1 版本,除支持上述两个模块外,还添加了车辆维修点模块。最后是一些平台相关信息,比如当生成不同操作系统下应用时,应用的标识信息;多次生成同一应用时,应用的版本信息。
在实际的构建过程中,开发人员只需要输入构建需要的信息,系统就会自动读取相应的内容并且和模板内容集成生成完整的 Worklight 项目。
自动构建
在前面的两部分内容中,文章描述了源文件的组织形式,在本节文章将描述如何将这些内容组织成一个完整的 Worklight 项目。自动构建可以使用类似 ant 的自动化脚本,在自动构建中将完成三个任务:一、根据参数提取需要的内容并且组成一个完整的 Worklight 项目;二、修改相应的配置信息,让最后生成的应用符合相应的需求;三、项目的编译,同步 web 内容到不同的操作系统端。所以第一步就是需要根据不同文件类型的特点规定文件拷贝的原则。
HTML 和 IMAGE 文件将按照覆盖的原则来构建不同客户和环境的代码,因为在 Worklight 平台中,这两个类型文件是根据覆盖原则来进行的:比如在 common 目录下有 test.html 文件,在 android 文件夹同目录下也有 test.html 文件,那么后者将代替前者。所以在不同操作系统或者不同客户的代码中,需要保留各自完整的内容。
JavaScript 文件是根据函数的区分来定制各自的文件的,也就是说,文件将以 core 环境下的内容作为基础,然后在其他环境下存放和 core 环境下有差别的函数。比如在 core 下有一个 a.js,其中包含十个函数;在 audi 下的 a.js 有两个函数需要复写,那么 audi 的 a.js 就存储这两个复写的函数即可。这么做的原因在于 JavaScript 文件可以通过脚本进行合并,同名函数出现在后面的内容会覆盖前面的内容(所以 core 环境的内容在最上面)。
对于 CSS 文件是根据 tag(比如定义的 class 或者 HTML 标签)来定义规则,如果在不同客户下相同规则的内容不同,就需要完全编辑这条规则。如清单 2 所示,两条规则都作用于 div 这个标签,其合并后(按照其先后顺序)会变成清单 3 的结果。
清单 2. 同名 CSS 标签
// applicatoin.css core div { color: red; text-align: center; } // application.css for audi div { color: blue; font-weight: bold; }
清单 3. 合成后的 CSS 标签
// applicatoin.css result div { color: blue; text-align: center; font-weight: bold; }
所以 CSS 不是简单的覆盖,而是融合,为了修改原先定义的规则(而不是标签),一定要在后面定义的地方覆盖它。
在描述了不同文件的覆盖规则后,自动构建的第一步可以由五小步组成:
根据需要构建的内容,输入客户、版本和相应的测试环境 系统读取 core 文件夹下需要的内容(根据版本中定义的模块)放入 Worklight 的 common 文件夹下 系统读取客户文件夹下需要的内容,根据其文件内容进行不同的操作 HTML 和 IMAGE 文件,覆盖其同名文件; JavaScript 和 CSS 文件,合并其同名文件,core 文件内容在上; 系统读取相应平台文件下需要的内容,根据环境放入 Android 或者 iPhone 文件夹下; 系统读取相应客户平台文件下需要的内容,按照规则 3 进行合并
这里需要提出一点,就是在 IMAGE 中有两类文件,对应于 web 内容和 native 内容,需要拷贝到不同的文件夹下。
配置信息修改是根据配置文件中需要调换的内容,修改已经生成的 Worklight 项目中相应的属性,比如后台的 URL,这需要根据具体项目定义其中的内容。最后项目的自动生成将放在应用生成的部分进行详细描述,因为都用到了 Worklight 提供的 jar 里面的方法。
应用生成
应用生成主要通过 Worklight 中提供的 build jar 来完成,相应的内容需要从企业版中获得。在应用中有三个命令,分别是 app-builder,app-deploy 和 war-builder,通过它们可以完成以下三部分功能:
将 common 中的内容同步到不同平台的代码上,这样生成的 apk 或者 ipa 才是修改后的内容,同时生成可以部署到 Worklight 服务器上的内容; 将同步后生成的内容部署到 Worklight 服务器上,其中 all 文件部属所有环境(包括 common); 将所有内容打包成一个 war 文件,用于部署到 WAS 服务器上;
在生成了部署到服务器的内容后,还需要生成不同平台的应用去安装到不同的操作系统上。在 android 环境下可以使用 android update project 来生成自动编译工程的 build 文件,然后通过其中的 release 命令和 jarsigner 命令完成应用的生成和签名活动,最后通过 zipalign 命令完成应用的压缩和整理工作。在 iOS 环境下需要使用 xcodebuild 和 xcrun 来生成 app 文件和 ipa 文件,最后通过企业级账号来发布相应的内容。详细的操作内容可以查看 Worklight 帮助文档,以及 Android和 iOS 的线上文档。
项目同步
当公共模块或者客户模块的内容被修改后,需要同步到原有的代码服务器上,这里需要根据文件的情况分成几种形式:当公共模块需要同步时,只要将 Worklight 项目中的内容放到 core 目录下各自的模块中,这时按照模块而不是按照文件类型来管理文件就显得很方便。这是因为按照模块自动化脚本只需要将内容放入到 core 文件夹就完成了任务,但是按照文件类型脚本需要不断的判断这个文件类型是属于哪个模块的,然后把内容放入到相应的模块中,这样增加了脚本的复杂度。
当客户模块需要同步时,步骤就比较复杂,因为在这样的应用中,需要判断内容是来自于原有的公共代码还是客户模块,甚至还需要考虑在一个文件中,哪部分内容来自于公共代码,哪部分来自于客户模块,所以要根据文件类型区别对待:
HTML 文件:先判断在客户模块中是否存在相应的内容,如果存在则将内容放入客户模块中,否则放入到公共模块中; IMAGE 文件:这类文件需要判断 web 部分和 native 部分,判断的流程也是先测试客户模块,如果存在就覆盖,不存在覆盖公共模块; JavaScript 文件:根据 Worklight 的规则,common 和不同环境的 JavaScript 会被放在一起,但是中间会有注释代码隔开。在本例中,自动脚本也使用类似的方法,将合成的 JavaScript 文件分为公共部分和客户部分,在同步时将不同的内容放入到各自的模块中; CSS 文件:CSS 文件的规则和 JavaScript 相同;
在确定了上述规则后,还需要做循环的测试,来保证各部分的完好。比如,当对奥迪的应用进行更新时修改了公共部分和奥迪部分的代码,在完成了项目同步后,还需要生成奔驰或者宝马的应用,查看相应的模块是否完好。
结论与展望
根据 Worklight 指定的文件架构,在管理多客户应用时碰到的问题,是架构本身造成的。因为根据项目架构的原则,应用是根据模块来管理其功能的,单单使用文件类型来管理功能容易造成代码的杂糅。所以本文提出了一种根据模块来管理开发手机应用代码的文件架构,这种架构可以通过自动化脚本完成公共部分和客户部分的组合,生成需要的具体 Worklight 项目;也能通过脚本将原有的内容分开,同步到各自的模块中,这就让开发者在管理面向不同客户的应用时更容易维护其代码。
不过为了更好的管理不同模块,减少内容修改的可能性,需要用到类似异步模块读取的技术,JavaScript 代码可以动态的决定需要读入的模块和相应的 CSS 内容,也包括 HTML 模块内容,这样才能更好的让模块之间松耦合,而在模块内聚合度高。在完成了这部分的内容后,相应的自动化脚本也需要做修改。