<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>绿色记忆 &#187; Cordova</title>
	<atom:link href="https://blog.gmem.cc/tag/cordova/feed" rel="self" type="application/rss+xml" />
	<link>https://blog.gmem.cc</link>
	<description></description>
	<lastBuildDate>Mon, 06 Apr 2026 12:46:48 +0000</lastBuildDate>
	<language>en-US</language>
		<sy:updatePeriod>hourly</sy:updatePeriod>
		<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.9.14</generator>
	<item>
		<title>Sencha Cmd学习笔记</title>
		<link>https://blog.gmem.cc/sencha-cmd-study-note</link>
		<comments>https://blog.gmem.cc/sencha-cmd-study-note#comments</comments>
		<pubDate>Tue, 29 Nov 2016 06:53:25 +0000</pubDate>
		<dc:creator><![CDATA[Alex]]></dc:creator>
				<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Cordova]]></category>
		<category><![CDATA[ExtJS]]></category>

		<guid isPermaLink="false">https://blog.gmem.cc/?p=13780</guid>
		<description><![CDATA[<p>简介 Sencha Cmd是一套跨平台的命令行工具，用于支持整个ExtJS开发的生命周期。它包含以下功能： 代码生成工具：可以生成整个应用程序，并且使用MVC模式扩展应用程序 JS编译器：此编译器理解Sencha的ExtJS/Touch框架，可以减少应用程序加载时间 Web服务器：一个轻量级的，开发用Web服务器 包管理系统：分布式的包管理系统，可以方便的集成其它用户开发的、发布到Sencha Package Repository的包（例如ExtJS主题）到当前应用程序 工作区管理：辅助跨越多个应用的框架、包、代码共享 构建脚本：自动生成应用的构建脚本，并且暴露before、after扩展点 支持与Cordova/Phone集成 图形捕获：为支持遗留浏览器，可以将CSS3特性转换为图片 优化工具：强大的代码选择工具，用于微调、确定哪些被包含在最终的构建中；确定跨越多个页面的公共代码并共享 灵活的配置系统 日志系统：详尽的日志，让你可以了解命令的内部工作步骤 命令 命令格式 [crayon-69d6b723071df685711969/] 常用选项 选项 说明 <a class="read-more" href="https://blog.gmem.cc/sencha-cmd-study-note">[...]</a></p>
<p>The post <a rel="nofollow" href="https://blog.gmem.cc/sencha-cmd-study-note">Sencha Cmd学习笔记</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></description>
				<content:encoded><![CDATA[<div class="wri_content_clear_both"><div class="blog_h1"><span class="graybg">简介</span></div>
<p>Sencha Cmd是一套跨平台的命令行工具，用于支持整个ExtJS开发的生命周期。它包含以下功能：</p>
<ol>
<li>代码生成工具：可以生成整个应用程序，并且使用MVC模式扩展应用程序</li>
<li>JS编译器：此编译器理解Sencha的ExtJS/Touch框架，可以减少应用程序加载时间</li>
<li>Web服务器：一个轻量级的，开发用Web服务器</li>
<li>包管理系统：分布式的包管理系统，可以方便的集成其它用户开发的、发布到Sencha Package Repository的包（例如ExtJS主题）到当前应用程序</li>
<li>工作区管理：辅助跨越多个应用的框架、包、代码共享</li>
<li>构建脚本：自动生成应用的构建脚本，并且暴露before、after扩展点</li>
<li>支持与Cordova/Phone集成</li>
<li>图形捕获：为支持遗留浏览器，可以将CSS3特性转换为图片</li>
<li>优化工具：强大的代码选择工具，用于微调、确定哪些被包含在最终的构建中；确定跨越多个页面的公共代码并共享</li>
<li>灵活的配置系统</li>
<li>日志系统：详尽的日志，让你可以了解命令的内部工作步骤</li>
</ol>
<div class="blog_h1"><span class="graybg">命令</span></div>
<div class="blog_h2"><span class="graybg">命令格式</span></div>
<pre class="crayon-plain-tag"># 一般命令调用格式
sencha [category] [command] [options...] [arguments...]
# 帮助
sencha help [module] [action]</pre>
<div class="blog_h2"><span class="graybg">常用选项</span></div>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">选项</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>--debug<br />--info<br />--quiet</td>
<td>设置调试日志的级别<br />--quiet  仅仅显示警告、错误</td>
</tr>
<tr>
<td>--strict</td>
<td>把警告作为错误看待</td>
</tr>
<tr>
<td>--plain</td>
<td>禁止调试日志中的高亮</td>
</tr>
<tr>
<td>--beta</td>
<td>启用Beta版本的包仓库</td>
</tr>
<tr>
<td>--cwd</td>
<td>设置命令执行的“当前目录”</td>
</tr>
<tr>
<td>--nologo</td>
<td>禁止输出版本信息</td>
</tr>
<tr>
<td>--sdk-path</td>
<td>对于非app类子命令，指定ExtJS SDK的目录</td>
</tr>
<tr>
<td>--time</td>
<td>显示命令的执行耗时</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">常用子命令</span></div>
<div class="blog_h3"><span class="graybg">app</span></div>
<p>这类子命令执行多种应用程序的构建处理：</p>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 15%; text-align: center;">命令</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>upgrade</td>
<td>自动生成的应用程序中，有两类文件与Cmd有关：构建脚本（<span style="color: #444444;">scaffold</span>）、ExtJS/Touch SDK。偶尔的情况下，你需要更新这些内容：<br />
<pre class="crayon-plain-tag"># 仅仅更新scaffold：包括.sencha子目录中的内容，以及app.js等文件
sencha app upgrade
# path-to-new-framework为可选，用于在更新scaffold的同时更新你使用的ExtJS的版本
sencha app upgrade [ path-to-new-framework ]</pre>
</td>
</tr>
<tr>
<td>init</td>
<td>初始化当前目录为一个Sencha Cmd应用程序，可以从空白目录开始：<br />
<pre class="crayon-plain-tag">sencha app init --ext=/path/to/extjs/ AppName --modern</pre>
</td>
</tr>
<tr>
<td>install</td>
<td>确保不完整的Sencha Cmd应用程序变得可以运行（补充缺失的文件）：<br />
<pre class="crayon-plain-tag">sencha app install --frameworks=/path/to/extjs</pre>
</td>
</tr>
<tr>
<td>build</td>
<td>构建应用程序：<br />
<pre class="crayon-plain-tag"># 基于开发环境构建
sencha app build development</pre>
</td>
</tr>
<tr>
<td>refresh</td>
<td>执行部分构建，仅仅构建脚本相关的部分</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">generate</span></div>
<p>生成模型、控制器或者其它组件，甚至整个应用程序：</p>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 15%; text-align: center;">命令</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>app</td>
<td>生成一个起步应用程序：<br />
<pre class="crayon-plain-tag">sencha generate app -ext -classic AppName ./AppPath
sencha -sdk /home/alex/JavaScript/extjs/6.2.0 generate app -modern AppName ./AppPath</pre>
</td>
</tr>
<tr>
<td>controller</td>
<td>为当前应用程序生成一个控制器：<br />
<pre class="crayon-plain-tag">sencha generate controller Central</pre>
</td>
</tr>
<tr>
<td>form</td>
<td>为当前应用生成一个表单，仅Touch</td>
</tr>
<tr>
<td>model</td>
<td>为当前应用生成一个模型：<br />
<pre class="crayon-plain-tag"># 在应用根目录执行
# 生成一个名为User的模型，包含3个字段
sencha generate model User id:int,name,email</pre>
</td>
</tr>
<tr>
<td>package</td>
<td>生成一个起步的本地包，存放到packages/local下：<br />
<pre class="crayon-plain-tag">sencha generate package pkgname</pre>
</td>
</tr>
<tr>
<td>profile</td>
<td>为当前应用程序生成一个配置，仅Touch</td>
</tr>
<tr>
<td>theme</td>
<td>为slice操作生成一个主题页面，仅ExtJS</td>
</tr>
<tr>
<td>view</td>
<td>为当前应用程序生成一个视图相关组件<br />
<pre class="crayon-plain-tag"># 在view/foo子目录下生成Thing视图，以及视图模型ThingModel、视图控制器ThingController
sencha generate view foo.Thing</pre>
</td>
</tr>
<tr>
<td>workspace</td>
<td>生成一个新的工作区</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">repository</span></div>
<p>这一类子命令用于管理包的远程仓库：</p>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 15%; text-align: center;">命令</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>list</td>
<td>列出可用的远程仓库</td>
</tr>
<tr>
<td>add</td>
<td>添加一个远程仓库：<br />
<pre class="crayon-plain-tag">sencha repository add sencha http://cdn.sencha.com/cmd/packages/</pre>
</td>
</tr>
<tr>
<td>remove</td>
<td>删除一个远程仓库</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">其它子命令</span></div>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 15%; text-align: center;">命令</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>upgrade</td>
<td>升级Sencha Cmd本身。示例：<br />
<pre class="crayon-plain-tag"># 检查新版本
sencha upgrade --check
# 执行升级
sencha upgrade
# 无人值守的升级
sencha upgrade --unattended
# 包含beta版本
sencha upgrade --check --beta </pre>
</td>
</tr>
</tbody>
</table>
<div class="blog_h1"><span class="graybg">与ExtJS6联用</span></div>
<div class="blog_h2"><span class="graybg">生成应用</span></div>
<p>可以使用下面的命令生成一个Universal应用的脚手架：</p>
<pre class="crayon-plain-tag">sencha -sdk /home/alex/JavaScript/extjs/6.2.0 generate app MyApp MyApp</pre><br />
<pre class="crayon-plain-tag">.
├── app                   # 当前应用有关文件的目录
│   ├── Application.js    # 当前应用的实现类，扩展Ext.app.Application
│   ├── model             # 视图模型、模型存放目录
│   ├── store             # 存储存放目录
│   └── view              # 视图控制器、控制器存放目录
├── app.js                # 应用入口点定义
├── app.json              # 应用描述符文件
├── bootstrap.js          # 自举文件，其中包含微加载器
├── build                 # 构建结果存放目录
│   ├── development       # 构建环境：development的构建结果
│   └── temp
├── build.xml
├── classic               # 针对classic toolkit的源代码
│   ├── sass
│   └── src               # 组件，主要是UI组件
├── ext                   # 复制来的ExtJS SDK完整目录
├── index.html            # 入口点网页
├── modern                # 针对modern  toolkit的源代码
│   ├── sass
│   └── src               # 组件，主要是UI组件
├── overrides             # 对组件的重写存放在此
├── packages              # 依赖的包存放在此
├── resources             # 资源文件存放在此
├── .sencha
│   ├── app
│   │   ├── defaults.properties   # 构建属性的默认值和说明
│   │   ├── development.defaults.properties # 针对构建环境development的默认构建属性
│   │   ├── development.properties # 针对构建环境development的构建属性
│   │   ├── ext.properties  # 针对ExtJS的构建属性
│   │   ├── sencha.cfg      # Sencha Cmd配置文件
└── workspace.json        # 工作区描述符文件</pre>
<div class="blog_h2"><span class="graybg">构建应用</span></div>
<p>执行下面的命令构建当前应用：</p>
<pre class="crayon-plain-tag">sencha app build</pre>
<p>该命令会构建你的HTML页面、JS代码和主题到build目录中。</p>
<div class="blog_h2"><span class="graybg">开发者模式</span></div>
<p>Sencha Cmd根据你的app.json，生成自举脚本（bootstrap）。自举脚本向动态类加载器（Ext.Loader以及微加载器）传递必要的信息，后者负责加载程序代码。自举脚本不会被你的JS代码的变动影响，因此你需要执行编辑 - 保存 - 重新载入 这样的工作流程。</p>
<p>要保证自举脚本与最新的源码文件匹配，可以基于以下两种途径之一：</p>
<ol>
<li>使用<pre class="crayon-plain-tag">sencha app watch</pre> ，该命令启动一个Web服务器，并且监控源码，在源码改变后立即执行一次开发环境构建（development build）。该方式支持Fashion的Live Update功能</li>
<li>手工构建并更新：<br />
<pre class="crayon-plain-tag"># 除了更新JS部分，还继续样式的编译，并生成CSS
sencha app build development
# 仅仅更新bootstrap与JS相关的部分
sencha app refresh</pre>
</li>
</ol>
<div class="blog_h1"><span class="graybg">Fashion</span></div>
<p>Cmd 6引入了此组件，用于支持快速开发ExtJS 6主题。联合使用app watch可以启用所谓实时更新（<span style="color: #444444;">Live Update</span>）的特性。</p>
<p>实时更新利用Fashion来编译Scss，并且将其生成的最新CSS注入到浏览器页面。Fashion基于JavaScript实现，不需要Ruby的支持。</p>
<p>Fashion扩展了Sass语言，添加了新的特性，以便<span style="color: #444444;">Sencha Inspector之类的工具可以可视化的查看、编辑主题、应用中定义的变量。</span></p>
<div class="blog_h2"><span class="graybg">启用实时更新</span></div>
<p>配置app.json以启用实时更新：</p>
<pre class="crayon-plain-tag">"development": {
    "tags": [
        "fashion"
    ]
},</pre>
<p>也可以不指定上述配置，直接修改URL来启用实时更新：<pre class="crayon-plain-tag">?platformTags=fashion:1</pre>  </p>
<p>启用实时更新时，加载到浏览器的是Sass而非生成的CSS，当Sass发生变化时，Fashion会立即编译之并更新页面上的CSS。</p>
<div class="blog_h2"><span class="graybg">动态变量</span></div>
<p>动态变量是Fashion对Sass的语言扩展。在Fashion中动态变量非常重要，动态变量类似于普通变量，但是其值被<pre class="crayon-plain-tag">dynamic()</pre> 标记包围。动态变量之间的交互方式与普通变量不同：</p>
<pre class="crayon-plain-tag">$bar: dynamic(blue);
$foo: dynamic($bar);  // 变量$foo依赖于$bar
$bar: dynamic(red);

@debug $foo;  // $foo的值为red而不是blue</pre>
<p>可以注意到，动态变量的值并非在逐行处理过程中确定的。对动态变量的处理分为赋值、估算两个阶段。</p>
<div class="blog_h3"><span class="graybg">赋值阶段</span></div>
<p>赋值阶段的处理类似于普通变量，按照级联的顺序赋值。对动态变量的赋值只能在文件范围（位于任何控制块的外面）进行：</p>
<pre class="crayon-plain-tag">$bar: dynamic(blue);

@if something {
    $bar: dynamic(red); // 非法
}

$bar: dynamic(if(something, red, blue)); // 合法</pre>
<p>这一限制是动态变量估算、提升行为（<span style="color: #444444;">hoisting behaviors</span>）所需。</p>
<p>在声明之后，再次对动态变量赋值，必须要附带<span style="color: #3b2c48;">dynamic()：</span></p>
<pre class="crayon-plain-tag">$bar: dynamic(blue);
$bar: red;  // bar被赋值为red
$bar: green !default;  // 重新赋值为green
@debug $bar;   // green</pre>
<div class="blog_h3"><span class="graybg">估算阶段</span></div>
<p>在该阶段，根据依赖顺序（而不是声明顺序）确定动态变量的实际值，例如：</p>
<pre class="crayon-plain-tag">$bar: dynamic(mix($colorA, $colorB, 20%));
$bar: dynamic(lighten($colorC, 20%));</pre>
<p>由于对$bar进行了再次赋值，$bar依赖于$colorC，因此仅当$colorC的值被估算出来后，$bar的值即可确定，至于$colorA、$colorB何时估算，与$bar无关。</p>
<div class="blog_h3"><span class="graybg">Hoisting</span></div>
<p>为了实现估算阶段的逻辑，Fashion收集所有动态变量，<span style="background-color: #c0c0c0;">在执行任何Sass代码之前统一的估算它们</span>。这意味着类似于JavaScript变量，Fashion动态变量也是被提升到作用域最前面的。</p>
<div class="blog_h3"><span class="graybg">Elevation</span></div>
<p>当一个变量用于给动态变量赋值，其本身也被升级为动态变量：</p>
<pre class="crayon-plain-tag">$foo: blue;   // 尽管$foo作为普通变量声明，但是由于动态变量$bar依赖于它，因此$foo被升级为动态变量
$bar: dynamic($foo);</pre>
<div class="blog_h2"><span class="graybg">扩展Fashion</span></div>
<p>你可以通过编写JS代码来扩展Fashion，要在Sass中包含这些扩展代码，可以：</p>
<pre class="crayon-plain-tag">require("my-module");
require("../path/file.js");</pre>
<p>在内部，Fashion使用ES6的System.import加载标准的JS模块。</p>
<p>一个扩展的代码可以如下：</p>
<pre class="crayon-plain-tag">// ES6以前的版本
exports.init = function(runtime) {
    runtime.register({
        magic: function (first, second) {
        }
    });
};
// ES6
module foo {
    export function init (runtime) {
        runtime.register({
            magic: function (first, second) {
            }
        });
    }
} </pre>
<div class="blog_h1"><span class="graybg">编译器友好代码指南</span></div>
<p>编译器是Sencha Cmd的主要组件。此编译器并不类似于：</p>
<ol>
<li>YUI压缩器</li>
<li>Google Closure编译器</li>
<li>UglifyJS</li>
</ol>
<p>上述工具解决JS开发者面临的不同问题，但是它们不能理解Sencha框架，例如Ext.define用于定义类。</p>
<div class="blog_h2"><span class="graybg">框架知晓</span></div>
<p>编译器用于提供框架相关的优化、诊断，一旦代码通过了此编译器，其它一般性的工具可以继续处理它。这类优化可以极大的改善ExtJS的加载时间，特别是针对遗留浏览器。</p>
<p>为了编译器能够最优化的操作， 你必须遵循一系列的编码约定，这样编译器才能理解你的代码，进而执行优化操作。</p>
<div class="blog_h2"><span class="graybg">代码组织</span></div>
<p>Cmd生成的动态加载器，以及之前的JSBuilder，都假设代码按照一定的规则组织。你应该保证：</p>
<ol>
<li>每个JavaScript源文件应该在全局作用域包含一个Ext.define 语句</li>
<li>源文件的文件Base名必须和ExtJS类的Simple name匹配</li>
<li>ExtJS类的命名空间应当映射为源码子目录</li>
<li>为了让编译器能够自由的在类级别选择代码，应当确保一个类对应一个文件</li>
</ol>
<p>第2、3两条类似于Java的包与文件系统的映射规则。</p>
<div class="blog_h2"><span class="graybg">类声明</span></div>
<p>编译器能够识别Ext.define的以下关键字（keywords）并进行优化处理：requires、uses、extend、mixins、statics、alias、singleton、override、alternateClassName、xtype</p>
<p>除了Ext.define最常用的调用方法——第二个参数传递简单对象以外，编译器也能够识别以下几种调用变体：</p>
<pre class="crayon-plain-tag">// 指定第二个参数为函数
Ext.define('Foo.bar.Thing', function (Thing) {
    return {
        // 这里声明关键字
        extend: '...',
    };
});
// 指定第二个参数为函数调用
Ext.define('Foo.bar.Thing', function () {

    return {
        // 这里声明关键字
        extend: '...'
    };
}());</pre>
<div class="blog_h2"><span class="graybg">重写（Overrides）</span></div>
<p>从ExtJS 4.1/Touch 2.0开始，Ext.define能够管理重写。在历史版本中，重写用于打补丁以解决BUG或者增强功能。在引入动态加载器后，情况变得复杂，因为<pre class="crayon-plain-tag">Ext.override</pre> 的执行需要时间。在大量使用重写的应用程序中，并非所有重写都被所有页面或者build使用。</p>
<p>现在，编译器能够理解重写，以及它们对依赖、加载顺序的影响。在未来的版本中，编译器在消除dead code（被override替换）时会更加激进。遵循本节的规则，你可以在未来版本获得益处。</p>
<p>标准的重写代码范例如下：</p>
<pre class="crayon-plain-tag">// 尽管这里选定的名字空间是任意的，但是最好遵循下文说明的规则
Ext.define('MyApp.patches.grid.Panel', {
    override: 'Ext.grid.Panel',
    ...
});</pre>
<p>通过重写来<span style="background-color: #c0c0c0;">打补丁</span>是最常见的应用。你应当把所有<span style="background-color: #c0c0c0;">补丁的命名空间与它针对的目标类的命名空间对应</span>，例如MyApp.patches对应Ext。</p>
<p>除了打补丁，重写还可以用做<span style="background-color: #c0c0c0;">不完整类</span>（Partial Classes）。当你使用Sencha Architect之类的代码生成器时，一个类通常分类两个部分：机器生成部分、人工编写部分，这种划分有助于机器部分的重新生成。不完整类的用法示例：</p>
<pre class="crayon-plain-tag">// 自动生成的类
Ext.define( 'Foo.bar.Thing', {
    // NOTE: This class is generated - DO NOT EDIT...
    requires: [
        'Foo.bar.custom.Thing'
    ],
    method: function () {
    },
} );
// 重写，作为部分类
Ext.define( 'Foo.bar.custom.Thing', {
    override: 'Foo.bar.Thing',
    method: function () {
        this.callParent(); // 调用自动生成的方法
    },
} );</pre>
<p>重写的最后一种用法是<span style="background-color: #c0c0c0;">作为切面（Aspect）</span> 。OOP会导致的一个问题就是过度肥胖的基类，这是因为它包含过多的公共逻辑。使用override，可以分离出其中的逻辑，而仅仅在需要使用的时候，才require之：</p>
<pre class="crayon-plain-tag">// 作为切面的重写
Ext.define('Foo.feature.Component', {
    override: 'Ext.Component'
});
// 作为切面的重写，与Foo.feature.Component提供一个产品族
Ext.define('Foo.feature.grid.Panel', {
    override: 'Ext.grid.Panel',

    requires: [
        'Foo.feature.Component' // 由于重写不产生继承关系，这里必须显式require
    ]
});


// 需要使用上述切面的客户代码：

requires: [
    'Foo.feature.grid.Panel'
]
// 或者
requires: [
    'Foo.feature.*'
]</pre>
<p>注意：在重写时，你可以调用<pre class="crayon-plain-tag">callSuper()</pre> 来绕过对被重写方法的调用，而调用其父版本。</p>
<div class="blog_h1"><span class="graybg">资源文件管理</span></div>
<p>除了JavaScript、CSS、HTML以外，Web应用程序往往包含很多代码无关的资源文件（也叫资产，Asset）——图片、视频、数据文件。Cmd将这些资产分类并提供多种管理它们的选项。</p>
<div class="blog_h2"><span class="graybg">资源的来源</span></div>
<p>资源文件一般都放在名为resources的目录下，这些目录可能来自：</p>
<ol>
<li>对于单Toolkit应用，位于应用根目录，例如MyApp/resources</li>
<li>对于Universal应用，位于应用根目录，以及各Toolkit下，例如MyApp/resources、MyApp/classic/resources</li>
<li>主题附带的资源文件，位于主题的根目录，例如theme-neptune/resources</li>
<li>软件包附带的资源文件，位于软件包的根目录</li>
</ol>
<p>你可以应用程序描述符app.json中的resources数组，用来定制应用程序资源的来源：</p>
<pre class="crayon-plain-tag">"resources" [{
    "path": "resources",   // 改变此配置以便在其它目录中存放资源
}]</pre>
<p>对于单Toolkit应用，仅该数组的第一个元素作为实际的资源路径，其它元素、第一个元素的output设置都被忽略。 </p>
<p>对于Universal应用，该数组可以指定多个元素，并且使用output来指定使用哪个资源池：</p>
<pre class="crayon-plain-tag">"resources": [{
    "path": "resources",
    "output": "shared"   # 使用shared资源池
}, {
    "path": "${toolkit.name}/resources"   # 使用默认资源池
}],</pre>
<p>这样，可以被多个Toolkit共享的资源，就可以放在（默认）应用根目录的resources目录下，而Toolkit独有的资源则放在与Toolkit同名的目录的resources子目录下。</p>
<p>对于包，支持像Universal应用那样声明多个资源目录、使用哪个资源池。</p>
<div class="blog_h2"><span class="graybg">资源的输出</span></div>
<p>app.json的顶级output元素， 声明在构建后的应用程序文件被输出到何处：</p>
<pre class="crayon-plain-tag">"output": {
    "base": "${workspace.build.dir}/${build.environment}/${app.name}",
    // 例如 ./workspace/build/production/MyApp
},</pre>
<p>对于Universal应用，可以定义资源池（<span style="color: #444444;">resource pools</span>）：</p>
<pre class="crayon-plain-tag">"output": {
    "base": "${workspace.build.dir}/${build.environment}/${app.name}",
    // 资源池定义
    "resources": {
        // 默认资源池，对应buildId下的resources目录
        "path": "${build.id}/resources",
        // 名为shared的共享资源池，对应应用程序的根目录下的resources子目录
        "shared": "resources"
    }

},</pre>
<p>应用、主题、包中的资源文件，都输出到某个资源池中：</p>
<ol>
<li>单Toolkit应用、主题中的资源文件，输出到默认资源池</li>
<li>Universal应用，默认的，应用根目录下resources输出到共享资源池；toolkit.name/resources输出到默认资源池</li>
<li>包的资源输出到资源池的package.name子目录下。这是应用程序构建为包提供的沙盒</li>
</ol>
<div class="blog_h2"><span class="graybg">资源的覆盖</span></div>
<p>多个不同来源的资源文件，可能存在路径名相同的情况，此时会按照一定的规则覆盖：</p>
<ol>
<li>由于主题支持继承，因此子主题中的同名资源，自动覆盖父主题中的资源</li>
<li>在应用的resources目录中（如果是Universal应用，则必须是toolkit.name/resources目录中）的文件，可以覆盖同路径的来自主题、包的资源文件</li>
</ol>
<div class="blog_h2"><span class="graybg">package.json配置</span></div>
<p>与应用类似，包也具有自己的描述符package.json：</p>
<pre class="crayon-plain-tag">// 构建输出
"output": "${package.dir}/build",
// 资源路径
"resources": [{
    "path": "resources"
}]</pre>
<p>为了让非Cmd应用使用包，可以单独构建之：<pre class="crayon-plain-tag">sencha package build</pre>  。注意包的构建不区分development/production，其build目录中的东西可以被script、link等元素引用。</p>
<div class="blog_h2"><span class="graybg">包资源与沙盒</span></div>
<p>为了防止不同包中同名资源被意外的覆盖，应用程序构建（App build）为包中的资源提供沙盒，即，为每个包的资源提供resources的子目录，子目录以包名为名称。</p>
<p>由于应用程序资源、包资源路径处理方式的不同，因此在CSS中引用时，需要注意。Cmd提供了API来获取资源的正确路径：</p>
<pre class="crayon-plain-tag">.arrow-button {
    // 不使用资源池，或者使用默认资源池时：
    background-image: url(get-resource-path('images/arrow.png'));
    // 使用资源池时：
    background: url(get-resource-path('images/foo.png', $pool: 'shared'));
}</pre>
<p>在JavaScript中，也有类似的API：</p>
<pre class="crayon-plain-tag">image.setSrc(
    // 第二个参数为资源池的名称
    Ext.getResourcePath('images/arrow.png', null, 'arrow-button') 
); </pre>
<div class="blog_h1"><span class="graybg">包管理</span></div>
<p>Sencha Cmd应用的packages目录包含local、remote两个子目录，分别存放本地、远程包。</p>
<p>你可以在app.json中使用requires声明对包的依赖。sencha app build、sencha app refresh命令会自动把包集成到应用程序中。</p>
<div class="blog_h2"><span class="graybg">本地包</span></div>
<p>执行下面的命令可以生成一个本地包：</p>
<pre class="crayon-plain-tag">sencha generate package common</pre>
<p>此包的内容放置在packages/local/common目录中。此包的packages.json中会自动添加<pre class="crayon-plain-tag">local: true</pre>  配置，该配置可以放置Cmd下载远程包并覆盖此本地包。</p>
<div class="blog_h2"><span class="graybg">远程包</span></div>
<p>远程包的管理是基于包仓库（package repositories）进行的，Cmd会自动生成一个本地的包仓库，用于缓存、发布包。</p>
<p>执行下面的命令，可以查看可用的远程仓库列表：<pre class="crayon-plain-tag">sencha repository list</pre> ，你可以使用<pre class="crayon-plain-tag">sencha repository add</pre> 、<pre class="crayon-plain-tag">sencha repository remove</pre> 命令来添加、删除远程仓库。</p>
<div class="blog_h2"><span class="graybg">包命名规范</span></div>
<p>sencha-*、ext-*、touch-*、cmd-*这些前缀的包名被内部使用。</p>
<div class="blog_h2"><span class="graybg">版本管理</span></div>
<p>可以指定当前包的版本号，以及它和什么样的历史版本兼容：</p>
<pre class="crayon-plain-tag">{
    // 当前版本
    "version": "n.n.n",
    // 兼容的最低版本
    "compatVersion": "2.4.2",
}</pre>
<p>在应用描述符的requires里，可以附加版本要求：<pre class="crayon-plain-tag">"requires": [ "ext-easy-button@1.0" ]</pre>  。版本要求语法：</p>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">示例</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>-1.2</td>
<td>最高1.2</td>
</tr>
<tr>
<td>1.0-<br />1.0+</td>
<td>最低1.0</td>
</tr>
<tr>
<td>1.0-1.2</td>
<td>版本在1.0到1.2之间</td>
</tr>
<tr>
<td>1.0-1.2?</td>
<td>版本在1.0到1.2之间，或者1.0-1.2的兼容版本</td>
</tr>
</tbody>
</table>
<div class="blog_h1"><span class="graybg">工作区管理</span></div>
<p>Cmd引入工作区的概念，以便管理多个需要共享框架、代码、样式、资源的应用程序。</p>
<div class="blog_h2"><span class="graybg">什么是工作区</span></div>
<p>构建一个大型应用程序的步骤与构建一个单页应用程序的起始步骤一样。一旦应用程序扩展到多个页面，以下常见问题将会出现：</p>
<ol>
<li>如何共享的使用同一框架</li>
<li>如果跨页面共享代码</li>
<li>如果共享第三方包</li>
</ol>
<p>为了解决这些问题，Cmd引入了工作区的概念。一个工作区是包含了多个<span style="background-color: #c0c0c0;">页面（在Sencha的术语里叫应用，Application，因为应用一般都是单页面的，一个应用只进行一次Ext.application调用）</span>、框架（不同版本）、包以及其它共享代码/文件的目录。工作区的根目录通常会纳入版本控制。</p>
<p>如果在工作区内组织各页面的目录并不重要，但是通常把它们的目录作为工作区根目录的直接子目录。</p>
<div class="blog_h2"><span class="graybg">创建工作区</span></div>
<p>执行下面的命令可以创建一个工作区：</p>
<pre class="crayon-plain-tag">sencha generate workspace /path/to/workspace

# 生成的目录结构如下：
.
├── packages
├── .sencha               # Sencha Cmd相关文件
│   ├── .cvsignore
│   ├── .gitignore
│   └── workspace         # 工作区相关文件
│   ├── plugin.xml
│   └── sencha.cfg        # Sencha Cmd配置
└── workspace.json        # 工作区描述符</pre>
<div class="blog_h1"><span class="graybg">微加载器</span></div>
<p>所谓微加载器（<span style="color: #444444;">Microloader</span>）是Sencha的数据驱动的JavaScript/CSS动态加载器。微加载器由Cmd提供，并作为生成的应用程序的一部分。</p>
<p>ExtJS 6的微加载器实现与ExtJS 5或者Touch不同，它增强了一些功能，这些功能可以在app.json中配置。执行<pre class="crayon-plain-tag">sencha app upgrade</pre> 时微加载器会被一同升级。</p>
<div class="blog_h2"><span class="graybg">加载Manifest</span></div>
<p>Cmd会把app.json转换为manifest供微加载器在运行时使用。你也可以在运行时调用<pre class="crayon-plain-tag">Ext.manifest</pre> 获得此manifest的引用。ExtJS6本身利用此manifest完成一些工作，例如兼容性层（Compatibility Layer）。</p>
<p>有三种方式来指定微加载器使用什么Manifest：</p>
<ol>
<li>嵌入式Manifest，配置：<br />
<pre class="crayon-plain-tag">"output": {
    "manifest": {
        "embed": true
    }
}</pre></p>
<p> 可以在构建期间把Manifest嵌入到bootstrap.js文件中</p>
</li>
<li>命名Manifest，你可以指定从哪个文件中加载Manifest，默认app.json：<br />
<pre class="crayon-plain-tag">&lt;script type="text/javascript"&gt;
    var Ext = Ext || {};
    Ext.manifest = 'foo';  // 从./foo.json加载
&lt;/script&gt;
&lt;script id="microloader" data-app="f72c0f68" type="text/javascript" src="bootstrap.js"&gt;&lt;/script&gt;</pre>
</li>
<li>动态指定Manifest，你可以动态的设置Ext.manifest为任意字符串为应用描述符文件的basename</li>
</ol>
<div class="blog_h2"><span class="graybg">Manifest的生成</span></div>
<p>微加载器负责解析app.json并生成manifest，大概步骤如下：</p>
<ol>
<li>读取app.json，其中内容被后续步骤解析</li>
<li>获取app.json中与当前构建环境（production/testing/development）匹配的设置，作为基础manifest</li>
<li>获取app.json中与当前构建配置匹配（builds.***）的设置，覆盖上面的manifest</li>
<li>读取app.json中与当前Toolkit匹配（classic/modern）的设置，覆盖上面的manifest</li>
<li>读取app.json中与当前packager匹配的设置，覆盖上面的manifest</li>
<li>开始处理依赖包，如果package.json中声明了js、css，则它们按照包的依赖顺序，插入上面manifest['js']或者manifest['css']数组的最前面。package.json中其它内容被置入manifest的packages属性中</li>
</ol>
<div class="blog_h2"><span class="graybg">引用微加载器</span></div>
<p>Cmd生成的脚手架代码中已经引用的微加载器：</p>
<pre class="crayon-plain-tag">&lt;script id="microloader" data-app="12345" type="text/javascript" src="bootstrap.js"&gt;&lt;/script&gt;</pre>
<p>其中data-app为应用的UUID，该ID会在LocalStorage中作为Key使用，避免多个页面的数据混乱在一起。</p>
<div class="blog_h1"><span class="graybg">应用描述符</span></div>
<p>由Cmd自动生成的app.json叫做应用描述符，该描述符会被微加载器使用，并且在运行时暴露为 Ext.manifest。你可以定制其中的很多配置项，以改变应用的行为。下表列出最常用的配置项：</p>
<table class=" fixed-word-wrap full-width">
<tbody>
<tr>
<td><strong>indexHtmlPath</strong> 应用的HTML文档文件路径，此路径相对于app.json文件。默认值是index.html，可以根据需要修改。修改后应该同时指定output配置</td>
</tr>
<tr>
<td><strong>framework</strong> 使用什么框架，可选值ext、touch，或者工作区自定义的框架名</td>
</tr>
<tr>
<td><strong>theme</strong> 对于ExtJS应用，此配置用于指定主题名。例如<pre class="crayon-plain-tag">"theme": "ext-theme-crisp"</pre> </td>
</tr>
<tr>
<td><strong>classpath  </strong>指定源码目录。例如：<br />
<pre class="crayon-plain-tag">"classpath": [
    "app",
    "${toolkit.name}/src"
] </pre>
</td>
</tr>
<tr>
<td>
<p><strong>js</strong></p>
<p>一个数组，指定需要被微加载器自动加载的JavaScript文件，默认脚手架生成如下内容：</p>
<pre class="crayon-plain-tag">"js": [{
    "path": "app.js",
    "bundle": true
}]</pre>
<p>上述配置实际上指定了应用程序的入口点。bundle=true意味着此配置条目被构建出的concatenated classes代替。 你可以继续增加配置：</p>
<pre class="crayon-plain-tag">"js": [{
    "path": "library.js",
    "includeInBundle": true // 包含在concatenated classes中，如果false，则此文件在构建结果中存在，由微加载器独立加载
},{
    "path": "app.js",
    "bundle": true  // 只能有一个元素定义此属性，设置为true则此文件作为concatenated classes容器
}]</pre>
<p>所有JavaScript文件必须按照其<span style="background-color: #c0c0c0;">执行顺序</span>在此声明</p>
<p>对于应用程序的依赖，应该在requires配置中声明，而不是此配置 </p>
</td>
</tr>
<tr>
<td>
<p><strong>css</strong></p>
<p>一个数组，指定微加载器需要按顺序加载的CSS文件。默认内容：</p>
<pre class="crayon-plain-tag">"css": [
    {
        "path": "${build.out.css.path}",
        "bundle": true,
        "exclude": [
            "fashion"
        ]
    }
],</pre>
<p>此默认值仅仅是一个导入sass目录构建结果的存根 </p>
</td>
</tr>
<tr>
<td><strong>requires</strong> 此数组引用应用所依赖的包的名称，当Cmd处理此数组时，会自动下载、抽取缺失的软件包到工作区中。包可以在自己的package.json中声明依赖，这样的传导依赖也会被下载、抽取。你可以同时指定包的版本</td>
</tr>
<tr>
<td>
<p><strong>output</strong> </p>
<p>控制构建的如何输出、在哪里输出。此配置可以控制构建输出的多个方面。例如，修改indexHtmlPath为../page.jsp后，你需要继续设置output：</p>
<pre class="crayon-plain-tag">"output": {
    "page": {
        "path": "../page.jsp",
        "enable": false // 仅仅说明页面URL是哪个，但是不赋值源文件到输出
    }
}</pre>
<p>你可以指定appCache属性，来控制哪些资产可以被浏览器缓存供离线使用：</p>
<pre class="crayon-plain-tag">"output": {
    "appCache": {
        // 启用缓存
        "enable": true
    },
    "appCache": {
        // 哪些使用缓存
        "cache": [
            "index.html"
        ],
        // 哪些必须联网获取
        "network": [
            "*"
        ],
        // 哪些在联网获取失败时使用缓存
        "fallback": []
    }
},</pre>
</td>
</tr>
<tr>
<td>
<p><strong>LocalStorage缓存</strong>
<p>除了浏览器自带的应用程序缓存（通过appCache配置），微加载器还支持通过LocalStorage缓存每一个资产文件。在尝试进行任何远程获取之前，微加载器会首先使用UUID查询LocalStorage，如果文件没有变化则直接使用缓存，这可以让应用程序的加载变得非常块。微加载器还支持增量补丁——即仅仅资产、CSS、JS变化的比特通过网络下载，然后合并到LocalStorage中。</p>
<p>要启用LocalStorage缓存，你需要逐个设置资产的update属性，取值full表示全量更新，delta表示增量更新：</p>
<pre class="crayon-plain-tag">"js": [
    {
        "path": "app.js",
        "bundle": true,
        "update": "delta"
    }
],

"css": [
    {
        "path": "app.css",
        "update": "full"
    }
]</pre>
<p>除了在资产上设置update，要让缓存生效，还必须声明全局的cache配置：</p>
<pre class="crayon-plain-tag">"cache": {
    "enable": true
}</pre>
<p>在开发阶段，常常把全局缓存设置为false，仅仅在产品构建时才设置为true </p>
<p>你还可以设置：<pre class="crayon-plain-tag">"cache": { "deltas": true }</pre>  这样所有update=delta资产的变化会出现在构建结果的detas目录中，如果设置deltas为字符串，则此字符串对应的目录代替deltas目录的功能</p>
<p>一旦微加载器检测到Application Cache、LocalStorage缓存的内容存在更新，会立即发布全局事件，你可以监听此事件并作出反应：</p>
<pre class="crayon-plain-tag">Ext.application( {
    name: 'MyApp',
    mainView: 'MyMainView',
    // 监听缓存更新事件
    onAppUpdate: function () {
        Ext.Msg.confirm( '应用程序已更新',
            function ( choice ) {
                if ( choice === 'yes' ) {
                    window.location.reload();
                }
            }
        );
    }
} );</pre>
</td>
</tr>
<tr>
<td>
<p><strong>builds</strong>
<p>此配置用于声明一个或者多个构建配置（Build Profiles）</p>
<p>如果应用程序存在多个变体，你就可以添加一个新的构建配置。例如：</p>
<pre class="crayon-plain-tag">"builds": {
    "classic": {
        "theme": "ext-theme-classic"
    },
    "gray": {
        "theme": "ext-theme-gray"
    },
    "access": {
        "theme": "ext-theme-access"
    },
    "
}</pre>
<p>builds中的每一个属性称为一个构建配置，此属性的值会在执行使用此配置构建时，覆盖app.json中的配置项以生成最终有有效的manifest </p>
</td>
</tr>
<tr>
<td>
<p><strong>production  /  testing   /  development</strong></p>
<p>对应不同的构建环境（Build Environment）的特定配置信息</p>
</td>
</tr>
<tr>
<td>
<p><strong>classic  /  modern</strong></p>
<p>对应不同的Toolkit的特定配置信息</p>
</td>
</tr>
<tr>
<td>
<p><strong>locales</strong></p>
<p>指定支持的区域配置：<pre class="crayon-plain-tag">"locales": [ "en", "he" ]</pre> 。在同时使用builds时，最终的构建配置的名称不再是classic，而是classic-en、classic-he等</p>
</td>
</tr>
<tr>
<td>
<p><b>tags  </b></p>
<p>你可以指定多个标记：</p>
<pre class="crayon-plain-tag">"tags": ["ios", "phone", "fashion"]
// 或者
"tags": {
    "ios": true,
    "phone": true,
    "desktop": false,
    "fashion": true
}</pre>
<p>这些标记被检测到后，将覆盖自动检测到的值</p>
</td>
</tr>
</tbody>
</table>
<div class="blog_h1"><span class="graybg">集成Cordova/PhoneGap</span></div>
<p>PhoneGap基于Cordova构建，并提供一些辅助工具，例如远程构建服务。</p>
<p>基于Sencha Cmd构建时，仅能构建DEBUG版本的Cordova/PhoneGap应用，要构建能够在应用商店下载的APP你需要使用Android Studio/XCode。</p>
<div class="blog_h2"><span class="graybg">创建支持Cordova/PhoneGap的应用</span></div>
<p>首先，按照一般性步骤创建Cmd应用：</p>
<pre class="crayon-plain-tag">mkdir MyApp &amp;&amp; cd MyApp
sencha app init --ext=/home/alex/JavaScript/extjs/6.2.0 MyApp --universal</pre>
<p>当开发ExtJS 6 Universal应用时，则上述生成的应用结构的app.json中已经包含了builds块，这种情况下sencha cordova/phonegap子命令无法修改此块，因此你需要手工的修改：</p>
<pre class="crayon-plain-tag">"builds": {
    "classic": {},
    "modern": {
        "toolkit": "modern",
        "theme": "theme-triton",
        // 如果使用Cordova，可以添加：
        "cordova": {
            "config": {
                "platforms": "ios",
                "id": "cc.gmem.myappid"
            }
        },
        // 如果使用PhoneGap，可以添加：
        "packager": "phonegap",
        "phonegap": {
            "config": {
                "platform": "ios",
                "id": "cc.gmem.myappid"
            }
        }
    }
},</pre>
<p>然后进入工程目录，初始化Cordova/PhoneGap支持：</p>
<pre class="crayon-plain-tag">sencha phonegap init cc.gmem.myappid  MyApp
# 或者
sencha cordova init cc.gmem.myappid MyApp</pre>
<p>当开发ExtJS Modern应用程序时，上述命令会自动生成如下builds块：</p>
<pre class="crayon-plain-tag">// native仅仅是一个构建配置名（build-name），你可以将其修改为任意值
"native": {
    "packager": "cordova",
    "cordova": {
        "config": {
            "platforms": "ios android",  // 需要支持的移动平台
            "id": "cc.gmem.myappid2",    // 用于唯一识别应用程序的标识符
            "name": "MyApp2"             // 应用程序名称
        }
    }
}</pre>
<p>上述命令执行完毕后，在工程目录会出现cordova或者phonegap子目录。 这些子目录的内容与普通Cordova/PhoneGap工程一致。</p>
<p>下面的Cmd子命令可以用于构建出Native软件包： </p>
<pre class="crayon-plain-tag"># 首先构建Sencha应用程序，然后构建Native应用
sencha app build {build-name}
# 首先构建，然后尝试在连接上来的设备上运行程序
sencha app run {build-name}
# 首先构建，然后尝试在模拟器上运行程序
sencha app emulate {build-name}
# 构建Sencha应用程序，然后拷贝到Cordova/PhoneGap指定目录，准备后续的Native构建
sencha app prepare {build-name}</pre>
<div class="blog_h2"><span class="graybg">开发PhoneGap远程应用</span></div>
<p>使用PhoneGap远程构建服务，可以避免很多麻烦的问题，例如下载SDK、工具，购买Mac电脑。你仅仅需要把自己的Web应用上传到PhoneGap服务器，Adobe就可以自动的把Native应用生成给你。在开始之前，你需要到PhoneGap网站申请一个免费账号。如果要构建iOS应用，你需要在PhoneGap网站上填写一系列的credentials信息。</p>
<p>要启用远程构建，需要配置app.json：</p>
<pre class="crayon-plain-tag">"builds": {
    "native": {
        "packager": "phonegap",
        "phonegap" : {
            "config": {
                "platform": "ios",
                "remote": true,  // 远程构建
                "id": "com.mydomain.MyApp"
            }
        }
    }
}</pre>
<p>为了向Cmd提供你的PhoneGap网站账号信息，你需要在应用根目录创建一个local.properties文件：</p>
<pre class="crayon-plain-tag">phonegap.username=name@me.com
phonegap.password=passwd</pre>
<p>远程构建完毕后，你可以到PhoneGap门户去下载安装包，或者将其发布到应用商店。 </p>
<div class="blog_h1"><span class="graybg">新特性列表</span></div>
<div class="blog_h2"><span class="graybg">6</span></div>
<table class=" full-width fixed-word-wrap">
<tbody>
<tr>
<td>
<p><strong>新的安装器</strong></p>
<p>此版本的安装程序自带了运行Cmd所需的JRE，并且移除了对Ruby的依赖（由于引入了Fasion）。新的安装器不再需要root权限</p>
<p>如果你使用老的框架版本，例如ExtJS 4/5、Touch2。你还是需要Ruby以及Compass包以便编译Sass代码</p>
</td>
</tr>
<tr>
<td>
<p><strong>Fashion</strong></p>
<p>Fashion是Sencha新开发的，用于其扩展的Sass语言的编译器。Cmd调用Fashion为ExtJS应用程序、主题构建Sass。当使用sencha app watch时，你可以获得实时更新（Live Update）—— 应用程序的CSS自动与最新代码同步，而不需要刷新浏览器</p>
<p>由于引入了Fashion，Cmd不再依赖于Ruby来生成主题</p>
</td>
</tr>
<tr>
<td>
<p><strong>工作区改进</strong></p>
<p>为了支持基于包的代码共享，Cmd6将不同的包分离到子目录中（原先一律放在packages目录）：</p>
<ol>
<li>packages/local：自己在本地生成的包</li>
<li>packages/remote：下载/抽取（extrace）的包。workspace.json中的packages.extract可以修改此默认位置</li>
</ol>
<p>remote目录可以安全的从CVS中忽略</p>
<p>从Cmd6开始，工作区的根目录存在一个workspace.json文件，此文件应该在CVS中存储</p>
</td>
</tr>
<tr>
<td>
<p><strong>Microloader</strong></p>
<p>微加载器是首先由Touch框架引入的，并且在Cmd 5中开始支持ExtJS 5应用程序</p>
<p>Cmd 6对微加载器进行了改进，添加了基于localStorage的缓存支持，并且可以在app.json中禁用缓存</p>
</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">6.1</span></div>
<table class=" fixed-word-wrap full-width">
<tbody>
<tr>
<td>
<p><strong>框架与工作区管理</strong></p>
<p>当项目规模增大后，在一个工作区中同时开发多个应用程序，并且使用多个ExtJS版本的可能性增加。在6.1之前，一个工作区仅仅可以使用单个版本的ExtJS，则要求所有应用程序必须升级使用的ExtJS版本。</p>
<p>现在，你可以在workspace.json中声明多个ExtJS版本：</p>
<pre class="crayon-plain-tag">"frameworks": {
    "ext62": "ext",
    "ext602": "ext-6.0.2"
}</pre>
<p>frameworks的每一个子项，其值对应工作区中的一个子目录，其键可以供应用程序的app.json引用：</p>
<pre class="crayon-plain-tag">"framework": "ext62"</pre>
<p>你可以随时的添加新的框架版本到工作区：</p>
<pre class="crayon-plain-tag"># 自动生成frameworks子项，其键根据目录中ExtJS的版本号推导出，例如6.2.0 版本的键为ext62，6.0.2版本的键为ext602
sencha framework add /path/to/ext
# 手工指定键值  path为拷贝到工作区的哪个子目录，key为键，必须以ext或者touch开头
sencha framework add -key extFiveO -source /path/to/ext-5.0.0 -path ext50</pre>
<p>添加完新框架后， 框架的代码会自动拷贝到工作区中。你可以使用新添加的框架，来创建一个应用程序：</p>
<pre class="crayon-plain-tag">sencha generate app -extFiveO AppName path/to/app</pre>
<p>执行下面的命令可以删除一个框架版本：</p>
<pre class="crayon-plain-tag"># --force 即使有应用程序在使用，也强制删除框架
sencha framework remove --force  extFiveO</pre>
<p>可以升级工作区内的应用程序，让其使用特定的框架版本：</p>
<pre class="crayon-plain-tag"># 从/path/to/ext目录升级以ext作为key的框架
sencha framework upgrade ext /path/to/ext
# 升级单个应用程序
sencha app upgrade ../ext62
# 升级整个工作区中所有应用程序、包中和Cmd相关的文件
sencha workspace upgrade </pre>
<p>在任何时候，你都可以列出当前工作区可用的框架版本：</p>
<pre class="crayon-plain-tag">sencha framework list</pre>
</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">6.2</span></div>
<table class=" full-width fixed-word-wrap">
<tbody>
<tr>
<td>
<p><strong>app init子命令</strong>
<p>允许初始化当前目录为一个Sencha Cmd应用程序。你可以从空白目录开始，执行下面的命令生成完整的HelloWorld程序结构：</p>
<pre class="crayon-plain-tag"># 除了--modern，还可以 --classic 或者 --universal
sencha app init --ext=/path/to/extjs/ AppName --modern</pre>
<p>执行构建后，应用程序可以在任意Web服务器上部署：<pre class="crayon-plain-tag">sencha app build</pre>  </p>
</td>
</tr>
<tr>
<td>
<p><strong>app install子命令</strong></p>
<p>确保不完整的Sencha Cmd应用程序变得可以运行（补充缺失的文件），示例：</p>
<pre class="crayon-plain-tag">sencha app install --frameworks=/path/to/extjs
# frameworks是 6.1引入的特性，允许你指向一个具有多个版本ExtJS的目录
# 当前使用的ExtJS版本定义在workspace.json中，Cmd会自动选择最匹配的版本</pre>
</td>
</tr>
<tr>
<td>
<p><strong>workspace init子命令</strong>
<p>确保当前目录是Sencha Cmd工作区或者工作区的一部分</p>
</td>
</tr>
<tr>
<td>
<p><strong>workspace install子命令</strong></p>
<p>确保不完整的Sencha Cmd工作区变得可以运行（补充缺失的文件），示例：</p>
<pre class="crayon-plain-tag">sencha workspace install --frameworks=/path/to/extjs </pre>
</td>
</tr>
<tr>
<td>
<p><strong>workspace cleanup子命令</strong>
<p>从workspace.json中移除已经不存在于工作区的应用程序条目</p>
</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">6.5</span></div>
<table class=" full-width fixed-word-wrap">
<tbody>
<tr>
<td>
<p><strong>ES6支持</strong></p>
<p>Cmd 6.5的主要变化就是支持ES6，即使是ExtJS 4.1.1+、Touch 2.1.x+也可以使用ES6来编写代码了</p>
<p>Cmd能够把ES6代码编译（Transpile）为老版本的JavaScript，这样老的浏览器可以被很好的支持。由于Transpile处理本身的特性，它必须在产品构建时执行，因此，你必须使用支持ES6的现代浏览器才能使用开发者模式（Developer Mode）</p>
<p>产品构建支持的浏览器包括IE8+和其它主流浏览器</p>
<p>如果你对那些老浏览器没兴趣，可以禁止Transpile：</p>
<p><pre class="crayon-plain-tag">"output": {
    "js": {
        "version": "ES6"
    }
}</pre>
</td>
</tr>
</tbody>
</table>
</div><p>The post <a rel="nofollow" href="https://blog.gmem.cc/sencha-cmd-study-note">Sencha Cmd学习笔记</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.gmem.cc/sencha-cmd-study-note/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>PhoneGap学习笔记</title>
		<link>https://blog.gmem.cc/phonegap-study-note</link>
		<comments>https://blog.gmem.cc/phonegap-study-note#comments</comments>
		<pubDate>Tue, 01 Dec 2015 07:21:52 +0000</pubDate>
		<dc:creator><![CDATA[Alex]]></dc:creator>
				<category><![CDATA[Android]]></category>
		<category><![CDATA[IOS]]></category>
		<category><![CDATA[JavaScript]]></category>
		<category><![CDATA[Cordova]]></category>

		<guid isPermaLink="false">https://blog.gmem.cc/?p=9504</guid>
		<description><![CDATA[<p>由于PhoneGap和Apache Cordova在底层上的一致性，本文主要讨论PhoneGap提供的额外工具，其他主题请参考： 基于Apache Cordova开发移动应用程序 PhoneGap简介 PhoneGap是什么 一个框架，支持通过标准的基于JavaScript的API，来开发跨平台的移动应用。PhoneGap同时支持基于Web、基于Native两种风格的混合应用，它通过联合使用Native元素和Web元素，来达到更加Native的look and feel。通过PhoneGap底层的Apache Cordova框架，我们可以使用很多设备的原生特性，例如GPS、罗盘、摄像头。 基于PhoneGap的混合应用有以下额外的特点： 不能完整调用原生API：只有受限的、通用的基础API被Cordova框架支持 平台差异化不足，应用界面长得差不多 PhoneGap与Apache Cordova的关系 后者是PhoneGap的底层框架，PhoneGap是后者的一个distribution。两者提供的命令行工具很类似，使用一套插件体系。PhoneGap提供了一些额外功能，包括： PhoneGap Developer App PhoneGap Desktop App <a class="read-more" href="https://blog.gmem.cc/phonegap-study-note">[...]</a></p>
<p>The post <a rel="nofollow" href="https://blog.gmem.cc/phonegap-study-note">PhoneGap学习笔记</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></description>
				<content:encoded><![CDATA[<div class="wri_content_clear_both"><p>由于PhoneGap和Apache Cordova在底层上的一致性，本文主要讨论PhoneGap提供的额外工具，其他主题请参考：</p>
<ol>
<li><a href="/mobile-app-dev-with-apache-cordova">基于Apache Cordova开发移动应用程序</a></li>
</ol>
<div class="blog_h1"><span class="graybg">PhoneGap简介</span></div>
<div class="blog_h2"><span class="graybg">PhoneGap是什么</span></div>
<p>一个框架，支持通过标准的基于JavaScript的API，来开发跨平台的移动应用。PhoneGap同时支持<span style="background-color: #c0c0c0;">基于Web、基于Native</span>两种风格的混合应用，它通过联合使用Native元素和Web元素，来达到更加Native的look and feel。通过PhoneGap底层的Apache Cordova框架，我们可以使用很多设备的原生特性，例如GPS、罗盘、摄像头。</p>
<p>基于PhoneGap的混合应用有以下额外的特点：</p>
<ol>
<li>不能完整调用原生API：只有受限的、通用的基础API被Cordova框架支持</li>
<li>平台差异化不足，应用界面长得差不多</li>
</ol>
<div class="blog_h2"><span class="graybg">PhoneGap与Apache Cordova的关系</span></div>
<p>后者是PhoneGap的底层框架，PhoneGap是后者的一个distribution。两者提供的<span style="background-color: #c0c0c0;">命令行工具很类似，使用一套插件体系</span>。PhoneGap提供了一些额外功能，包括：</p>
<ol>
<li>PhoneGap Developer App</li>
<li>PhoneGap Desktop App</li>
<li>PhoneGap Enterprise</li>
<li>云端构建功能</li>
</ol>
<div class="blog_h1"><span class="graybg">安装PhoneGap和设备端工具</span></div>
<div class="blog_h2"><span class="graybg">命令行工具（CLI）</span></div>
<p>PhoneGap依赖于Node.js、NPM（Node.js目前自带）、git，准备好后，运行：</p>
<pre class="crayon-plain-tag">npm install -g phonegap@latest</pre>
<p>安装结束后，即可运行命令<pre class="crayon-plain-tag">phonegap </pre> ，来执行工程<span style="background-color: #c0c0c0;">创建、构建、安装、启动HTTP服务</span>等功能。除了使用命令行工具外，PhoneGap还提供了<a href="http://build.phonegap.com/">云端构建服务</a>，简化构建、App打包流程。</p>
<div class="blog_h2"><span class="graybg">桌面工具（PhoneGap Desktop App）</span></div>
<p>PhoneGap为Windows、Mac OS X提供了图形化界面，使用它可以方便的创建新的PhoneGap应用。可以到<a href="https://github.com/phonegap/phonegap-app-desktop/releases/download/0.2.0/PhoneGapSetup-win32.exe">这里</a>下载Windows的安装包。</p>
<p>该工具价值不大，使用PhoneGap CLI可以很容易的代替之。</p>
<div class="blog_h2"><span class="graybg"><a id="device-tool"></a>设备端工具</span></div>
<p>PhoneGap提供了一个移动APP——<a href="http://app.phonegap.com/">PhoneGap Developer App</a>（以下简称PGDA），可以方便的在设备上<span style="background-color: #c0c0c0;">预览、测试</span>PhoneGap应用。不需要<span style="background-color: #c0c0c0;">任何插件的支持或者编译步骤</span>，该APP就可以支持设备Native特性的访问。</p>
<p>该APP有安卓、IOS、WP三个版本，可以到对应的商店下载安装。</p>
<div class="blog_h3"><span class="graybg">定制PGDA</span></div>
<p>PGDA本质上就是一个Phonegap应用程序，只是它具有从远程服务器上同步最新Web代码的能力。PGDA默认自带了Cordova内置插件，如果你要使用第三方插件，必须自己构建PGDA。</p>
<p>PGDA的源码托管在：<a href="https://github.com/phonegap/phonegap-app-developer">https://github.com/phonegap/phonegap-app-developer</a></p>
<p>下载源码后，可以参考如下方式添加新插件支持，并构建（以Android为例）：</p>
<pre class="crayon-plain-tag">cd phonegap-app-developer
# 添加NFC插件的支持
cordova plugin add phonegap-nfc
#构建Android平台
cordova build android</pre>
<p>构建完毕后，文件platforms\android\build\outputs\apk\android-debug.apk即为最新的PGDA安装包，安装到设备即可。 </p>
<div class="blog_h1"><span class="graybg">Hello World!</span></div>
<p>打开PhoneGap桌面版本，点击左侧的+号，即可创建新的PhoneGap应用。输入路径、名称、ID后，即可创建工程。新创建的工程的结构如下：</p>
<table class="fixed-word-wrap" style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="width: 25%; text-align: center;">目录/文件</td>
<td style="text-align: center;">说明 </td>
</tr>
</thead>
<tbody>
<tr>
<td>.cordova</td>
<td>存放Apache Cordova相关的文件 </td>
</tr>
<tr>
<td>hooks</td>
<td rowspan="5">这些目录/文件与Apache Cordova工程同名目录/文件的含义相同</td>
</tr>
<tr>
<td>platforms</td>
</tr>
<tr>
<td>plugins</td>
</tr>
<tr>
<td>www</td>
</tr>
<tr>
<td>config.xml</td>
</tr>
</tbody>
</table>
<p>亦可导入既有的PhoneGap应用，Apache Cordova应用也是支持的，可以导入。</p>
<p>点击项目右侧的右侧的运行按钮，会启动一个HTTP服务，侦听3000端口。</p>
<p>打开移动设备，点击PGDA图标，输入上述HTTP服务的地址和端口，点击Connect，看到<span style="background-color: #c0c0c0;">DEVICE IS READY</span>字样，说明设备和HTTP服务器连接成功。在此设备界面上：</p>
<ol>
<li>四指轻触：刷新视图</li>
<li>三指轻触：回到PGDA主界面</li>
</ol>
<p>现在，可以打开<pre class="crayon-plain-tag">www/index.html</pre> 下的文件进行编辑，例如我们可以修改<pre class="crayon-plain-tag">&lt;h1&gt;PhoneGap&lt;/h1&gt;</pre>中的文本。其结果将很快的反映到移动设备上。</p>
<div class="blog_h1"><span class="graybg">PhoneGap CLI详解</span></div>
<p>命令格式：<pre class="crayon-plain-tag">phonegap [子命令] [选项]</pre> </p>
<div class="blog_h3">全局命令</div>
<table class=" fixed-word-wrap full-width">
<thead>
<tr>
<td style="width: 20%; text-align: center;">命令</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>create</td>
<td>
<pre class="crayon-plain-tag">phonegap create [options] &lt;path&gt; [id [name [config]]]</pre></p>
<p>创建新的PhoneGap工程：<br />path 在此目录创建工程<br />id 作为应用程序唯一标识的包名称<br />name 应用程序的名称<br />config JSON格式的配置信息，将被存放到.cordova/config.json目录<br />options<br />　--template &lt;name&gt; 从既有的应用模板创建，template命令可以管理模板<br />　--copy-from &lt;path&gt; 从既有的工程复制并创建<br />　--link-to &lt;path&gt; 链接到既有的www目录而不复制</p>
<p>举例：</p>
<p><pre class="crayon-plain-tag">phonegap create project-dir cc.gmem.phonegap.study PhonegpStudy</pre>
</td>
</tr>
<tr>
<td>help</td>
<td>显示帮助信息</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">工程命令</span></div>
<table class=" fixed-word-wrap full-width">
<thead>
<tr>
<td style="width: 20%; text-align: center;"><span style="font-weight: normal; line-height: 19px;">命令</span></td>
<td style="text-align: center;"><span style="font-weight: normal; line-height: 19px;">说明</span></td>
</tr>
</thead>
<tbody>
<tr>
<td><span style="line-height: 19px;">run</span></td>
<td><span style="font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; font-size: small;"><span style="line-height: 19px;"><pre class="crayon-plain-tag">phonegap run [&lt;platforms&gt;] [options] [-- [platform options]]</pre> <br />准备、构建、安装应用程序到指定平台，如果不提供platforms参数，则对所有已添加的平台执行<br />options<br /></span></span>　--device 安装到设备<br />　--emulator 安装到模拟器<br />　--target &lt;id&gt; 安装到指定的目标<br />　--debug 以Debug模式构建（默认）<br />　--release 以Release模式构建<br />　--nobuild 跳过构建，直接安装</td>
</tr>
<tr>
<td><span style="line-height: 19px;">platform</span></td>
<td><span style="line-height: 19px;">管理平台，支持add、remove、update、check、list操作，与Cordova命令基本一致</span></td>
</tr>
<tr>
<td><span style="line-height: 19px;">plugin</span></td>
<td><span style="line-height: 19px;">管理插件，支持add、remove、list等操作，与Cordova命令基本一致</span></td>
</tr>
<tr>
<td><span style="line-height: 19px;">template</span></td>
<td>
<pre class="crayon-plain-tag"># 列出可用模板
phonegap template list</pre></p>
<p>从既有工程创建模板，或者列出可用模板</p>
</td>
</tr>
<tr>
<td><span style="line-height: 19px;">info</span></td>
<td><span style="line-height: 19px;">显示工程的相关信息 ，与Cordova命令基本一致</span></td>
</tr>
<tr>
<td><span style="line-height: 19px;">serve</span></td>
<td><span style="line-height: 19px;"><pre class="crayon-plain-tag">phonegap serve [options]</pre> <br />作为本地Web应用程序运行，PGDA可以连接到这样的Web服务：<br />options<br />　--port, -p &lt;n&gt; HTTP端口，默认3000<br />　--autoreload 当服务器端文件发生变化时，自动刷新PGDA，默认true<br />　--no-autoreload 禁止上述刷新<br />　--browser 启用桌面浏览器支持，默认false<br /></span></td>
</tr>
<tr>
<td><span style="line-height: 19px;">remote</span></td>
<td><pre class="crayon-plain-tag">phonegap remote [command]</pre> <br />在PhoneGap云端构建服务上执行命令：<br />command<br />　login　登录到云端构建<br />　logout 从云端构建退出登录<br />　build &lt;platform&gt; 构建指定的平台<br />　install &lt;platform&gt; 安装指定的平台<br />　run &lt;platform&gt; 构建并安装指定的平台</td>
</tr>
<tr>
<td><span style="line-height: 19px;">prepare</span></td>
<td rowspan="3"><span style="line-height: 19px;">与对应的Cordova命令基本一致</span></td>
</tr>
<tr>
<td><span style="line-height: 19px;">compile</span></td>
</tr>
<tr>
<td><span style="line-height: 19px;">emulate</span></td>
</tr>
<tr>
<td><span style="line-height: 19px;">cordova</span></td>
<td><span style="line-height: 19px;">执行指定的Cordova命令</span></td>
</tr>
</tbody>
</table>
<div class="blog_h1"><span class="graybg">PhoneGap云构建服务</span></div>
<p>利用Adobe提供的PhoneGap云端服务，只需要上传PhoneGap应用程序的资产文件（HTML、CSS、JavaScript），后续的构建工作可以在云端完成。使用云构建服务，可以：</p>
<ol>
<li>避免本地维护各套平台SDK、甚至安装多套操作系统的麻烦</li>
<li>加快开发周期，在云端可以更快的构建，并且构建结果可以推送到测试人员预安装的App中</li>
</ol>
<p>云构建服务分为免费、收费版本，免费版本仅支持一个私有（闭源、商业）App。</p>
<div class="blog_h1"><span class="graybg">常见问题</span></div>
<div class="blog_h3"><span class="graybg">PhoneGap Developer App不自动重新加载</span></div>
<p>自动重新加载时，PhoneGap CLI会在控制台上打印信息：<pre class="crayon-plain-tag">[phonegap] 200 /__api__/autoreload</pre> </p>
<p>自动重新加载的前提条件是，页面引用cordova的JavaScript文件：</p>
<pre class="crayon-plain-tag">&lt;script type="text/javascript" src="cordova.js"&gt;&lt;/script&gt;</pre>
<p>此外，某些版本上的模拟器不能正常reload，可能是兼容性问题。</p>
<div class="blog_h3"><span class="graybg">iOS6上PhoneGap Developer App报错：404 /__api__/appzip</span></div>
<p>我的手机是越狱后的iOS 6.1.2，在AppStore下载的PGDA 1.5.12，无法连接到通过<pre class="crayon-plain-tag">phonegap serve</pre> 启动的服务器。 </p>
<p>到<a href="https://github.com/phonegap/phonegap-app-developer/tree/master/resources/release/ios">GitHub</a>上逐个下载预构建版本并安装测试，发现：</p>
<ol>
<li>1.5.x：全部不能使用</li>
<li>1.4.0：可以连接但无法自动重新加载</li>
<li>1.3.0：能够连接，也可以autoreload</li>
</ol>
<p>很无语……现在安装1.3.0正常使用。</p>
</div><p>The post <a rel="nofollow" href="https://blog.gmem.cc/phonegap-study-note">PhoneGap学习笔记</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.gmem.cc/phonegap-study-note/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>基于Apache Cordova开发移动应用程序</title>
		<link>https://blog.gmem.cc/mobile-app-dev-with-apache-cordova</link>
		<comments>https://blog.gmem.cc/mobile-app-dev-with-apache-cordova#comments</comments>
		<pubDate>Mon, 09 Nov 2015 09:20:41 +0000</pubDate>
		<dc:creator><![CDATA[Alex]]></dc:creator>
				<category><![CDATA[Android]]></category>
		<category><![CDATA[IOS]]></category>
		<category><![CDATA[Mobile]]></category>
		<category><![CDATA[Cordova]]></category>

		<guid isPermaLink="false">https://blog.gmem.cc/?p=9730</guid>
		<description><![CDATA[<p>基础知识 名词术语 目标平台：本文中会反复出现该名词，目标平台是指移动操作系统+移动应用SDK构成的移动应用支撑环境。有时也称作移动平台，或简称平台 构建平台：在其上运行Cordova工具，生成目标平台应用程序的宿主操作系统 PhoneGap：目前作为Apache Cordova的一个发行版，它们的命令行非常相似。PhoneGap提供了一些更高级的组件，例如云端构建、设备端工具（PhoneGap Developer App） 移动应用开发技术的选型 可以选择三种方式来开发移动应用，它们各有特点： 原生（Native）应用 为特定的设备、操作系统构建。需要熟悉设备SDK，甚至特殊的开发语言：Objective-C/Swift (iOS)；Java (Android)；C# (Windows) 安装到设备，作为本地程序运行 使用原生的API和控件 通常必须经过应用市场的验证才能被客户使用 可以离线使用 性能优秀 纯Web应用 在任何移动浏览器中运行 <a class="read-more" href="https://blog.gmem.cc/mobile-app-dev-with-apache-cordova">[...]</a></p>
<p>The post <a rel="nofollow" href="https://blog.gmem.cc/mobile-app-dev-with-apache-cordova">基于Apache Cordova开发移动应用程序</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></description>
				<content:encoded><![CDATA[<div class="wri_content_clear_both"><div class="blog_h1"><span class="graybg">基础知识</span></div>
<div class="blog_h2"><span class="graybg">名词术语</span></div>
<ol>
<li>目标平台：本文中会反复出现该名词，目标平台是指<span style="background-color: #c0c0c0;">移动操作系统+移动应用SDK</span>构成的移动应用支撑环境。有时也称作移动平台，或简称平台</li>
<li>构建平台：在其上运行Cordova工具，生成目标平台应用程序的宿主操作系统</li>
<li>PhoneGap：目前作为Apache Cordova的一个发行版，它们的命令行非常相似。PhoneGap提供了一些更高级的组件，例如云端构建、设备端工具（PhoneGap Developer App）</li>
</ol>
<div class="blog_h2"><span class="graybg">移动应用开发技术的选型</span></div>
<p>可以选择三种方式来开发移动应用，它们各有特点：</p>
<div class="blog_h3"><span class="graybg">原生（Native）应用</span></div>
<ol>
<li>为特定的设备、操作系统构建。需要熟悉设备SDK，甚至特殊的开发语言：Objective-C/Swift (iOS)；Java (Android)；C# (Windows)</li>
<li>安装到设备，作为本地程序运行</li>
<li>使用原生的API和控件</li>
<li>通常必须经过应用市场的验证才能被客户使用</li>
<li>可以离线使用</li>
<li>性能优秀</li>
</ol>
<div class="blog_h3"><span class="graybg">纯Web应用</span></div>
<ol>
<li>在任何移动浏览器中运行</li>
<li>支持受限的使用设备特性</li>
<li>基于标准的Web技术构建，包括HTML、CSS、JavaScript</li>
<li>需要支持多种屏幕尺寸</li>
<li>需要Web服务器的支撑，不支持离线使用</li>
<li>性能较差</li>
</ol>
<div class="blog_h3"><span class="graybg">混合（Hybrid）应用</span></div>
<p>所谓混合应用，就是结合了前两者的特性和优势的应用，混合应用可以分为两种风格：</p>
<ol>
<li>基于Web的混合应用（Web hybrid mobile apps）：APP运行在一个很薄的<span style="background-color: #c0c0c0;">Native容器+WebView</span>中，该容器仅作为访问Native特性的桥梁，所有UI组件均基于Web技术构建</li>
<li>基于Native的混合应用（Native hybrid mobile apps）：包含很多的Native控件，以及1-N个WebView。常常<span style="background-color: #c0c0c0;">使用Native控件做导航、并在提供应用内容的WebView之间进行切换</span></li>
</ol>
<p>混合应用具有以下特性：</p>
<ol>
<li>安装到设备，作为本地程序运行</li>
<li>应用逻辑主要在WebView（嵌入的Web浏览器）中运行</li>
<li>大部分代码是跨平台的</li>
<li>支持访问原生特性</li>
<li>由于应用内容主要使用基于设备内置浏览器引擎，因此性能比起原生应用差</li>
</ol>
<div class="blog_h1"><span class="graybg">Apache Cordova简介</span></div>
<p>Apache Cordova是一个开源移动应用开发框架，它允许你使用标准的Web技术——HTML5、CSS3、JavaScript ——来进行跨平台开发，避免使用各移动平台Native的编程语言和SDK。基于Cordova的应用程序被包装在各平台的Wrapper中，使用标准兼容的API访问设备传感器、数据和其它Native特性。</p>
<div class="blog_h2"><span class="graybg">基本组件</span></div>
<p>Apache Cordova 工程依赖于一个命名为<span style="background-color: #c0c0c0;">config.xml</span>的配置文件，该配置文件影响应用程序的工作方式（例如是否对横屏做出响应）。</p>
<p>应用程序本身实现为<span style="background-color: #c0c0c0;">网页</span>，默认情况下是本地的一个index.html文件，该文件可能引用任何Web资源，例如JavaScript、CSS。应用程序被包装在Native的Wrapper中，作为WebView运行。该WebView作为更大的混合（Hybrid ）应用的一个组件，或者占据整个UI。</p>
<p><span style="background-color: #c0c0c0;">插件接口（plugin interface ）</span>允许Cordova和Native组件之间进行通信，插件接口以JavaScript的形式提供，从而允许在网页上调用Native代码。理想情况下，各平台的API应该一致，在3.0中，官方插件提供标准化的设备API，而<a href="http://plugins.cordova.io/">第三方插件</a>可能提供一些特定于特殊平台的API。Cordova官方提供了自定义插件的<a href="https://cordova.apache.org/docs/en/latest/guide/hybrid/plugins/index.html">开发文档</a>。</p>
<p>从3.0开始，新创建的Cordova工程默认不包含任何插件，所有插件都需要手工添加。</p>
<div id="cordova-workflow" class="blog_h2"><span class="graybg">开发工作流（Workflow）</span></div>
<p>Cordova 应用的开发工作流有两种：</p>
<ol>
<li>以Cordova CLI为中心跨平台工作流：如果需要应用在尽可能多的平台上运行，可以使用这种方式，该方式需要很少的平台特定开发工作量。CLI是3.0引入的，可以同时为多个平台构建工程的工具，它是Cordova跨平台工作流的核心。CLI会<span style="background-color: #c0c0c0;">创建Cordova工程的初始结构、把Web资产文件拷贝到各平台的子目录下、对各平台进行必要的配置，然后运行脚本生成二进制程序</span>。CLI也提供了为应用添加插件的公共接口</li>
<li>以平台为中心的工作流：如果需要构建特定单个平台的应用，以及需要在底层基于平台SDK对代码进行修改，可以使用该方式。典型的例子是开发Native-based混合应用，需要把WebView和其他Native组件集成时。使用这种方式开发时，可以通过<span style="background-color: #c0c0c0;">Plugman</span>来添加插件</li>
</ol>
<p>推荐以第1种方式开始，使用CLI创建Cordova工程，在<span style="background-color: #c0c0c0;">必要时切换</span>到第2种方式。针对平台的低级别Shell工具可以在工程的<pre class="crayon-plain-tag">platforms/*/cordova</pre> 目录下找到（其中*代表平台名称）。<span style="background-color: #c0c0c0;">一旦切换，就不能返回</span>，因为CLI管理的跨平台源代码集会在每次构建时被覆盖。为了保护你修改过的代码，必须使用平台特定的Shell工具，这些Shell工具忽略跨平台代码而依赖于平台特定的代码。</p>
<div class="blog_h1"><span class="graybg">基于CLI的跨平台工作流</span></div>
<p>使用Cordova的命令行接口（CLI），可以完成新工程的创建、针对各平台进行构建、并部署到设备（或模拟器）中运行。</p>
<p>在安装和使用CLI前，必须下载和安装相关目标平台的SDK和相关工具，如果不安装，在调用CLI进行应用构建时会收到错误提示，例如：</p>
<pre class="crayon-plain-tag">#没有安装完整Android SDK机器上的报错信息
Running command: cmd "/s /c "...\cordova-study\platforms\android\cordova\build.bat""
[Error: Please install Android target: "android-22".

Hint: Open the SDK manager by running: D:\Android\sdk\tools\android.BAT
You will require:
1. "SDK Platform" for android-22
2. "Android SDK Platform-tools (latest)
3. "Android SDK Build-tools" (latest)]
ERROR building one of the platforms: Error: cmd: Command failed with exit code 2
You may not have the required environment or OS to build this project</pre>
<p>CLI支持的<span style="background-color: #c0c0c0;">目标平台（构建平台）</span>包括：</p>
<ol>
<li>iOS（Mac）</li>
<li>Android (Mac, Linux, Windows)</li>
<li>BlackBerry 10 (Mac, Linux, Windows)</li>
<li>Windows Phone 8 (Windows)</li>
</ol>
<p>可以看到如果要支持全部主要的目标平台，你需要在不同操作系统上运行CLI命令，这意味着你应该使用代码库（SVN/Git）来管理工程。</p>
<div class="blog_h2"><span class="graybg">安装Cordova CLI</span></div>
<ol>
<li><a href="/node-js-faq">安装Node.js</a></li>
<li><a href="https://git-scm.com/downloads">安装Git客户端</a></li>
<li>通过npm命令安装cordova模块：<br />
<pre class="crayon-plain-tag"># 安装Cordova为全局模块
npm install -g cordova
#安装特定版本的Cordova
npm install -g cordova@3.1.0-0.2.0

#更新Cordova
npm update -g cordova

#显示版本信息
cordova -v</pre>
</li>
</ol>
<div class="blog_h2"><span class="graybg">创建Cordova工程</span></div>
<p>执行下面的命令创建工程：</p>
<pre class="crayon-plain-tag">pushd D:\JavaScript\projects\cordova\5.4.1
# cordova-study在当前目录下创建的子目录，作为工程文件的容器
# cc.gmem.study.cordova 域名反转的包名，可能被生成的外部文件引用，例如Android包名
# CordovaStudy 应用程序的标题
# 第2/3个参数可以在config.xml中手工修改
cordova create cordova-study cc.gmem.study.cordova CordovaStudy</pre>
<p>命令执行完毕后，生成如下目录结构： </p>
<table class="fixed-word-wrap" style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="width: 25%; text-align: center;">目录/文件</td>
<td style="text-align: center;">说明 </td>
</tr>
</thead>
<tbody>
<tr>
<td>hooks</td>
<td>Cordova钩子脚本的存放目录</td>
</tr>
<tr>
<td>platforms</td>
<td>生成的平台特定的文件，包括工程代码和元数据。基于CLI的跨平台工作流进行开发时，应该避免修改此目录下的任何文件</td>
</tr>
<tr>
<td>plugins</td>
<td>应用程序使用的插件目录</td>
</tr>
<tr>
<td>merges</td>
<td>用于合并到各平台assets/www中的文件，特定于平台的文件，按照www的结构，在此目录下存放，在构建时会自动合并</td>
</tr>
<tr>
<td>www</td>
<td>存放应用程序的主页面（index.html），以及css、js、img等Web资源。这些资产内容会<span style="background-color: #c0c0c0;">存放在设备的本地文件系统，而不是远程Web服务器</span>上。开发Cordova应用的主要工作在此目录下进行</td>
</tr>
<tr>
<td>config.xml</td>
<td>包含了用于生成和分发应用程序的重要元数据</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">添加目标平台支持</span></div>
<p>运行下面的命令添加对特定平台的支持：</p>
<pre class="crayon-plain-tag">#添加Android平台的支持
cordova platform add android
#输出如下：
Running command: cmd "/s /c "...\create.bat ...\cordova-study\platforms\android cc.gmem.study.cordova CordovaStudy --cli""
Creating Cordova project for the Android platform:
        #创建目标平台工程特定的文件
        Path: platforms\android
        Package: cc.gmem.study.cordova
        Name: CordovaStudy
        Activity: MainActivity
        Android target: android-22
Copying template files...
Android project created with cordova-android@4.1.1
#安装插件
Discovered plugin "cordova-plugin-whitelist" in config.xml. Installing to the project
Fetching plugin "cordova-plugin-whitelist@1" via npm
Installing "cordova-plugin-whitelist" for android


#添加iOS平台的支持，需要在安装了iOS SDK的Mac OS X上执行
cordova platform add ios
#添加Windows Phone 8平台的支持，需要在Windows上运行
cordova platform add wp8


#下面的命令示例了如何移除平台的支持
cordova platform remove blackberry10

#添加指定版本的cordova平台支持，并保存到config.xml中
cordova platform add android@3.7.0 --save</pre>
<p>运行上述命令后，Cordova工程的platforms、plugins子目录出现一些下载/生成的文件：</p>
<table class="fixed-word-wrap" style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="width: 140px; text-align: center;">目录</td>
<td style="width: 100px; text-align: center;">子目录</td>
<td style="text-align: center;">说明 </td>
</tr>
</thead>
<tbody>
<tr>
<td rowspan="7">platforms/android      </td>
<td>.</td>
<td>当前目录包含了一个完整的Android工程，可以导入到Android Studio或者ADT中</td>
</tr>
<tr>
<td>cordova</td>
<td>特定于Android平台的Shell工具，可以完成Android平台应用的构建、清理、运行</td>
</tr>
<tr>
<td>CordovaLib</td>
<td>一个Android工程，包含了作为Android库的Cordova源代码</td>
</tr>
<tr>
<td>assets</td>
<td rowspan="4">
<p>遵循Android工程的默认用途</p>
<p>其中：</p>
<ol>
<li>assets/www包含了Cordova应用的所有Web资源文件，包括HTML、CSS、JS等。<span style="background-color: #c0c0c0;">Cordova工程中的www目录中的内容全部复制一份</span>覆盖到该目录。插件的Web资源也拷贝并覆盖到plugins子目录</li>
<li>src目录包含自动生成的一个Activity源码、已经拷贝过来的插件源代码</li>
</ol>
</td>
</tr>
<tr>
<td>src</td>
</tr>
<tr>
<td>libs</td>
</tr>
<tr>
<td>res</td>
</tr>
<tr>
<td>plugins</td>
<td colspan="2">存放下载的对应各目标平台的插件源代码、文档</td>
</tr>
</tbody>
</table>
<p><a href="#cordova-workflow">前面</a>我们说过，创建新工程可以从CLI命令行开始，在必要时切换到特定平台的开发工作中去。在此，就可以进行切换了：只需要把platforms子目录导入到Eclipse、XCode等IDE即可，然后以IDE为中心完成后续开发。</p>
<p>如果继续执行以CLI为中心的工作流，则需要继续了解下面的小节。</p>
<div class="blog_h2"><span class="graybg">构建应用</span></div>
<p>执行下面的命令可以构建应用程序的二进制文件并打包：</p>
<pre class="crayon-plain-tag"># 构建所有平台
cordova build
# 构建特定平台
cordova build ios</pre>
<p>上述build命令实际上包含了两个步骤： </p>
<pre class="crayon-plain-tag"># 准备阶段，会把Cordova源码拷贝、合并到平台的对应目录中
# 一旦执行完该命令，就可以用特定平台提供的SDK进行修改、编译
cordova prepare ios
# 编译
cordova compile ios</pre>
<div class="blog_h2"><span class="graybg">在设备上测试应用</span></div>
<p>运行下面的命令重新构建特定平台的应用，并部署到模拟器中：</p>
<pre class="crayon-plain-tag"># 运行下面的命令后，Cordova会自动启动模拟器，并把APK安装到上面
# 如果有模拟器正在运行，则使用既有的模拟器
cordova emulate android
# APK安装完毕后，将自动运行</pre>
<p> 或者，将启用了调试模式的手机连接到当前计算机，然后运行：</p>
<pre class="crayon-plain-tag">cordova run android</pre>
<div class="blog_h2"><span class="graybg">添加插件支持</span></div>
<p>你可以使用Web技术修改Cordova生成的应用骨架，来开发漂亮的页面。但是如果<span style="background-color: #c0c0c0;">想使用设备底层特性，则必须添加插件</span>，利用插件可以访问核心Cordova API。第三方/自己开发的插件可以用来访问特殊的设备特性，要访问通用设备特性使用Cordova提供的的插件就足够了。官方插件和第三方插件的列表位于<a href="http://cordova.apache.org/plugins/">http://cordova.apache.org/plugins/</a>。</p>
<p>下面的命令用来搜索插件：</p>
<pre class="crayon-plain-tag">#同时包含bar和code的插件
cordova plugin search bar code</pre>
<p>下面的命令用来列出当前工程已添加的插件：</p>
<pre class="crayon-plain-tag">cordova plugin ls</pre>
<p>下面的命令用来移除已经添加的插件：</p>
<pre class="crayon-plain-tag">cordova plugin rm cordova-plugin-console</pre>
<p>下面的命令用来给应用程序添加插件：</p>
<pre class="crayon-plain-tag">#依据插件ID添加
cordova plugin add plugin-id
#添加特定版本的插件
cordova plugin add plugin-id@latest
cordova plugin add plugin-id@1.2.0

# 设备基本特性
cordova plugin add cordova-plugin-device
# 网络连接信息
cordova plugin add cordova-plugin-network-information
# 电池状态
cordova plugin add cordova-plugin-battery-status
# 加速度计
cordova plugin add cordova-plugin-device-motion
# 横屏切换
cordova plugin add cordova-plugin-device-orientation
# 地理位置信息
cordova plugin add cordova-plugin-geolocation
# 摄像头
cordova plugin add cordova-plugin-camera
# 媒体截屏
cordova plugin add cordova-plugin-media-capture
# 媒体回放
cordova plugin add cordova-plugin-media
# 访问设备或网络上的文件
cordova plugin add cordova-plugin-file
cordova plugin add cordova-plugin-file-transfer
# 通过振动或者对话框通知用户
cordova plugin add cordova-plugin-dialogs
cordova plugin add cordova-plugin-vibration
# 访问通讯录
cordova plugin add cordova-plugin-contacts
# 国际化
cordova plugin add cordova-plugin-globalization
# 启动画面支持
cordova plugin add cordova-plugin-splashscreen
# 打开新的应用内浏览器窗口
cordova plugin add cordova-plugin-inappbrowser
# 调试控制台
cordova plugin add cordova-plugin-console</pre>
<p>CLI根据需要为各平台添加插件代码。Plugman可以为单个平台添加插件支持。 </p>
<p>如果插件没有在默认源（registry.cordova.io ）注册，通过下面的命令添加：</p>
<pre class="crayon-plain-tag"># 从Git的master分支下载插件
cordova plugin add https://github.com/someone/aplugin.git
# 从特定标记或者分支下载插件
cordova plugin add https://github.com/someone/aplugin.git#r0.2.0
# 如果插件（以及plugin.xml）在Git仓库的子目录下，可以
cordova plugin add https://github.com/someone/aplugin.git#:/sub/dir
# 也可以从本地文件系统添加插件
cordova plugin add ../local_plugin_dir</pre>
<div class="blog_h2"><span class="graybg">利用合并进行平台差异化处理</span></div>
<p>前面提到过，在以CLI为中心的跨平台开发工作流下，不应当修改platforms子目录。那么，如果如何实现平台差异化呢？例如为不同平台提供不同的样式表.css文件，这是很合理的需求。</p>
<p>Cordova提供了一种合并机制，在工程顶级目录下，可以添加一个merges目录，其子目录是各平台的名称，例如<pre class="crayon-plain-tag">merges/android</pre> 。这些子目录的内部结构和www目录一样，在构建时，会自动覆盖www中的文件，并最终拷贝到特定平台的资产目录下。举例来说：<pre class="crayon-plain-tag">merges/android/css/index.css</pre> 会覆盖Android平台下对应的<pre class="crayon-plain-tag">www/css/index.css</pre> 文件。</p>
<p>merges中的文件如果在www下没有对应物，会直接复制到特定平台的资产目录下。</p>
<div class="blog_h2"><span class="graybg">CLI命令说明</span></div>
<p>命令格式：<pre class="crayon-plain-tag">cordova 子命令 [选项]</pre> </p>
<div class="blog_h3"><span class="graybg">通用选项</span></div>
<table class=" fixed-word-wrap full-width">
<thead>
<tr>
<td style="text-align: center;">选项</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>-v, --version</td>
<td>打印CLI版本</td>
</tr>
<tr>
<td>-d, --verbose</td>
<td>显示CLI命令的活动的详细信息</td>
</tr>
<tr>
<td>--no-update-notifier</td>
<td>禁止检查CLI的版本更新</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">全局命令</span></div>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 25%; text-align: center;">子命令</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>create</td>
<td>创建一个新的Cordova工程</td>
</tr>
<tr>
<td>help</td>
<td>显示命令帮助：<br />
<pre class="crayon-plain-tag"># 显示run子命令的帮助信息
cordova run --help</pre>
</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">工程命令</span></div>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 25%; text-align: center;">子命令</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>info</td>
<td>显示当前工程的详细信息，例如哪些平台被支持，添加了哪些插件</td>
</tr>
<tr>
<td>requirements</td>
<td>检查并显示指定平台的要求，例如SDK版本</td>
</tr>
<tr>
<td>platform</td>
<td>
<p>管理工程支持的平台：<br />add 添加一个平台支持<br />remove 移除平台支持<br />list 列出已经安装和可用的平台<br />update 更新特定平台使用的Cordova版本<br />check 列出支持cordova platform update的平台</p>
</td>
</tr>
<tr>
<td>plugin</td>
<td>管理工程使用的插件：<br />add 添加一个插件<br />remove 移除一个插件<br />list 列出当前安装的插件列表<br />search 从插件仓库搜索插件</td>
</tr>
<tr>
<td>prepare</td>
<td>拷贝、合并工程文件到目标平台，以备构建</td>
</tr>
<tr>
<td>compile</td>
<td>编译目标平台，命令格式：<br /><pre class="crayon-plain-tag">cordova compile [PROD] [TARGET] [EXP] [PLATS] [-- POPTS]</pre> </p>
<p>PROD：编译的变体， --debug|--release<br />TARGET：目标设备， --device|--emulator|--target=FOO<br />PLATS：目标平台，可以指定多个平台<br />POPTS：平台特定的选项 </p>
</td>
</tr>
<tr>
<td>build</td>
<td>等价于<pre class="crayon-plain-tag">cordova prepare &amp;&amp; cordova compile</pre> </td>
</tr>
<tr>
<td>clean</td>
<td>清除已经构建出的构件（artifacts）</td>
</tr>
<tr>
<td>run</td>
<td>prepare、compile，并在设备或者模拟器上运行工程：<br /> --nobuild 不进行构建<br />--debug 部署Debug版本<br />--release  部署Release版本</td>
</tr>
<tr>
<td>emulate</td>
<td>等价于<pre class="crayon-plain-tag">cordova run --emulator</pre> </td>
</tr>
<tr>
<td>serve</td>
<td>
<p>启动一个本地HTTP服务器，把工程作为Web服务使用：<br /><pre class="crayon-plain-tag">cordova serve [PORT]</pre> </p>
<p>此命令启动的服务与PhoneGap Developer App无法搭配使用，可以改用：</p>
<pre class="crayon-plain-tag">phonegap serve</pre>
</td>
</tr>
</tbody>
</table>
<div class="blog_h1"><span class="graybg">以Android平台为中心的工作流</span></div>
<p>首先，参考文章：<a href="/eclipse-adt-and-mvn-based-android-dev">基于Eclipse ADT和Maven的Android开发</a> 完整Android开发环境的搭建。
<p>由于Android平台官方的开发工具现在是Android Studio，并且基于Gradle构建，所以最好搭建好Gradle环境和Gradle的Eclipse插件。可参考文章：<a href="/gradle-study-note">Gradle学习笔记</a>。</p>
<div class="blog_h2"><span class="graybg">导入为Eclipse ADT工程</span></div>
<p>通过CLI创建Cordova工程后，一旦添加了Android平台的支持，就可以把platforms/android子目录作为工程导入到Eclipse ADT或者Android Studio。</p>
<p>以Eclipse ADT为例，操作步骤为：</p>
<ol>
<li>File ⇨ Import</li>
<li>在弹出的对话框中选择 Android ⇨ Existing Android Code Into Workspace，点击下一步</li>
<li>把Root Directory定位到Cordova工程的platforms/android目录，选择其中的两个工程，如下图：<br /><img class="aligncenter size-full wp-image-9857" src="https://blog.gmem.cc/wp-content/uploads/2015/11/import-cordova-project.jpg" alt="import-cordova-project" width="679" height="550" /></li>
<li>点击完成，工作区出现两个新的ADT工程，导入完毕</li>
</ol>
<p>这两个工程就是普通的ADT工程。其中cordovalib是cordova-android的依赖库。在cordova-android上点击右键 ⇨ Run as ⇨ Android Application，即可打包并部署到设备上运行。</p>
<p>上述方式导入的工程结构比较混乱，如果后续开发脱离Cordova进行，建议清理其中不需要的目录和文件。如果不习惯基于Gradle进行构建，可以创建或者转换为Maven工程。</p>
<div class="blog_h2"><span class="graybg">转换为Eclipse Gradle工程</span></div>
<p>安装了Gradle的Eclipse插件后，可以把platforms/android子目录转换为Gradle工程：</p>
<ol>
<li>File ⇨ Import</li>
<li>在弹出的对话框中选择 Gradle ⇨ Gradle Project，点击下一步</li>
<li>在弹出的对话框中，把Root folder定位到platforms/android子目录，然后点击右侧的Build Model按钮。Gradle从Maven仓库下载一些构件到Gradle缓存目录，完毕后显示如下画面：<img class="aligncenter size-full wp-image-10269" src="https://blog.gmem.cc/wp-content/uploads/2015/11/import-as-gradle-project.jpg" alt="import-as-gradle-project" width="653" height="716" /></li>
<li>点击完成。你可以自由的修改Eclipse工程的名称</li>
</ol>
<div class="blog_h2"><span class="graybg">转换为Eclipse Maven工程</span></div>
<p>如果习惯于使用Maven进行构建工作，可以参考下面的步骤，把platforms/android子目录转换为Maven工程：</p>
<ol>
<li>参考：<a href="/eclipse-adt-and-mvn-based-android-dev">基于Eclipse ADT和Maven的Android开发</a>创建Android的Maven工程，设工程根目录为<pre class="crayon-plain-tag">%MVN_PROJECT_ROOT%</pre> 。删除自动生成的Activity、res目录，并转换为android-maven-plugin 4.3.0标准的工程结构</li>
<li>如果使用SVN，在目录%MVN_PROJECT_ROOT% 上设置属性svn:ignore为：<br />
<pre class="crayon-plain-tag">target
bin
.*
cli</pre>
</li>
<li>通过CLI创建对应的（同名同包）Cordova工程，添加必要的插件，设工程根目录为<pre class="crayon-plain-tag">%CORDOVA_PROJECT_ROOT%</pre> </li>
<li>创建Maven工程指向Cordova工程的符号链接：<br />
<pre class="crayon-plain-tag">mklink /D %MVN_PROJECT_ROOT%\cli %CORDOVA_PROJECT_ROOT%</pre>
</li>
<li>另外创建一个Android的Maven工程“cordova-android”，project.properties中设置<pre class="crayon-plain-tag">android.library=true</pre> ，表示这是一个库工程。设工程根目录为<pre class="crayon-plain-tag">%MVN_LIB_ROOT%</pre> </li>
<li>修改<pre class="crayon-plain-tag">%MVN_PROJECT_ROOT%\pom.xml</pre> ，添加cordova-android的依赖，例如：<br />
<pre class="crayon-plain-tag">&lt;dependency&gt;
    &lt;groupId&gt;org.apache.cordova&lt;/groupId&gt;
    &lt;artifactId&gt;cordova-android&lt;/artifactId&gt;
    &lt;version&gt;4.4.1-SNAPSHOT&lt;/version&gt;
    &lt;type&gt;apklib&lt;/type&gt;
&lt;/dependency&gt;</pre>
</li>
<li>修改Cordova工程的<pre class="crayon-plain-tag">%CORDOVA_PROJECT_ROOT%\cli\platforms\android\build-extras.gradle</pre> 文件，添加以下Tasks：<br />
<pre class="crayon-plain-tag">task syncdown_src(type : Copy){
    from 'src'
    into '../../../src/main/java'
}
task syncdown_assets(type : Copy){
    from 'assets'
    into '../../../src/main/assets'
}
task syncdown_res(type : Copy){
    from 'res'
    into '../../../src/main/res'
}
task syncdown_libsrc(type : Copy){
    def dest = '../../../../cordova-android/src/main/java'
    if(file(dest).exists()){
        from 'CordovaLib/src'
        into dest
    }
}
task syncdown_manifest(type : Copy){
    from 'AndroidManifest.xml'
    into '../../../src/main'
}
task syncdown( dependsOn: [syncdown_src, syncdown_assets, syncdown_res, syncdown_libsrc, syncdown_manifest] )</pre>
</li>
<li>每当添加Cordova插件，或者修改Cordova工程中其它源码文件后，定位到<pre class="crayon-plain-tag">%MVN_PROJECT_ROOT%\cli</pre>目录，执行：<br />
<pre class="crayon-plain-tag">@echo off
call cordova prepare 
pushd platforms\android
call gradle syncdown
popd</pre></p>
<p>这会让所有修改从Cordova工程目录，同步到其Android平台子目录，然后再同步到Maven工程对应目录中。 </p>
</li>
</ol>
<div class="blog_h2"><span class="graybg">Android平台命令行工具</span></div>
<p>Android平台特定的命令行工具位于<pre class="crayon-plain-tag">platforms/android/cordova</pre> 子目录中，这些命令比Cordova CLI更有针对性，在以Android为中心的开发时，可以代替之：</p>
<pre class="crayon-plain-tag">###  构建工程  ###

#Apache Cordova CLI方式
cordova build android

#Android平台命令行工具
platforms/android/cordova/build --debug   #生成调试信息
platforms/android/cordova/build --release #对应用进行签名
#其它参数
# --ant 使用Ant进行构建
# --gradle 使用Gradle进行构建，Cordova 4.0以后的默认值
# --nobuild 不进行构建
# --prepenv 不进行构建，但是拷贝必要的构建脚本
# --versionCode=# 指定本次构建的版本号
# --minSdkVersion=# 指定本次构建要求的最低Android SDK版本代码
# --gradleArg=# 指定传递给Gradle的命令行参数
# --buildConfig 指定包含了构建配置的JSON文件


###  部署到模拟器  ###
# 需要首先配置并启动一台Android模拟器

#Apache Cordova CLI方式
cordova emulate android

#Android平台命令行工具
platforms/android/cordova/run --emulator
platforms/android/cordova/run --target=targetId #指定特定ID的模拟器
#当执行run时，默认会自动构建，可以通过参数-debug --release --nobuild控制此行为
platforms/android/cordova/run --emulator --nobuild

#通过Eclipse部署
#也可以在Eclipse中，Run As - Android Application方式部署应用



###  部署到真实设备  ###

#Apache Cordova CLI方式
cordova run android

#Android平台命令行工具
platforms/android/cordova/run --device



###  其他Android平台命令行工具  ###
platforms/android/cordova/log    #记录APP详细的运行日志
platforms/android/cordova/clean  #清理工程文件</pre>
<p>使用Android命令行build、run时，可以通过<pre class="crayon-plain-tag">--buildConfig</pre> 传入一个JSON，为构建提供配置信息。下面的JSON提供了APK签名所需的信息：</p>
<pre class="crayon-plain-tag">{
     "android": {
         "debug": {
             "keystore": "android.keystore",
             "storePassword": "android",
             "alias": "mykey1", 
             "password" : "password",
             "keystoreType": ""
         },
         "release": {
             "keystore": "android.keystore",
             "storePassword": "", //Release的密码可以不写，等待命令行提示
             "alias": "mykey2",
             "password" : "password",
             "keystoreType": ""
         }
     }
 }</pre>
<div class="blog_h2"><span class="graybg">通过Gradle执行构建</span></div>
<p>自4.0开始Gradle是Cordova默认的构建工具。</p>
<p>下表列出Cordova提供的<a href="/gradle-study-note#gradle-project-properties">Gradle工程属性</a>，可以用于定制构建： </p>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 35%; text-align: center;">属性</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>cdvBuildMultipleApks</td>
<td>
<p>默认值：false，表示为所有平台生成一个通用的大的APK</p>
<p>如果设置为true，对library projects支持的每个平台（x86, ARM...）分别生成一个APK。如果工程使用大的Native库时很重要，因为Native库可能大大增加APK体积</p>
</td>
</tr>
<tr>
<td>cdvVersionCode</td>
<td>覆盖AndroidManifest.xml中的versionCode</td>
</tr>
<tr>
<td>cdvReleaseSigningPropertiesFile</td>
<td>
<p>默认值：release-signing.properties</p>
<p>包含Release版本APK签名需信息的属性文件的名称，属性文件的内容示例如下：</p>
<pre class="crayon-plain-tag">storeFile=relative/path/to/keystore.p12
storePassword=SECRET1
storeType=pkcs12
keyAlias=DebugSigningKey
keyPassword=SECRET2
#密码是可选的，如果不提供，构建时会进行交互式的提示</pre>
</td>
</tr>
<tr>
<td>cdvDebugSigningPropertiesFile</td>
<td>
<p>默认值：debug-signing.properties
<p>包含Debug版本APK签名需信息的属性文件的名称</p>
</td>
</tr>
<tr>
<td>cdvBuildToolsVersion</td>
<td>覆盖自动检测到的android.buildToolsVersion属性值。该属性由Android的Gradle插件引入</td>
</tr>
<tr>
<td>cdvCompileSdkVersion</td>
<td>覆盖自动检测到的android.compileSdkVersion属性值。该属性由Android的Gradle插件引入</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">扩展Gradle构建脚本</span></div>
<p>如果需要扩展Gradle构建逻辑，可以创建一个<pre class="crayon-plain-tag">build-extras.gradle</pre> 文件，并在其中编写脚本。该脚本会自动被包含在<pre class="crayon-plain-tag">build.gradle</pre> 的<span style="background-color: #c0c0c0;">首部</span>。注意避免修改build.gradle本身，该文件是自动生成的，可能被Cordova覆盖。下面是扩展脚本的一个例子：</p>
<pre class="crayon-plain-tag"># 此文件的内容被放在build.gradle的开始处

# 设置工程扩展属性
ext.cdvDebugSigningPropertiesFile = 'android-debug-keys.properties'

# 这里指定的闭包，会在build.gradle结尾处执行， 因为build.gradle的最后包含如下代码：
# if (hasProperty('postBuildExtras')) {
#     postBuildExtras()
# }
ext.postBuildExtras = {
    android.buildTypes.debug.applicationIdSuffix = '.debug'
}</pre>
<div class="blog_h2"><span class="graybg">打包和部署</span></div>
<p>有两种方式来打包Cordova应用，并部署到Android设备或者模拟器上测试：</p>
<ol>
<li>使用Maven：<pre class="crayon-plain-tag">package android:deploy android:run</pre> </li>
<li>使用ADT：在apk工程上面点击右键 Run As ⇨ Android Application</li>
</ol>
<p>注意，必须让你的设备或者模拟器链接到当前机器上。</p>
<p>这两种方式都比较慢，每次部署都需要数十秒，非常浪费时间。因此可以使用PhoneGap提供的<a href="/phonegap-study-note#device-tool">设备端工具</a>，该工具可以免部署的运行Cordova应用。</p>
<div class="blog_h1"><span class="graybg">理解Cordova HelloWorld</span></div>
<div class="blog_h3"><span class="graybg">启动</span></div>
<p>应用启动后，会自动启用一个WebView，加载config.xml中指定的起始页（默认index.html），让HTML页面占据整个屏幕空间。</p>
<p>例如，对Android平台，系统会执行MainActivity，该Activity是CordovaActivity的子类：</p>
<pre class="crayon-plain-tag">public class MainActivity extends CordovaActivity
{
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        // 加载HTML
        loadUrl(launchUrl);
    }
}</pre>
<div class="blog_h3"><span class="graybg">HTML和JavaScript脚本</span></div>
<pre class="crayon-plain-tag">&lt;!DOCTYPE html&gt;
&lt;html&gt;
    &lt;head&gt;
        &lt;!-- 内容安全策略 --&gt;
        &lt;meta http-equiv="Content-Security-Policy" 
            content="default-src 'self' data: gap: https://ssl.gstatic.com 'unsafe-eval'; 
                     style-src 'self' 'unsafe-inline'; 
                     media-src *"&gt;
        &lt;!-- 禁止IOS的电话号码识别 --&gt;
        &lt;meta name="format-detection" content="telephone=no"&gt;
        &lt;!-- 禁止Windows Phone 8+的触碰时高亮 --&gt;
        &lt;meta name="msapplication-tap-highlight" content="no"&gt;
        &lt;!-- WARNING: for iOS 7, remove the width=device-width and height=device-height attributes --&gt;
        &lt;!-- 
            视口元标签：指定应用程序占用屏幕的多大空间，以及应用程序如何缩放（zoom） 
            minimum-scale、maximum-scale 最小/最大缩放比率 initial-scale 为初始缩放比率
            user-scalable 是否允许用户缩放
            width、height 界面的宽高
        --&gt;
        &lt;meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width"&gt;
        &lt;link rel="stylesheet" type="text/css" href="css/index.css"&gt;
        &lt;title&gt;Hello World&lt;/title&gt;
    &lt;/head&gt;
    &lt;body&gt;
        &lt;div class="app"&gt;
            &lt;h1&gt;Apache Cordova&lt;/h1&gt;
            &lt;div id="deviceready" class="blink"&gt;
                &lt;p class="event listening"&gt;Connecting to Device&lt;/p&gt;
                &lt;p class="event received"&gt;Emulater is Ready&lt;/p&gt;
            &lt;/div&gt;
        &lt;/div&gt;
        &lt;!-- 
            该JS是 Apache Cordova 提供的，用于访问设备硬件（例如摄像头、GPS）的JavaScript API
            该JS只是一个占位符，没有对应的文件。该脚本在运行时由PhoneGap Developer App注入；或者在构建时由Cordova CLI注入
        --&gt;
        &lt;script type="text/javascript" src="cordova.js"&gt;&lt;/script&gt;
        &lt;script type="text/javascript" src="js/index.js"&gt;&lt;/script&gt;
    &lt;/body&gt;
&lt;/html&gt;</pre><br />
<pre class="crayon-plain-tag">var app = {
    // 程序入口点
    initialize: function() {
        this.bindEvents();
    },
    // 绑定事件监听器，例如'load', 'deviceready', 'offline',  'online'
    bindEvents: function() {
        document.addEventListener('deviceready', this.onDeviceReady, false);
    },
    //事件deviceready是Cordova专有的，该事件发生后，Cordova提供的设备API加载完毕并可用
    onDeviceReady: function() {
        app.receivedEvent('deviceready');
    },
    // 修改DOM内容
    receivedEvent: function(id) {
        var parentElement = document.getElementById(id);
        var listeningElement = parentElement.querySelector('.listening');
        var receivedElement = parentElement.querySelector('.received');

        listeningElement.setAttribute('style', 'display:none;');
        receivedElement.setAttribute('style', 'display:block;');

        console.log('Received Event: ' + id);
    }
};

app.initialize();</pre>
<div class="blog_h1">开发与调试指南</div>
<div class="blog_h2"><span class="graybg">开发最佳实践</span></div>
<div class="blog_h3"><span class="graybg">单页面应用（SPA）</span></div>
<p>Cordova应该是基于单页面应用设计的。所谓单页面应用，是指不存在URL导航的Web应用。单页面引用仅在最初加载一次页面，后续的更新通过JavaScript、Ajax完成。</p>
<p>单页面应用有利于增强移动应用的性能，因为避免了<span style="background-color: #c0c0c0;">重复加载</span>各种资产文件（脚本、图片）。单页面应用对Cordova有额外的好处，因为Cordova应用必须<span style="background-color: #c0c0c0;">等待deviceready事件</span>，URL导航导致重复的事件等待。</p>
<div class="blog_h3"><span class="graybg">性能方面的考虑</span></div>
<p>Cordova开发者往往错误的假设，移动设备能够达到和桌面应用一样性能。实际上，尽管移动设备已经长足发展了近十年，在电池、性能方面，仍然无法和桌面应用比较。注意以下几个方面：</p>
<ol>
<li>click vs touch：click事件被大量误用。大部分移动设备添加300ms的延迟，用以区分touch和touch hold。使用touchstart、touchend可能有很大的性能改善——避免频繁的UI更新和其它行为</li>
<li>CSS 变换 vs DOM操控：硬件加速的CSS变换比起基于JavaScript的动画，性能有极大的优势</li>
<li>网络延迟：尽量使用Cordova支持的本地存储机制，避免反复通过网络获取数据、资源</li>
</ol>
<div class="blog_h3"><span class="graybg">识别并处理离线状态</span></div>
<p>使用Cordova提供的offline、online事件的支持，你可以识别出离线状态，并正确的响应用户。注意，这两种事件并不是非常可靠，有时候你应当使用Ajax来判断设备是否真正的断开网络了。</p>
<p>注意：苹果的商店拒绝不能正确处理离线状态的APP上架。</p>
<div class="blog_h3"><span class="graybg">数据存储方案</span></div>
<p>有几种存储方案可以供Apache Cordova应用使用：</p>
<ol>
<li>LocalStorage：同步的基于键值对的存储</li>
<li><a href="http://dev.w3.org/html5/webdatabase/">WebSQL</a>：基于SQL语句的API，支持Android、iOS</li>
<li>IndexedDB：支持Windows 8、Windows Phone 8</li>
<li>cordova-plugin-file插件：缓存数据到本地文件系统</li>
</ol>
<div class="blog_h2"><span class="graybg">测试Cordova应用</span></div>
<p>测试Cordova应用时，你可以使用真实设备、模拟器，甚至是桌面浏览器。但是，在多种真实设备上测试是很有必要的： </p>
<ol>
<li>模拟器（Simulator）不能模拟一些真实存在的情况：例如低电池时，应用会出现某个问题，这个问题在模拟器上难以体现</li>
<li>模拟器（Emulator）和真实设备的行为存在不一致：某些模拟器会导致页面显示混乱，但是在真实设备上没有这种问题。4.1.2的AVD，运行5.4版本Cordova的HelloWorld应用，就存在页面显示混乱的情况。你可能需要禁用Host GPU加速</li>
<li>Simulator往往比真实设备快很多，而Emulator则慢很多</li>
<li>在不同OS的设备上测试是有必要的，应用的行为可能不符合预期</li>
</ol>
<div class="blog_h3"><span class="graybg">PhoneGap Developer App</span></div>
<p>使用PGDA可以避免编译、打包、部署到设备的繁冗步骤，在开发阶段能够节约很多时间。</p>
<div class="blog_h2"><span class="graybg">调试Cordova应用</span></div>
<div class="blog_h3"><span class="graybg">iOS Debugging</span></div>
<p>使用XCode可以调试Cordova应用Native的那一部分。</p>
<div class="blog_h3"><span class="graybg">Safari Remote Debugging with Web Inspector</span></div>
<p>使用Safari的web inspector，你可以Debug Webview和Cordova应用的JavaScript代码，仅支持OS X和iOS6+。通过OS X中的Safari连接到设备（或模拟器），可以使用Safari的dev tools调试Cordova应用。</p>
<div class="blog_h3"><span class="graybg">Chrome Remote Debugging</span></div>
<p>与Safari类似，Chrome Dev Tools也可以用来<a href="https://developers.google.com/chrome/mobile/docs/debugging">调试Cordova应用</a>。需要<span style="background-color: #c0c0c0;">Android 4.4、API 19+、Chrome 30+</span>才可以进行调试。使用<a href="https://github.com/google/ios-webkit-debug-proxy/">WebKit proxy</a>，Chrome Dev Tools也可以调试iOS应用。</p>
<div class="blog_h3"><span class="graybg">Ripple</span></div>
<p><a href="http://ripple.incubator.apache.org/">Ripple</a>是一个基于Web的移动设备环境模拟器，用于快速开发基于Apache Cordova、Blackberry WebWorks等框架的移动Web应用。</p>
<div class="blog_h3"><span class="graybg">GapDebug</span></div>
<p>综合性的移动应用调试工具，支持在现代移动设备（iOS、Android）上调试混合应用，对PhoneGap、Cordova提供非常好的支持。</p>
<p>该工具实质上是对Chrome Dev Tools的包装和增强。</p>
<p>具体参见：<a href="/gapdebug">使用GapDebug调试Phonegap应用</a></p>
<div class="blog_h1"><span class="graybg">Cordova生命周期事件</span></div>
<p>除非特别说明所有事件均在<pre class="crayon-plain-tag">document</pre> 对象上发布。</p>
<table class=" fixed-word-wrap full-width">
<thead>
<tr>
<td style="width: 20%; text-align: center;">事件</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>deviceready</td>
<td>对任何Cordova应用程序都非常关键，该事件的发生意味着Cordova设备API已经加载完毕、并可以使用。用法示例：<br />
<pre class="crayon-plain-tag">document.addEventListener("deviceready", onDeviceReady, false);
function onDeviceReady() {
    // 现在可以安全的使用Cordova设备API了
}</pre>
</td>
</tr>
<tr>
<td>pause</td>
<td>当应用程序进入后台时，发布该事件<br />注意：在iOS中，此事件的监听器对Cordova API的调用、对alert、console.log的调用都被阻塞，直到resume发生时才执行。</td>
</tr>
<tr>
<td>resume</td>
<td>当应用程序从后台返回前台执行时，发布此事件<br />注意：在iOS中，resume的监听器中，对交互式函数（例如alert）的调用，必须异步执行，否则应用程序会挂起。下面是正确的代码示例：<br />
<pre class="crayon-plain-tag">document.addEventListener("resume", onResume, false);
function onResume() {
   setTimeout(function() {
       alert(1);
   }, 0);
}</pre>
</td>
</tr>
<tr>
<td>backbutton</td>
<td>当用户按下后退按钮时，发布此事件，可以覆盖后台按钮的默认行为<br />不支持iOS系统</td>
</tr>
<tr>
<td>menubutton</td>
<td>当用户按下菜单按钮时，发布此事件，可以覆盖菜单按钮的默认行为<br />不支持iOS系统</td>
</tr>
<tr>
<td>searchbutton</td>
<td>当用户按下搜索按钮时，发布此事件，可以覆盖搜索按钮的默认行为<br />仅支持Android系统 </td>
</tr>
<tr>
<td>volumedownbutton</td>
<td>当用户按下减小音量按钮时，发布此事件，仅支持Android、BlackBerry 10</td>
</tr>
<tr>
<td>volumeupbutton</td>
<td>当用户按下增大音量按钮时，发布此事件，仅支持Android、BlackBerry 10</td>
</tr>
<tr>
<td style="text-align: center;" colspan="2"><strong>插件cordova-plugin-battery-status引入的事件</strong></td>
</tr>
<tr>
<td>batterycritical</td>
<td rowspan="3">
<p>这些事件都在window对象上发布。分别在关键电量、低电量、电池状态变化时（增减超过1%、或者插上电源、拔下电源）时发布。可以访问事件对象的如下属性：</p>
<ol>
<li>level，电量百分比数值，0-100</li>
<li>isPlugged，布尔型，判断是否插入电源</li>
</ol>
</td>
</tr>
<tr>
<td>batterylow</td>
</tr>
<tr>
<td>batterystatus</td>
</tr>
<tr>
<td style="text-align: center;" colspan="2"><strong>插件cordova-plugin-network-information引入的事件</strong></td>
</tr>
<tr>
<td>online</td>
<td rowspan="2"> 当设备上线（连接到互联网）时，以及设备下线时，发布对应的事件</td>
</tr>
<tr>
<td>offline</td>
</tr>
</tbody>
</table>
<div class="blog_h1"><span class="graybg">Cordova插件API</span></div>
<p>Cordova自带了一个最小化的API集合，工程可以通过插件添加额外的API。本节介绍Cordova核心插件的API。</p>
<p>本节中，使用样式区分不同的API类型：</p>
<ol>
<li>斜体：表示静态（static）成员</li>
<li>下划线标注：类型定义（typedef）</li>
<li>括号后缀：表示方法/函数</li>
</ol>
<div class="blog_h3"><span class="graybg">cordova-plugin-battery-status</span></div>
<p>提供三个电池相关的事件，如上节所述</p>
<div class="blog_h3"><span class="graybg">cordova-plugin-camera </span></div>
<p>该插件定义了一个全局的<pre class="crayon-plain-tag">navigator.camera</pre> 对象，用于拍摄照片、从系统的图片库选择照片。该插件常用API如下表：</p>
<table class=" fixed-word-wrap full-width">
<thead>
<tr>
<td style="width: 15%; text-align: center;">所有者</td>
<td style="width: 20%; text-align: center;">方法/属性</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td rowspan="5">navigator.<br />camera</td>
<td><em>getPicture()</em></td>
<td>
<pre class="crayon-plain-tag">camera.getPicture(
    onSuccess successCallback, 
    onError errorCallback, 
    CameraOptions options
)</pre></p>
<p>使用摄像头拍照，或者从设备的图片库中获取照片。如果<br /><pre class="crayon-plain-tag">options.sourceType == Camera.PictureSourceType.</pre> ：</p>
<ol>
<li><span style="color: #333333; font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; font-size: small;"><span style="line-height: 22px;">CAMERA（默认）：打开摄像头并拍照，一旦拍照完毕，则关闭默认照相程序，并返回到当前应用</span></span></li>
<li>PHOTOLIBRARY、SAVEDPHOTOALBUM：显示一个对话框，让用户选择一个既有的照片。此时当前函数返回一个CameraPopoverHandle，用于重新定位图片选择对话框（例如设备的方向改变时）</li>
</ol>
<p>图片通过Base64编码或者URI的方式（默认）传递给<pre class="crayon-plain-tag">successCallback</pre> 。如果<br /><pre class="crayon-plain-tag">options.destinationType == Camera.DestinationType.</pre> ：</p>
<ol>
<li>FILE_URI（默认）：返回文件URI，对于Android返回<pre class="crayon-plain-tag">content://</pre> 形式的URI。这是最好的方式，因为现代设备的拍的照片、图片库中的照片的质量可能很高，而此插件没有对其进行缩小、压缩处理，因此使用DATA_URL可能导致内存方面的问题</li>
<li>NATIVE_URI：返回Native的URI</li>
<li>DATA_URL：返回Base64编码的字符串</li>
</ol>
<p>得到图片URI或者Base64编码后，你可以：</p>
<ol>
<li>使用<pre class="crayon-plain-tag">&lt;img&gt;</pre> 渲染到当前页面</li>
<li>存储到本地</li>
<li>发送到远程服务器</li>
</ol>
</td>
</tr>
<tr>
<td><em>cleanup()</em></td>
<td>清理图片临时文件，仅当options.sourceType为CAMERA、options.destinationType为FILE_URI时需要调用<br />仅支持iOS</td>
</tr>
<tr>
<td><span style="text-decoration: underline;"><em>onError()</em></span></td>
<td>获取照片的错误回调，入参：
<ol>
<li><span style="color: #333333; font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; font-size: 13px; line-height: 22px;">sring </span><span style="color: #333333; font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; font-size: 13px; line-height: 22px;">message：来自设备Native代码的错误信息</span></li>
</ol>
</td>
</tr>
<tr>
<td><em><span style="text-decoration: underline;">onSuccess()</span></em></td>
<td>获取照片的成功回调，入参：
<ol>
<li><span style="color: #333333; font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; font-size: 13px; line-height: 22px;">sring imageData：Base64图片数据或者URI</span></li>
</ol>
</td>
</tr>
<tr>
<td><em><span style="text-decoration: underline;">CameraOptions</span></em></td>
<td>
<p>用于指定摄像头参数的对象，包含以下属性：</p>
<ol>
<li>quality：图片质量，0-100，100表示全分辨率+无损压缩。注意摄像头的分辨率无法得知</li>
<li>destinationType、sourceType：参考getPicture()方法的介绍</li>
<li>allowEdit：在选择前，允许简单的编辑照片</li>
<li>encodingType：图像编码方式，默认Camera.EncodingType.JPEG</li>
<li>targetWidth、targetHeight：目标图片的尺寸，纵横比保持不变</li>
<li>mediaType：当sourceType为PHOTOLIBRARY 或SAVEDPHOTOALBUM时，指定媒体类型，默认Camera.MediaType.PICTURE</li>
<li>correctOrientation：修正照片的方向</li>
<li>saveToPhotoAlbum：在拍照后，保存照片到设备相册</li>
<li>popoverOptions：仅用于iOS的iPad，用来指定popover的位置</li>
<li>cameraDirection：使用前摄像头还是后摄像头，默认Camera.Direction.BACK</li>
</ol>
</td>
</tr>
<tr>
<td rowspan="5">Camera   </td>
<td>enum<br />DestinationType</td>
<td>图片目标类型，可选值：<br />DATA_URL<br />FILE_URI<br />NATIVE_URI</td>
</tr>
<tr>
<td>enum<br />EncodingType</td>
<td>图像编码方式，可选值：<br />JPEG<br />PNG </td>
</tr>
<tr>
<td>enum<br />MediaType</td>
<td>媒体类型，可选值： <br />PICTURE：仅选择图片<br />VIDEO：仅选择视频，对于视频，DestinationType只能时URI<br />ALLMEDIA：同时选择图片和视频</td>
</tr>
<tr>
<td>enum<br />PictureSourceType</td>
<td>图片来源类型，可选值：<br />PHOTOLIBRARY：图片库，对于Android和SAVEDPHOTOALBUM一样<br />CAMERA：即时拍照<br />SAVEDPHOTOALBUM：图片库，对于Android和SAVEDPHOTOALBUM一样</td>
</tr>
<tr>
<td>enum<br />Direction</td>
<td>使用哪个摄像头，可选值：BACK、FRONT</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">cordova-plugin-console</span></div>
<p>该插件确保<pre class="crayon-plain-tag">console.log()</pre> 能够正常工作，为非Android平台提供额外的函数支持。该插件定义了一个全局的<pre class="crayon-plain-tag">console</pre> 对象，并引入以下方法：</p>
<table class=" fixed-word-wrap full-width">
<thead>
<tr>
<td style="width: 25%; text-align: center;">方法</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>log</td>
<td style="vertical-align: top;" rowspan="12">这些方法支持C语言printf风格的字符串格式化：
<ol>
<li><span style="color: #333333; font-family: Georgia, 'Times New Roman', 'Bitstream Charter', Times, serif; font-size: small;"><span style="line-height: 22px;">%j 格式化为JSON</span></span></li>
<li>%o 格式化为JSON</li>
<li>%c 格式化为''</li>
<li>%% 转义%</li>
<li>%* 其他均使用目标参数的<pre class="crayon-plain-tag">toString()</pre> 方法进行打印</li>
</ol>
</td>
</tr>
<tr>
<td>error</td>
</tr>
<tr>
<td>exception</td>
</tr>
<tr>
<td>warn</td>
</tr>
<tr>
<td>info</td>
</tr>
<tr>
<td>debug</td>
</tr>
<tr>
<td>assert</td>
</tr>
<tr>
<td>dir</td>
</tr>
<tr>
<td>dirxml</td>
</tr>
<tr>
<td>time</td>
</tr>
<tr>
<td>timeEnd</td>
</tr>
<tr>
<td>table</td>
</tr>
</tbody>
</table>
<div class="blog_h1">Cordova配置文件：config.xml</div>
<p>位于Cordova工程根目录的配置文件config.xml，可以在全局上控制Cordova应用程序的行为：</p>
<ol>
<li>指定核心Cordova API特性</li>
<li>启用Cordova插件</li>
<li>进行平台相关的设置</li>
</ol>
<p>config.xml指定的配置会影响所有应用程序和CordovaWebView实例。</p>
<p>当使用Cordova的命令行接口（CLI）构建工程时，该文件会被修改并拷贝到各平台的对应目录，例如<pre class="crayon-plain-tag">platforms/ios/AppName/config.xml</pre> </p>
<div class="blog_h2"><span class="graybg">全局与跨平台配置选项</span></div>
<pre class="crayon-plain-tag">&lt;?xml version='1.0' encoding='utf-8'?&gt;
&lt;!-- widget元素： id 当前应用程序的唯一标识，通常是域名倒写，在Android中对应了包名 version 主版本.次版本.补丁版本 --&gt;
&lt;widget id="cc.gmem.study.cordova" version="0.0.1" xmlns="http://www.w3.org/ns/widgets" xmlns:cdv="http://cordova.apache.org/ns/1.0"&gt;
    &lt;!-- 应用程序的正式名称，该名称会出现在设备的主屏幕，以及应用商店 --&gt;
    &lt;name&gt;CordovaStudy&lt;/name&gt;
    &lt;!-- 应用程序描述和作者信息，会显示在应用商店 --&gt;
    &lt;description&gt;
        A sample Apache Cordova application that responds to the deviceready event.
    &lt;/description&gt;
    &lt;author email="dev@cordova.apache.org" href="http://cordova.io"&gt;
        Apache Cordova Team
    &lt;/author&gt;
    &lt;!-- 指定应用的起始网页 --&gt;
    &lt;content src="index.html" /&gt;
    &lt;!-- 指定应用使用的Cordova插件 --&gt;
    &lt;plugin name="cordova-plugin-whitelist" spec="1" /&gt;
    &lt;!-- 指定应用可以和哪些外部域名进行交互，默认值允许与任何外部服务交互 --&gt;
    &lt;access origin="*" /&gt;
    &lt;!-- Intent白名单 --&gt;
    &lt;allow-intent href="http://*/*" /&gt;
    &lt;allow-intent href="https://*/*" /&gt;
    &lt;allow-intent href="tel:*" /&gt;
    &lt;allow-intent href="sms:*" /&gt;
    &lt;allow-intent href="mailto:*" /&gt;
    &lt;allow-intent href="geo:*" /&gt;
    &lt;!-- platform元素只会拷贝到对应平台下的config.xml中 --&gt;
    &lt;platform name="android"&gt;
        &lt;allow-intent href="market:*" /&gt;
        &lt;!-- 平台特定的方向设置 --&gt;
        &lt;preference name="Orientation" value="sensorLandscape" /&gt;
    &lt;/platform&gt;
    &lt;platform name="ios"&gt;
        &lt;allow-intent href="itms:*" /&gt;
        &lt;allow-intent href="itms-apps:*" /&gt;
        &lt;!-- IOS：同时设置portrait、landscape模式 --&gt;
        &lt;preference name="Orientation" value="all" /&gt;
    &lt;/platform&gt;
    &lt;!-- 以键值对的形式定义若干选项，键大小写不敏感 --&gt;
    &lt;preference&gt;
        &lt;!-- 是否隐藏设备顶部的状态栏 --&gt;
        &lt;preference name="Fullscreen" value="false" /&gt;
        &lt;!-- 支持Android、IOS。是否禁止拖拽滚动超过顶部或者底部，如果不需要“下拉以刷新”之类的功能，可以禁止 --&gt;
        &lt;preference name="DisallowOverscroll" value="false" /&gt;
        &lt;!-- 支持Android、Blackberry。应用的背景色 --&gt;
        &lt;preference name="BackgroundColor" value="0xff0000ff" /&gt;
        &lt;!-- 支持Blackberry。隐藏键盘上方额外的工具栏 --&gt;
        &lt;preference name="HideKeyboardFormAccessoryBar" value="false" /&gt;
        &lt;!-- 锁定方向，禁止响应设备的转动。可选值landscape、portrait、default --&gt;
        &lt;preference name="Orientation" value="default" /&gt;
    &lt;/preference&gt;
    &lt;!-- 
        如果使用CLI，添加的插件不会反应到feature元素，但是如果使用针对特定平台的config.xml，
        可以设置feature元素，以启用设备级别的API或者外部插件
    --&gt;
    &lt;!-- 在Android平台上启用Device API --&gt;
    &lt;feature name="Device"&gt;
        &lt;param name="android-package" value="org.apache.cordova.device.Device" /&gt;
    &lt;/feature&gt;
    &lt;!-- 在IOS平台上启用Device API --&gt;
    &lt;feature name="Device"&gt;
        &lt;param name="ios-package" value="CDVDevice" /&gt;
    &lt;/feature&gt;
    &lt;!-- 添加钩子，以扩展Cordova的功能 --&gt;
    &lt;hook type="after_plugin_install" src="scripts/afterPluginInstall.js" /&gt;
    
    &lt;!-- 指定应用程序图标，platform/width/height/density都是可选的，density为Android专有 --&gt;
    &lt;icon src="res/ios/icon.png" platform="ios" width="57" height="57" density="mdpi" /&gt;
    &lt;!-- 可以采用平台专有的方式指定图标 --&gt;
    &lt;platform name="android"&gt;
        &lt;icon src="res/android/ldpi.png" density="ldpi" /&gt;
        &lt;icon src="res/android/mdpi.png" density="mdpi" /&gt;
        &lt;icon src="res/android/hdpi.png" density="hdpi" /&gt;
        &lt;icon src="res/android/xhdpi.png" density="xhdpi" /&gt;
     &lt;/platform&gt;
     &lt;platform name="ios"&gt;
        &lt;!-- iOS 8.0+ --&gt;
        &lt;!-- iPhone 6 Plus  --&gt;
        &lt;icon src="res/ios/icon-60@3x.png" width="180" height="180" /&gt;
        &lt;!-- iOS 7.0+ --&gt;
        &lt;!-- iPhone / iPod Touch  --&gt;
        &lt;icon src="res/ios/icon-60.png" width="60" height="60" /&gt;
        &lt;icon src="res/ios/icon-60@2x.png" width="120" height="120" /&gt;
        &lt;!-- iPad --&gt;
        &lt;icon src="res/ios/icon-76.png" width="76" height="76" /&gt;
        &lt;icon src="res/ios/icon-76@2x.png" width="152" height="152" /&gt;
        &lt;!-- iOS 6.1 --&gt;
        &lt;!-- Spotlight Icon --&gt;
        &lt;icon src="res/ios/icon-40.png" width="40" height="40" /&gt;
        &lt;icon src="res/ios/icon-40@2x.png" width="80" height="80" /&gt;
        &lt;!-- iPhone / iPod Touch --&gt;
        &lt;icon src="res/ios/icon.png" width="57" height="57" /&gt;
        &lt;icon src="res/ios/icon@2x.png" width="114" height="114" /&gt;
        &lt;!-- iPad --&gt;
        &lt;icon src="res/ios/icon-72.png" width="72" height="72" /&gt;
        &lt;icon src="res/ios/icon-72@2x.png" width="144" height="144" /&gt;
        &lt;!-- iPhone Spotlight and Settings Icon --&gt;
        &lt;icon src="res/ios/icon-small.png" width="29" height="29" /&gt;
        &lt;icon src="res/ios/icon-small@2x.png" width="58" height="58" /&gt;
        &lt;!-- iPad Spotlight and Settings Icon --&gt;
        &lt;icon src="res/ios/icon-50.png" width="50" height="50" /&gt;
        &lt;icon src="res/ios/icon-50@2x.png" width="100" height="100" /&gt;
     &lt;/platform&gt;
     
     &lt;!-- 指定启动画面，也可用使用Splashscreen插件，动态编程控制 --&gt;
     &lt;platform name="android"&gt;
        &lt;!-- you can use any density that exists in the Android project --&gt;
        &lt;splash src="res/screen/android/splash-land-hdpi.png" density="land-hdpi"/&gt;
        &lt;splash src="res/screen/android/splash-land-ldpi.png" density="land-ldpi"/&gt;
        &lt;splash src="res/screen/android/splash-land-mdpi.png" density="land-mdpi"/&gt;
        &lt;splash src="res/screen/android/splash-land-xhdpi.png" density="land-xhdpi"/&gt;

        &lt;splash src="res/screen/android/splash-port-hdpi.png" density="port-hdpi"/&gt;
        &lt;splash src="res/screen/android/splash-port-ldpi.png" density="port-ldpi"/&gt;
        &lt;splash src="res/screen/android/splash-port-mdpi.png" density="port-mdpi"/&gt;
        &lt;splash src="res/screen/android/splash-port-xhdpi.png" density="port-xhdpi"/&gt;
    &lt;/platform&gt;

    &lt;platform name="ios"&gt;
        &lt;!-- images are determined by width and height. The following are supported --&gt;
        &lt;splash src="res/screen/ios/Default~iphone.png" width="320" height="480"/&gt;
        &lt;splash src="res/screen/ios/Default@2x~iphone.png" width="640" height="960"/&gt;
        &lt;splash src="res/screen/ios/Default-Portrait~ipad.png" width="768" height="1024"/&gt;
        &lt;splash src="res/screen/ios/Default-Portrait@2x~ipad.png" width="1536" height="2048"/&gt;
        &lt;splash src="res/screen/ios/Default-Landscape~ipad.png" width="1024" height="768"/&gt;
        &lt;splash src="res/screen/ios/Default-Landscape@2x~ipad.png" width="2048" height="1536"/&gt;
        &lt;splash src="res/screen/ios/Default-568h@2x~iphone.png" width="640" height="1136"/&gt;
        &lt;splash src="res/screen/ios/Default-667h.png" width="750" height="1334"/&gt;
        &lt;splash src="res/screen/ios/Default-736h.png" width="1242" height="2208"/&gt;
        &lt;splash src="res/screen/ios/Default-Landscape-736h.png" width="2208" height="1242"/&gt;
    &lt;/platform&gt;
&lt;/widget&gt;</pre>
<div class="blog_h2"><span class="graybg">安卓配置选项</span></div>
<pre class="crayon-plain-tag">&lt;!-- 
    应用程序是否保持后台运行，甚至在pause事件发生之后 
    设置为fasle不会在pause事件后终止应用，而是仅仅当应用位于后台时，暂停CordovaWebView中代码的执行
--&gt;
&lt;preference name="KeepRunning" value="true"/&gt;
&lt;!-- 加载页面超时的毫秒数 --&gt;
&lt;preference name="LoadUrlTimeoutValue" value="20000"/&gt;
&lt;!-- 启动屏幕，位于res/drawable-*中的资源名称，不带扩展名 --&gt;
&lt;preference name="SplashScreen" value="splash"/&gt;
&lt;!-- 启动屏幕图片显示的毫秒数 --&gt;
&lt;preference name="SplashScreenDelay" value="3000"/&gt;
&lt;!-- 控制通过InAppBrowser打开的页面，是否可以和通过默认浏览器打开的页面访问同一localStorage和WebSQL --&gt;
&lt;preference name="InAppBrowserStorageEnabled" value="true"/&gt;
&lt;!-- 默认为null，如果设置，那么在加载第一个页面时，显示指定的标题、消息以及一个spinner。标题和消息使用逗号分隔 --&gt;
&lt;preference name="LoadingDialog" value="My Title,My Message"/&gt;
&lt;!-- 默认为null，与上一个配置类似，但是用于除了第一个页面的所有其它页面 --&gt;
&lt;preference name="LoadingPageDialog" value="My Title,My Message"/&gt;
&lt;!-- 默认为null，出错时显示的页面 --&gt;
&lt;preference name="ErrorUrl" value="myErrorPage.html"/&gt;
&lt;!-- 是否在屏幕顶部显示标题 --&gt;
&lt;preference name="ShowTitle" value="false"/&gt;
&lt;!-- 设置应用的日志级别：ERROR, WARN, INFO, DEBUG, VERBOSE --&gt;
&lt;preference name="LogLevel" value="ERROR"/&gt;
&lt;!-- 设置Activity的android:launchMode属性 --&gt;
&lt;preference name="AndroidLaunchMode" value="singleTop"/&gt;
&lt;!-- 设置User Agent，默认不设置 --&gt;
&lt;preference name="OverrideUserAgent" value="Mozilla/5.0 My Browser" /&gt;</pre>
<div class="blog_h1">指定平台或插件的版本</div>
<p>从Cordova4.3开始，支持保存（save ）、恢复（restore ）平台支持版本、插件版本信息。</p>
<p>当使用<pre class="crayon-plain-tag">--save</pre> 选项调用<pre class="crayon-plain-tag">cordova platform</pre> 、<pre class="crayon-plain-tag">cordova plugin</pre> 命令时，会自动把版本信息存放到config.xml中。执行<pre class="crayon-plain-tag">cordova prepare</pre> 时，会从config.xml中读取版本信息，并同步相应文件到platforms子目录中。</p>
<div class="blog_h3"><span class="graybg">保存平台版本</span></div>
<p>执行下面的命令，即可保存特定版本的平台：</p>
<pre class="crayon-plain-tag"># 命令格式
cordova platform add &lt;platform[@&lt;version&gt;] | directory | git_url&gt; --save
# 命令举例
cordova platform add android --save
cordova platform add android@3.7.0 --save    #后续不带@version添加平台，自动使用 3.7.0版本
cordova platform add android@https://github.com/apache/cordova-android.git​ --save
cordova platform add C:/path/to/android/platform --save

# 保存所有平台的版本
cordova platform save</pre>
<p>运行此命令后，config.xml中出现类似下面的条目：</p>
<pre class="crayon-plain-tag">&lt;engine name="android" spec="~4.0.0" /&gt;</pre>
<p>执行下面的命令，可以更新特定平台的版本： </p>
<pre class="crayon-plain-tag"># 命令格式
cordova platform update &lt;platform[@&lt;version&gt;] | directory | git_url&gt; --save

# 命令举例
cordova platform update android --save
cordova platform update android@3.8.0 --save
cordova platform update /path/to/android/platform --save</pre>
<div class="blog_h3"><span class="graybg">保存插件版本</span></div>
<p>执行下面的命令，即可保存插件版本到config.xml：</p>
<pre class="crayon-plain-tag"># 命令格式
cordova plugin add &lt;plugin[@&lt;version&gt;] | directory | git_url&gt; --save

# 命令举例
cordova plugin add cordova-plugin-console --save
cordova plugin add cordova-plugin-console@0.2.13 --save     #后续不带@version添加此插件，自动使用0.2.13版本
cordova plugin add https://github.com/apache/cordova-plugin-console.git --save
cordova plugin add C:/path/to/console/plugin --save

# 保存所有插件的版本
cordova plugin save</pre>
<p>运行此命令后，config.xml中出现类似下面的条目：</p>
<pre class="crayon-plain-tag">&lt;plugin name="cordova-plugin-console" spec="~1.0.0" /&gt;</pre>
<p>执行下面的命令，可以更新特定插件的版本： </p>
<pre class="crayon-plain-tag"># 命令格式
cordova plugin update &lt;plugin[@&lt;version&gt;] | directory | git_url&gt; --save

# 命令举例
cordova plugin update cordova-plugin-console --save
cordova plugin update cordova-plugin-console@0.2.13 --save
cordova plugin update /path/to/console/plugin --save</pre>
<div class="blog_h1"><span class="graybg"> 隐私与安全</span></div>
<div class="blog_h2"><span class="graybg">隐私指南</span></div>
<p>移动应用的隐私问题是每个移动App都需要关注的内容，用户需要它们的私人信息被合理使用，并不泄漏。下面是一些被广泛接受的建议：</p>
<ol>
<li>隐私策略（Privacy Policy）：使用平白的语言声明你的App的隐私策略</li>
<li>敏感信息的收集：在获取敏感信息，例如通讯录、地理位置、摄像机信息前，获得用户的许可</li>
<li>与第三方共享敏感信息：如果你的App获取的信息需要与第三方网站/应用共享，应当获得用户许可</li>
<li>仅在必要的时候才收集隐私信息</li>
</ol>
<div class="blog_h2"><span class="graybg">安全指南</span></div>
<p>本节包含一些开发Cordova应用时，处理安全问题的最佳实践。</p>
<ol>
<li>不要使用自签名的网站证书：如果你的服务器使用SSL，务必使用知名CA签发的数字证书</li>
<li>避免使用IFrame引用外部网页（不受你控制的第三方网站）：位于白名单中的网页，如果运行在IFrame中，它是可以访问Native Cordova Bridge的。引用外部链接，最好使用<span style="background-color: #c0c0c0;">InAppBrowser</span>，外部链接不会有机会执行Cordova API</li>
<li>关于域名白名单：<span style="background-color: #c0c0c0;">Android API 10以下</span>、<span style="background-color: #c0c0c0;">WP8中的IFrame和XMLHttpRequest</span>，都不受域名白名单控制，这意味着任何网页都可以调用Cordova接口</li>
<li>不要缓存敏感信息，例如用户名、密码</li>
<li>避免使用<pre class="crayon-plain-tag">eval()</pre> ，除非你确切知道脚本的内容</li>
</ol>
<div class="blog_h3"><span class="graybg">域名白名单配置</span></div>
<p>域名白名单（Domain whitelisting）机制用来控制Cordova应用对外部域名的访问。默认的，新创建的App<span style="background-color: #c0c0c0;">被允许访问所有外部域名</span>，在产品发布时，你必须确定需要访问哪些域名，并进行白名单配置。</p>
<p>对于Android和iOS，上述安全策略通过插件cordova-plugin-whitelist实现，具有更好的安全性和可配置性，推荐使用：</p>
<pre class="crayon-plain-tag">&lt;!-- 
    导航白名单（Navigation Whitelist）
    控制WebView可以被导航到那些URL，仅应用到顶级导航 
--&gt;
&lt;!-- 允许连接到google.com--&gt;
&lt;allow-navigation href="http://google.com/*" /&gt;
&lt;!-- 允许使用HTTP/HTTPS或者其它任何协议连接到google.com的所有子域 --&gt;
&lt;allow-navigation href="*://*.google.com/*" /&gt;
&lt;!-- 允许导航到任意网站--&gt;
&lt;allow-navigation href="*" /&gt;
&lt;!-- 上面一条等价于这三条：--&gt;
&lt;allow-navigation href="http://*/*" /&gt;
&lt;allow-navigation href="https://*/*" /&gt;
&lt;allow-navigation href="data:*" /&gt;


&lt;!-- 
    Intent白名单（Intent Whitelist）
    控制App可以要求系统打开哪些URL，默认任何URL都不允许 
--&gt;
&lt;!-- 允许在一个浏览器窗口中打开网页 --&gt;
&lt;allow-intent href="http://*/*" /&gt;
&lt;allow-intent href="https://*/*" /&gt;
&lt;allow-intent href="http://google.com/*" /&gt;
&lt;allow-intent href="*://*.example.com/*" /&gt;
&lt;!-- 允许在短信应用中打开SMS链接 --&gt;
&lt;allow-intent href="sms:*" /&gt;
&lt;!-- 允许拨打电话 --&gt;
&lt;allow-intent href="tel:*" /&gt;
&lt;!-- 允许打开地图 --&gt;
&lt;allow-intent href="geo:*" /&gt;
&lt;!-- 允许使用对应的App打开所有其它类型的URL --&gt;
&lt;allow-intent href="*" /&gt;


&lt;!-- 
    网络请求白名单（Network Request Whitelist）
    控制哪些网络请求被允许：Ajax、images等
    注意，最好使用Content Security Policy（CSP），CSP更安全。这节的配置主要用于不支持CSP的老旧Webview
    默认Android允许访问https://ssl.gstatic.com/accessibility/javascript/android/，这确保TalkBack正常工作
--&gt;
&lt;!-- 允许对google.com执行Ajax、图片请求 --&gt;
&lt;access origin="http://*.google.com" /&gt;
&lt;!-- 允许针对content URL的请求 --&gt;
&lt;access origin="content:///*" /&gt;
&lt;!-- 允许任何请求 --&gt;
&lt;access origin="*" /&gt;</pre>
<p>对于其它平台，Cordova应用依赖于config.xml中的<pre class="crayon-plain-tag">&lt;access&gt;</pre> 元素进行白名单设置：</p>
<pre class="crayon-plain-tag">&lt;!-- 允许访问google.com --&gt;
&lt;access origin="http://google.com" /&gt;
&lt;!-- 允许访问HTTPS版本的google.com --&gt;
&lt;access origin="https://google.com" /&gt;
&lt;!-- 允许访问maps.google.com --&gt;
&lt;access origin="http://maps.google.com" /&gt;
&lt;!-- 允许访问google.com的所有子域名 --&gt;
&lt;access origin="http://*.google.com" /&gt;
&lt;!-- 允许访问所有网站 --&gt;
&lt;access origin="*" /&gt;</pre>
<div class="blog_h3"><span class="graybg">内容安全策略（Content Security Policy）</span></div>
<p>参见：<a href="/html-faq#csp">HTML知识集锦</a></p>
<p>CSP用于控制图片、Ajax、<pre class="crayon-plain-tag">&lt;video&gt;</pre> 、WebSocket等所有请求。上面的whitelist插件的network request whitelist，<span style="background-color: #c0c0c0;">只能控制</span>图片、Ajax，因此有必要在HTML的<pre class="crayon-plain-tag">&lt;meta&gt;</pre> 配置CSP：</p>
<pre class="crayon-plain-tag">&lt;!-- 较为安全的默认值 --&gt;
&lt;meta http-equiv="Content-Security-Policy"
    content="default-src 'self' data: gap: https://ssl.gstatic.com; style-src 'self' 'unsafe-inline'; media-src *"&gt;

&lt;!-- 允许同源和google.com的所有内容--&gt;
&lt;meta http-equiv="Content-Security-Policy" content="default-src 'self' google.com"&gt;

&lt;!-- 
    允许所有内容，除了：
    对于CSS，仅允许同源的CSS文件，以及内联的样式
    对于脚本，仅允许同源的JS文件，内联的JavaScript脚本，以及eval()
--&gt;
&lt;meta http-equiv="Content-Security-Policy"
    content="default-src *; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-inline' 'unsafe-eval'"&gt;

&lt;!-- 仅允许同源的、通过HTTPS的Ajax请求 --&gt;
&lt;meta http-equiv="Content-Security-Policy" content="default-src 'self' https:"&gt;

&lt;!-- 允许来自google.com的IFrame --&gt;
&lt;meta http-equiv="Content-Security-Policy" content="default-src 'self'; frame-src 'self' https://google.com"&gt;</pre>
<div class="blog_h1"><span class="graybg">Cordova Hooks</span></div>
<p>钩子是Cordova Application、Plugin添加的特殊脚本，用于<span style="background-color: #c0c0c0;">定制化Cordova的命令</span>。可以在hooks目录添加钩子，也可以在config.xml、plugin.xml中声明钩子。钩子按照下面的<span style="background-color: #c0c0c0;">顺序串行执行</span>：</p>
<ol>
<li>来自/hooks目录的Application钩子</li>
<li>来自config.xml的Application钩子</li>
<li>来自plugins/.../plugin.xml的插件钩子</li>
</ol>
<p>目前支持的钩子类型有：</p>
<ol>
<li>after_build</li>
<li>after_compile</li>
<li>after_clean</li>
<li>after_docs</li>
<li>after_emulate</li>
<li>after_platform_add</li>
<li>after_platform_rm</li>
<li>after_platform_ls</li>
<li>after_plugin_add</li>
<li>after_plugin_ls</li>
<li>after_plugin_rm</li>
<li>after_plugin_search</li>
<li>after_plugin_install  仅执行被安装插件声明在plugin.xml中的钩子</li>
<li>after_prepare</li>
<li>after_run</li>
<li>after_serve</li>
<li>before_build</li>
<li>before_clean</li>
<li>before_compile</li>
<li>before_docs</li>
<li>before_emulate</li>
<li>before_platform_add</li>
<li>before_platform_rm</li>
<li>before_platform_ls</li>
<li>before_plugin_add</li>
<li>before_plugin_ls</li>
<li>before_plugin_rm</li>
<li>before_plugin_search</li>
<li>before_plugin_install  仅执行被安装插件声明在plugin.xml中的钩子</li>
<li>before_plugin_uninstall  仅执行被卸载插件声明在plugin.xml中的钩子</li>
<li>before_prepare</li>
<li>before_run</li>
<li>before_serve</li>
<li>pre_package 仅支持Windows、Windows Phone平台</li>
</ol>
<div class="blog_h3"><span class="graybg">在何处存放钩子</span></div>
<p>如果把钩子存放在hooks目录，必须遵从严格的命名规定：<pre class="crayon-plain-tag">hooks/钩子类型/脚本文件.js</pre> 。例如，如果你需要在prepare命令执行完毕后，附加一些行为，可以编写脚本：<pre class="crayon-plain-tag">hooks/after_prepare/cordova-syncdown.js</pre> 。应避免使用hooks目录，未来的Cordova版本可能废弃这种方式。</p>
<p>在config.xml、plugin.xml中声明钩子的方式如下：</p>
<pre class="crayon-plain-tag">&lt;hook type="before_build" src="scripts/appBeforeBuild.bat" /&gt;
&lt;hook type="before_build" src="scripts/appBeforeBuild.js" /&gt;
&lt;hook type="before_plugin_install" src="scripts/appBeforePluginInstall.js" /&gt;</pre>
<div class="blog_h3"><span class="graybg">钩子编程接口</span></div>
<p>一般使用NodeJS来编写钩子，Node模块定义的方式如下：</p>
<pre class="crayon-plain-tag">module.exports = function( ctx )
{
}</pre>
<p>其中ctx包含很多上下文信息： </p>
<pre class="crayon-plain-tag">{
  "hook": "after_prepare",
  "scriptLocation": "after_prepare/script.js",
  "cmdLine": "/path/to/cordova arguments",
  "opts": {
    "projectRoot":"/path/to/project",
    "cordova": {
      "platforms": ["android"],
      "plugins": ["com.plugin.withhooks"],
      "version": "0.21.7-dev"
    },
    "plugin": {
      "id": "com.plugin.withhooks",
      "pluginInfo": {
        ...
      },
      "platform": "android",
      "dir": "/path/to/project/plugins/com.plugin.withhooks"
    }
  },
  "cordova": {} //cordova object
}</pre>
<div class="blog_h1"><span class="graybg">常见问题</span></div>
<div class="blog_h2"><span class="graybg">避免Cordova在构建时自动下载gradle</span></div>
<p>修改$CORDOVA_PROJECT_ROOT/platforms/android/cordova/lib/build.js 中如下变量的值：</p>
<pre class="crayon-plain-tag">var distributionUrl = 'distributionUrl=../../../gradle-2.2.1-all.zip';</pre>
<p>然后，把本地磁盘的 gradle-2.2.1-all.zip拷贝或者链接到$CORDOVA_PROJECT_ROOT/platforms 即可。执行<pre class="crayon-plain-tag">cordova prepare</pre>  时，会从config.xml中读取版本信息，并同步到已经添加的各平台的对应目录中。</p>
<p>让Gradle使用本地Maven仓库中的构件，避免不必要的下载动作</p>
<p>修改$CORDOVA_PROJECT_ROOT/platforms/android/build.gradle中的内容：</p>
<pre class="crayon-plain-tag">repositories {
    mavenLocal()
    //添加上面一行的内容
    mavenCentral()
}</pre>
<p>&nbsp;</p>
</div><p>The post <a rel="nofollow" href="https://blog.gmem.cc/mobile-app-dev-with-apache-cordova">基于Apache Cordova开发移动应用程序</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.gmem.cc/mobile-app-dev-with-apache-cordova/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
