<?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; Android</title>
	<atom:link href="https://blog.gmem.cc/category/work/mobile/android/feed" rel="self" type="application/rss+xml" />
	<link>https://blog.gmem.cc</link>
	<description></description>
	<lastBuildDate>Thu, 16 Apr 2026 07:10:45 +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>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>
		<item>
		<title>Android Studio知识集锦</title>
		<link>https://blog.gmem.cc/android-studio-faq</link>
		<comments>https://blog.gmem.cc/android-studio-faq#comments</comments>
		<pubDate>Tue, 24 Mar 2015 09:30:12 +0000</pubDate>
		<dc:creator><![CDATA[Alex]]></dc:creator>
				<category><![CDATA[Android]]></category>
		<category><![CDATA[AS]]></category>
		<category><![CDATA[Ubuntu]]></category>

		<guid isPermaLink="false">https://blog.gmem.cc/?p=11187</guid>
		<description><![CDATA[<p>Ubuntu相关的AS知识 环境变量设置 [crayon-69e09a4918d39348530139/] 安装KVM以加速AVD  [crayon-69e09a4918d3d776531390/] 字体渲染优化 Android Studio（包括Jetbrains、WebStorms等IDE），在Ubuntu下字体渲染过于单薄，可以使用修改过的Open JDK： [crayon-69e09a4918d3f287274095/] 修改Jetbrains的启动脚本，设置上面安装的Open JDK为JAVA_HOME，并设置以下JVM参数： [crayon-69e09a4918d42799421622/] 下面是.desktop文件的示例： [crayon-69e09a4918d44996715773/] &#160; &#160;</p>
<p>The post <a rel="nofollow" href="https://blog.gmem.cc/android-studio-faq">Android Studio知识集锦</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_h2"><span class="graybg">Ubuntu相关的AS知识</span></div>
<div class="blog_h3"><span class="graybg">环境变量设置</span></div>
<pre class="crayon-plain-tag">ANDROID_HOME=/home/alex/Android/Sdk
PATH=$ANDROID_HOME/tools:$ANDROID_HOME/platform-tools:$PATH</pre>
<div class="blog_h3"><span class="graybg">安装KVM以加速AVD </span></div>
<pre class="crayon-plain-tag"># 安装必要的软件
sudo apt-get install qemu-kvm libvirt-bin ubuntu-vm-builder bridge-utils
# 添加当前用户到组
sudo adduser alex kvm
sudo adduser alex libvirtd

# 启动AVD管理器，新建模拟器，注意一定要选择Atom的镜像
android avd</pre>
<div class="blog_h3"><span class="graybg">字体渲染优化</span></div>
<p>Android Studio（包括Jetbrains、WebStorms等IDE），在Ubuntu下字体渲染过于单薄，可以使用修改过的Open JDK：</p>
<pre class="crayon-plain-tag"># 可选启用HUD
sudo add-apt-repository ppa:danjaredg/jayatana
sudo apt-get update
sudo apt-get install jayatana
# 安装带Font fix的JDK版本
sudo add-apt-repository ppa:no1wantdthisname/openjdk-fontfix
sudo apt-get update
sudo apt-get install openjdk-7-jdk</pre>
<p>修改Jetbrains的启动脚本，设置上面安装的Open JDK为JAVA_HOME，并设置以下JVM参数：</p>
<pre class="crayon-plain-tag">-Dawt.useSystemAAFontSettings=lcd
-Dswing.aatext=true
-Dsun.java2d.xrender=true
-Dsun.java2d.pmoffscreen=false</pre>
<p>下面是.desktop文件的示例：</p>
<pre class="crayon-plain-tag">[Desktop Entry]
Encoding=UTF-8
Version=1.0
Type=Application
Name=Android Studio
Icon=jetbrains-studio.png
Path=/home/alex
Exec=/usr/lib/jvm/java-1.7.0-openjdk-amd64/bin/java -Xbootclasspath/a:/home/alex/Android/ide/as/1.5/bin/../lib/boot.jar -classpath /home/alex/Android/ide/as/1.5/bin/../lib/bootstrap.jar:/home/alex/Android/ide/as/1.5/bin/../lib/extensions.jar:/home/alex/Android/ide/as/1.5/bin/../lib/util.jar:/home/alex/Android/ide/as/1.5/bin/../lib/jdom.jar:/home/alex/Android/ide/as/1.5/bin/../lib/log4j.jar:/home/alex/Android/ide/as/1.5/bin/../lib/trove4j.jar:/home/alex/Android/ide/as/1.5/bin/../lib/jna.jar:/usr/lib/jvm/java-1.7.0-openjdk-amd64/lib/tools.jar -Xms256m -Xmx1280m -XX:MaxPermSize=350m -XX:ReservedCodeCacheSize=225m -XX:+UseConcMarkSweepGC -XX:SoftRefLRUPolicyMSPerMB=50 -da -Djna.nosys=true -Djna.boot.library.path= -Djna.debug_load=true -Djna.debug_load.jna=true -Dsun.io.useCanonCaches=false -Djava.net.preferIPv4Stack=true  -Dawt.useSystemAAFontSettings=lcd -Dswing.aatext=true -Dsun.java2d.xrender=true -Dsun.java2d.pmoffscreen=false -Djb.vmOptionsFile=/home/alex/Android/ide/as/1.5/bin/studio64.vmoptions -XX:ErrorFile=/home/alex/java_error_in_STUDIO_%p.log -Djb.restart.code=88 -Didea.paths.selector=AndroidStudio1.5 -Didea.platform.prefix=AndroidStudio com.intellij.idea.Main
StartupNotify=false
StartupWMClass=jetbrains-studio
OnlyShowIn=Unity;
X-UnityGenerated=true</pre>
<p>&nbsp;</p>
<p>&nbsp;</p>
</div><p>The post <a rel="nofollow" href="https://blog.gmem.cc/android-studio-faq">Android Studio知识集锦</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.gmem.cc/android-studio-faq/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Gradle学习笔记</title>
		<link>https://blog.gmem.cc/gradle-study-note</link>
		<comments>https://blog.gmem.cc/gradle-study-note#comments</comments>
		<pubDate>Fri, 16 Jan 2015 08:45:14 +0000</pubDate>
		<dc:creator><![CDATA[Alex]]></dc:creator>
				<category><![CDATA[Android]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[Gradle]]></category>
		<category><![CDATA[Groovy]]></category>
		<category><![CDATA[学习笔记]]></category>

		<guid isPermaLink="false">https://blog.gmem.cc/?p=10070</guid>
		<description><![CDATA[<p>基础知识 Gradle简介 Gradle是近年来流行起来的自动化构建工具，它具有以下特性： 灵活、通用的构建功能，类似Ant 基于约定的构建框架，类似Maven 强大的多工程构建支持 支持基于Apache Ivy的依赖管理，现已使用Gradle自己的依赖处理引擎（dependency resolution engine） 完整的支持既有的Maven或者Ivy构件仓库，且不需要pom.xml或者ivy.xml就可以进行依赖管理 整合型构建工具： 支持Ant任务，Gradle可以直接引入Ant工程，并在运行时将Ant targets转换为Gradle tasks 可以自动的把Maven的pom.xml转换为Gradle脚本 基于Groovy的构建脚本。构建脚本可以很容易的维护和重用 用于描述构建的丰富的领域模型 Gradle Wrapper：运行在没有安装Gradle的机器上运行Gradle构建 Gradle的核心是一个基于Groovy的、扩展的DSL（Domain Specific <a class="read-more" href="https://blog.gmem.cc/gradle-study-note">[...]</a></p>
<p>The post <a rel="nofollow" href="https://blog.gmem.cc/gradle-study-note">Gradle学习笔记</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">Gradle简介</span></div>
<p>Gradle是近年来流行起来的自动化构建工具，它具有以下特性：</p>
<ol>
<li>灵活、通用的构建功能，类似Ant</li>
<li>基于约定的构建框架，类似Maven</li>
<li>强大的多工程构建支持</li>
<li>支持基于Apache Ivy的依赖管理，现已使用Gradle自己的依赖处理引擎（dependency resolution engine）</li>
<li>完整的支持既有的Maven或者Ivy构件仓库，且不需要pom.xml或者ivy.xml就可以进行依赖管理</li>
<li>整合型构建工具：
<ol>
<li>支持Ant任务，Gradle可以直接引入Ant工程，并在运行时将Ant targets转换为Gradle tasks</li>
<li>可以自动的把Maven的pom.xml转换为Gradle脚本</li>
</ol>
</li>
<li>基于Groovy的构建脚本。构建脚本可以很容易的维护和重用</li>
<li>用于描述构建的丰富的领域模型</li>
<li>Gradle Wrapper：运行在没有安装Gradle的机器上运行Gradle构建</li>
</ol>
<p>Gradle的核心是一个基于Groovy的、扩展的DSL（Domain Specific Language），而不是向Maven、Ant那样，通过XML去描述构建步骤，具有非常好的灵活性。</p>
<div class="blog_h2"><span class="graybg">Gradle重要概念</span></div>
<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>工程（Project）</td>
<td>每个构建都是由<span style="background-color: #c0c0c0;">一个或多个</span>projects 构成，project是一个抽象概念，既可以和通常的IDE中的工程对应，也可以仅仅代表某项需要完成的工作</td>
</tr>
<tr>
<td>任务（Task）</td>
<td>
<p>每个project可以包含一个或者多个task，task代表细化的构建步骤</p>
<p>各Task之间可以形成依赖关系，被依赖的Task总是先执行。Gradle能够依据Task形成的无回路有向图（Directed Acyclic Graph），依次执行Task，保证每个任务至多被执行一次</p>
</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">新特性</span></div>
<div class="blog_h3"><span class="graybg">3.x</span></div>
<ol>
<li>Gradle Daemon性能增强，让构建速度最快有75%的提升，默认启用</li>
<li>更好的IDE支持</li>
<li>改进并行任务执行</li>
<li>初步的Java 9支持</li>
<li>新的plugins DSL，用于启用插件，代替原先的<pre class="crayon-plain-tag">apply plugin</pre>：<br />
<pre class="crayon-plain-tag">plugins {
    id 'my.special.plugin' version '1.0' apply false
} </pre>
</li>
<li>改进增量构建</li>
<li>更快的依赖解析</li>
<li>改进eclipse-wtp插件</li>
<li>插件库升级</li>
</ol>
<div class="blog_h3"><span class="graybg">4.x</span></div>
<ol>
<li>支持依赖的并行下载</li>
<li>改进构建缓存</li>
<li>添加Google的Maven仓库的快捷方式：<br />
<pre class="crayon-plain-tag">repositories {
    google()
}</pre>
</li>
<li>支持使用Gradle属性来配置日志级别，属性名org.gradle.logging.level，支持的值：quiet, warn, lifecycle, info, debug，默认lifecycle</li>
<li>
<p>对删除文件的Task进行更好的建模。一个Task可以使用 @Destroys 注解，明确声明自己会删除文件：</p>
<pre class="crayon-plain-tag">class RemoveTempDirs extends DefaultTask {
    @Destroys
    FileCollection tempDirs

    @TaskAction
    void removeDirs() {
        project.delete(tempDirs)
    }
} </pre>
</li>
</ol>
<div class="blog_h3"><span class="graybg">5.x</span></div>
<ol>
<li>更快的构建速度</li>
<li>更细粒度的传递性依赖管理</li>
<li>内存使用更高效</li>
<li>新的Gradle调用选项</li>
<li>新的Task/Plugin API</li>
</ol>
<div class="blog_h1"><span class="graybg">gradlew</span></div>
<p>即Gradle Wrapper，是一段脚本，此脚本<span style="background-color: #c0c0c0;">调用你声明的版本的Gradle</span>，如果目标版本不存在，则从网络上下载。
<p>使用gradlew可以保证构建过程的一致性，但是网络差的话你就得静静的等待下载完成。</p>
<div class="blog_h2"><span class="graybg">生成gradlew</span></div>
<p>要生成gradlew相关的文件，需要本机已经安装任意版本的Gradle。执行命令<pre class="crayon-plain-tag">gradle wrapper</pre>即可。</p>
<p>生成的gradlew文件包括：</p>
<ol>
<li>gradle/wrapper/gradle-wrapper.properties 配置文件</li>
<li>gradle/wrapper/gradle-wrapper.jar 包含用于下载Gradle发行版的代码</li>
<li>gradlew, gradlew.bat Shell脚本和Windows批处理脚本</li>
</ol>
<pre class="crayon-plain-tag">#Tue Jun 25 10:21:26 CST 2019
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
# 从何处下载
# 后缀 -all 表示包含源码，-bin 表示不包含源码
distributionUrl=https\://services.gradle.org/distributions/gradle-4.10-all.zip</pre>
<p>如果下载缓慢，可以手工拷贝，存放到<pre class="crayon-plain-tag">~/.gradle/wrapper/dists</pre>对应的子目录下。</p>
<div class="blog_h2"><span class="graybg">升级gradlew</span></div>
<p>执行下面的命令升级到指定版本：</p>
<pre class="crayon-plain-tag">./gradlew wrapper --gradle-version 5.4.1</pre>
<div class="blog_h2"><span class="graybg">使用gradlew</span></div>
<p>gradlew的命令行调用格式和gradle完全一致。 </p>
<div class="blog_h1"><span class="graybg">安装与配置</span></div>
<ol>
<li>安装Gradle需要JDK/JRE，版本在6.0+。Gradle内置了Groovy库，因此后者不需要</li>
<li>到<a href="http://gradle.org/gradle-download/">官网</a>下载Gradle，并解压到某个目录，该目录设置为环境变量<pre class="crayon-plain-tag">GRADLE_HOME</pre> </li>
<li>将<pre class="crayon-plain-tag">$GRADLE_HOME/bin</pre> 添加到环境变量PATH中</li>
<li>打开终端，运行<pre class="crayon-plain-tag">gradle -v</pre> 验证安装是否成功</li>
</ol>
<div class="blog_h1"><span class="graybg">与Eclipse的集成</span></div>
<div class="blog_h2"><span class="graybg">插件安装</span></div>
<p>可以从Nodeclipse提供的Update Site：http://www.nodeclipse.org/updates/gradle-ide-pack/ 安装Gradle插件。</p>
<p>此站点中杂项内容非常多，可以根据需要，选择性的安装：</p>
<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>Gradle IDE</td>
<td>Pivotal提供 的Gradle与Eclipse的集成</td>
</tr>
<tr>
<td>Minimalist Gradle Editor</td>
<td>.gradle文件的编辑器，提供语法高亮等简单功能</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">Eclipse配置</span></div>
<div class="blog_h3"><span class="graybg">Pivotal Gradle IDE</span></div>
<p>打开Window ⇨ Preferences ⇨ Gradle：</p>
<ol>
<li>Gradle Distribution 点选 Folder，填写<pre class="crayon-plain-tag">%GRADLE_HOME%</pre> 对应的目录</li>
<li>Gradle User Home 点选 Directory，填写<pre class="crayon-plain-tag">%USERPROFILE%\.gradle</pre> </li>
</ol>
<p>打开Window ⇨ Preferences ⇨ Gradle ⇨ Arguments：</p>
<ol>
<li>Java Home 点选Workspace JRE，选择一个已安装的JRE</li>
<li>默认JVM参数可以根据需要调整</li>
</ol>
<div class="blog_h3"><span class="graybg">Gradle for Eclipse</span></div>
<p>打开Window ⇨ Preferences ⇨ Gradle EnIDE：</p>
<ol>
<li>Gradle Home to use：填写<pre class="crayon-plain-tag">%GRADLE_HOME%</pre> 对应的目录</li>
</ol>
<div class="blog_h3"><span class="graybg">Start Explorer</span></div>
<p>打开Window ⇨ Preferences ⇨ Desktop Environment：</p>
<ol>
<li>点选 Custom Desktop Environment</li>
<li>Start Shell设置为Console2，例如<pre class="crayon-plain-tag">D:\Programs\Console2\Console.exe -d ${resource_path}</pre> </li>
</ol>
<div class="blog_h2"><span class="graybg">创建Gradle工程</span></div>
<p>打开File ⇨ New ⇨ Other，选择Gradle/Gradle Project，即可创建Gradle工程，Sample project选择Java Quickstart，点击完成。</p>
<p>初次创建Gradle IDE会去下载一些东西，完毕后，形成类似下面的工程结构：</p>
<p><img class="aligncenter size-full wp-image-10089" src="https://blog.gmem.cc/wp-content/uploads/2015/01/gradle-project.jpg" alt="gradle-project" width="335" height="213" /></p>
<p>可以看到，默认配置下Gradle沿用了Maven的工程目录习惯。除了把pom.xml换成build.gradle，Maven工程和Gradle工程的结构完全相同。</p>
<p>Eclipse工程结构和元数据完全根据build.gradle导出，这个和Maven也是类似的。工程根目录右键 ⇨ Gradle ⇨ Refresh All，可以根据当前的build.gradle更新Eclipse工程结构。</p>
<p>要构建Gradle工程，可以工程根目录右键 ⇨ Run As ⇨ Gradle Build。</p>
<div class="blog_h1"><span class="graybg">Gradle命令行</span></div>
<p>命令格式：</p>
<pre class="crayon-plain-tag">//可以同时指定多个Task
gradle [option...] [task...]</pre>
<p>用法示例：</p>
<pre class="crayon-plain-tag"># 依次调用 compile 和 test 任务
# 它们所依赖的任务会根据依赖图的顺序被依次调用，每个任务只会调用一次
# 只需要声明任务之间的依赖，不需要人工控制任务执行顺序，是相对于Maven的优势
gradle compile test

# 使用-x 排除某个任务
gradle dist -x test

# 默认情况下，一旦有一个任务失败，Gradle就会终止执行，下面的选项禁止此行为
gradle run --continue

# 简化任务名：不需要输入完整，只需要足够避免歧义的前缀
gradle ru
# 简化任务名：驼峰式首字母，下面的命令调用gradle compTest
gradle cT

# Gradle默认依据当前目录下的build.gradle，使用-b可以选择构建其它脚本
gradle -q -b subdir/myproject.gradle hello

# 使用-p，可以选择构建的目录
gradle -q -p subdir hello

# 获取子工程列表
gradle -q projects

# 列出工程中所有任务
gradle tasks
gradle -q tasks --all   #同时显示任务依赖关系

# 显示任务的详细信息
gradle help --task taskName

# 显示工程中所有依赖库的列表
gradle dependencies  #输出依据依赖配置（Dependencies Configuration）分组
# 显示api子工程的所有依赖列表
gradle -q dependencies api:dependencie
# 显示api子工程的、testCompile配置的所有依赖列表
gradle -q api:dependencies --configuration testCompile
# 显示某个特定依赖库的情况，显示groovy依赖库的情况
gradle -q api:dependencyInsight --dependency groovy --configuration compile

# 显示工程属性列表
gradle properties
gradle -q api:propertie #子工程属性列表

# 记录构建日志
gradle run --profile   # 日志存放到build/reports/profile 

# 以特定的工程属性来构建
gradle hello -PskipHello

# 设置JVM属性
gradle hello -Djava.io.tempdir=/tmp

# 丢弃已经编译的脚本缓存
gradle  --recompile--scripts 

# 显示图形界面
gradle --gui </pre>
<div class="blog_h1"><span class="graybg"><a id="gradle-project-properties"></a>Gradle工程属性</span></div>
<p>除了通过gradle命令行参数<pre class="crayon-plain-tag">-P</pre> 直接为Gradle工程添加属性之外，还可以使用<pre class="crayon-plain-tag">gradle.properties</pre> 文件。如果有多个子工程，则每个子工程的目录都可以提供此文件：</p>
<pre class="crayon-plain-tag">gradlePropertiesProp=gradlePropertiesValue
# 对于根工程，可以设置JVM系统属性，这些属性以systemProp.开头
systemProp.system=systemValue</pre>
<div class="blog_h1"><span class="graybg">编写构建脚本</span></div>
<p><pre class="crayon-plain-tag">build.gradle</pre> 本质上是Groovy脚本，任何合法的Groovy语句都可以添加到其中。</p>
<div class="blog_h3"><span class="graybg">Hello World</span></div>
<p>在当前目录下创建一个UTF-8编码的build.gradle文件，或者通过Eclipse创建一个Gradle工程。打开build.gradle文件，输入下面的内容：</p>
<pre class="crayon-plain-tag">//可以声明工程属性
description = 'Hello project'

//声明一个名为hello的task
//新任务默认是org.gradle.api.DefaultTask的实例
task hello {
    //闭包指定了hello的action
    doLast {
        println 'Hello world!'  //任何Groovy脚本都支持
    }
}

//上述任务也可以用下面的快捷声明方式
task hello &lt;&lt; {
    println 'Hello world!'
}</pre>
<p>然后，打开终端，pushd到当前目录，运行命令：<pre class="crayon-plain-tag">gradle -q hello</pre>  ，可以看到输出：<pre class="crayon-plain-tag">Hello world!</pre> 。该命令就是调用hello这个Task，而这个Task仅仅是打印一条语句到控制台。</p>
<div class="blog_h3"><span class="graybg">默认任务</span></div>
<p>使用下面的脚本可以指定默认任务，这样，如果<pre class="crayon-plain-tag">gradle</pre> 命令不指定task参数，则自动运行这些任务：</p>
<pre class="crayon-plain-tag">defaultTasks 'hello', 'run'</pre>
<div class="blog_h3"><span class="graybg">Project API</span></div>
<p>对于构建中的每个工程，Gradle都创建了一个<pre class="crayon-plain-tag">org.gradle.api.Project</pre> 类型的对象，工程脚本<pre class="crayon-plain-tag">build.gradle</pre> 的顶级作用域就是该对象（this指向当前工程）：</p>
<pre class="crayon-plain-tag">apply plugin: 'java'

println this.name     //当前对象——当前工程——的name属性
println name          //自动委托给当前对象
println project.name  //this.project == this，预定义属性
//上面三个语句打印一样的结果</pre>
<p>除了上面的name以外，你还可以访问很多Project的属性，例如： </p>
<table class="fixed-word-wrap" style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="text-align: center;">属性</td>
<td style="text-align: center;">类型</td>
<td style="text-align: center;">说明 </td>
</tr>
</thead>
<tbody>
<tr>
<td>project</td>
<td>Project</td>
<td>当前Project 实例对象</td>
</tr>
<tr>
<td>path</td>
<td>String</td>
<td>工程的绝对路径</td>
</tr>
<tr>
<td>description</td>
<td>String</td>
<td>工程的描述</td>
</tr>
<tr>
<td>projectDir</td>
<td>File</td>
<td>包含了构建脚本的目录</td>
</tr>
<tr>
<td>build</td>
<td>File</td>
<td>projectDir/build</td>
</tr>
<tr>
<td>group</td>
<td>Object</td>
<td>组标识</td>
</tr>
<tr>
<td>name</td>
<td>String</td>
<td>工程名称</td>
</tr>
<tr>
<td>version</td>
<td>Object</td>
<td>版本标识</td>
</tr>
<tr>
<td>ant</td>
<td>org.gradle.api.AntBuilder</td>
<td>Ant实例对象</td>
</tr>
</tbody>
</table>
<p>Project包含很多常用的方法，可以在构建脚本中使用：</p>
<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>buildscript</td>
<td>配置驱动<span style="background-color: #c0c0c0;">构建过程本身的依赖</span>，常常在此脚本块中声明依赖和仓库</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">Script API</span></div>
<p>每个构建脚本都自动实现<pre class="crayon-plain-tag">org.gradle.api.Script</pre> 接口，因此<a href="https://docs.gradle.org/current/dsl/org.gradle.api.Script.html">该接口</a>提供的属性、方法你可以自由的使用。</p>
<div class="blog_h3"><span class="graybg">变量声明</span></div>
<p>在构建脚本中，你可以声明两种变量：</p>
<ol>
<li>局部变量 ( local ) ：只在局部作用域可见，使用关键字<pre class="crayon-plain-tag">def</pre> 声明，例如：<br />
<pre class="crayon-plain-tag">def dest = "dest"
task copy(type: Copy) {
    form "source"
    into dest
}</pre>
</li>
<li>扩展属性 ( extra )：Gradle领域模型中所有增强对象（包括但不限于：projects, tasks, source sets）都可以添加扩展属性，扩展属性在任何地方可见。使用ext脚本块可以同时声明多个属性。下面是一个示例：<br />
<pre class="crayon-plain-tag">ext {
    //为Project添加属性
    springVersion = "3.1.0.RELEASE"
    emailNotification = "build@master.org"
}
//也可以这样写
ext.springVersion = "3.1.0.RELEASE"
springVersion = "3.1.2.RELEASE"  //声明以后再修改，不需要ext关键字

//这样写会报错，自定义的外部属性，必须首先使用ext声明
hibernateVersion = "3.6.10.Final"

//设置所有源码集的purpose属性
sourceSets.all { ext.purpose = null }

sourceSets {
    //分别设置每个源码集的属性
    main {
        purpose = "production"
    }
    test {
        purpose = "test"
    }
    plugin {
        purpose = "production"
    }
}

task printProperties &lt;&lt; {
    println springVersion
    println emailNotification
    sourceSets.matching { it.purpose == "production" }.each { println it.name }
}</pre>
</li>
</ol>
<div class="blog_h3"><span class="graybg">Groovy语言特性与DSL</span></div>
<p>Gradle大量使用了Groovy提供的灵活语法特性和DSL支持。整个构建脚本都是合法的Groovy代码，虽然有时看起来更像是XML那样的配置文件：</p>
<pre class="crayon-plain-tag">//下面的脚本，更像是配置文件
dependencies {
    compile group: 'commons-collections', name: 'commons-collections', version: '3.2'
    testCompile project(':shared')
}</pre>
<p>实际上dependencies是Project对象的一个方法，它的签名如下：</p>
<pre class="crayon-plain-tag">public interface Project{
    void dependencies(Closure configureClosure);
}</pre>
<p>可以看到，该方法的入参是一个闭包，我们可以把上面的脚本改写的更像C-Style的代码：</p>
<pre class="crayon-plain-tag">dependencies({
    //这里是闭包体，其包含了两个方法调用：
    compile( [ group: 'commons-collections', name: 'commons-collections', version: '3.2' ] )
    testCompile( project(':shared') )
    //那么，闭包里面调用的方法，是哪个对象的呢？是this也就是闭包本身吗？不是……
    //根据Groovy的规则，闭包里面的变量，其Scope由闭包的delegate属性指定，因此上面的调用实际上是：
    delegate.testCompile( project(':shared') )
    //delegate已经由Gradle自动设置为dependencies
})</pre>
<p>我们还可以把上述脚本改写为等价的Java语句： </p>
<pre class="crayon-plain-tag">Closure&lt;Object&gt; c = new Closure&lt;Object&gt;(){

    public Object call( Object... args ){
    
        assert delegate == project.dependencies
        
        Map&lt;String,String&gt; map = new HashMap&lt;String,String&gt;();
        map.put( 'group',   'commons-collections' );
        map.put( 'name',    'commons-collections' );
        map.put( 'version', '3.2' );
        delegate.compile( dep );
        
        delegate.testCompile( project(':shared') )
        
    }
}
c.delegate = project.dependencies
project.dependencies( c );</pre>
<div class="blog_h3"><span class="graybg">脚本包含</span></div>
<p>可以把其它脚本的内容包含到当前构建脚本中：</p>
<pre class="crayon-plain-tag">println "configuring $project"
task hello &lt;&lt; {
    println' 'hello form other srcipt'
}</pre><br />
<pre class="crayon-plain-tag">apply from: 'other.gradle'  //包含其它脚本</pre>
<p> 其它脚本中定义的任务，可以直接使用：<pre class="crayon-plain-tag">gradle -q hello</pre> </p>
<div class="blog_h1"><span class="graybg">依赖管理</span></div>
<p>依赖管理是任何一种构建工具的关键特性，Gradle提供了易于理解的、和Maven/Ivy兼容的依赖管理机制。Gradle的依赖管理具有以下特点：</p>
<ol>
<li>传递性依赖管理</li>
<li>支持非受管的（non-managed）依赖：依赖可以仅仅是位于版本控制工具或者共享磁盘上的文件</li>
<li>支持定制化的依赖定义：可以在构建脚本中描述依赖的层次</li>
<li>完全可定制的依赖解析机制（Dependency Resolution）</li>
<li>与Maven和Ivy完全兼容</li>
<li>与既有的依赖管理工具，例如Maven和Ivy集成</li>
</ol>
<p>依赖管理分为两部分的内容：</p>
<ol>
<li>依赖项（dependencies）：当前工程在构建或者运行时需要的东西</li>
<li>发布项（publications）：当前工程的构建的产出，这些产出可能需要上传到仓库</li>
</ol>
<p>对于依赖项，Gradle采用了类似Maven的方式进行声明，而不像Ant那样指定绝对路径。依赖项的所在位置（远程仓库或者本地目录）另外声明。对于发布项，行为依赖于配置，可能是发布到本地目录，或者远程仓库。</p>
<div class="blog_h3"><span class="graybg">依赖配置</span></div>
<p>Gradle把依赖按照<span style="background-color: #c0c0c0;">依赖配置（dependency configuration）分组</span>。每个分组具有一个名称和若干其它属性，分组可以继承其它分组。某些插件会自动引入一些依赖配置，例如Java插件添加了：</p>
<table class="fixed-word-wrap full-width" 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>compile</td>
<td>用来编译项目源代码的依赖</td>
</tr>
<tr>
<td>runtime</td>
<td>在运行时被生成的类使用的依赖，默认也作为编译时依赖</td>
</tr>
<tr>
<td>testCompile</td>
<td>编译测试代码的依赖</td>
</tr>
<tr>
<td>testRuntime</td>
<td>运行测试所需要的依赖</td>
</tr>
</tbody>
</table>
<p>除了插件自动引入的依赖配置以外，你还可以在构建脚本中自行添加。自定义依赖配置在很多情况下有用，例如添加一个既不在编译，也不在测试时需要的“运行时”依赖（典型的例子是JDBC驱动程序）到归档文件中。</p>
<p>下面示例了如何声明依赖配置：</p>
<pre class="crayon-plain-tag">configurations {
    compile {
        description = 'compile classpath'
        transitive = true //是否启用传递性依赖
    }
    runtime {
        extendsFrom compile
    }
}</pre>
<div class="blog_h3"><span class="graybg">依赖项声明</span></div>
<p>每一个依赖项声明的格式为：</p>
<pre class="crayon-plain-tag">dependencies{
    // 依赖配置名称 依赖项规格说明
}</pre>
<p>依赖项规格是什么形式取决于依赖的类型：</p>
<table class="fixed-word-wrap" style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="width: 18%; text-align: center;">依赖类型</td>
<td style="text-align: center;">依赖项规格说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>
<p>外部模块依赖</p>
</td>
<td>
<p>即对当前构建以外的模块（Module）的依赖，模块可以存放在Maven、Ivy远程仓库或者本地仓库中，甚至位于本地目录中。一个外部依赖使用group、name、version三个属性限定。下面是一些例子：</p>
<pre class="crayon-plain-tag">dependencies {
    //可以使用group name version字段来指定
    runtime group: 'hibernate', name: 'hibernate-core', version: '3.6.7'
    //也可以使用分号分隔的group:name:version指定
    runtime 'hibernate:hibernate-core:3.6.7','springframework:spring-aop:2.5'
    //可以控制传递性依赖
    runtime('hibernate:hibernate:3.0.5') {
        transitive = true
    }
    //仅构件标记（Artifact only notation），不去下载模块描述符（例如pom.xml），仅仅下载构件本身
    runtime "org.groovy:groovy:2.2.0@jar"
    //使用分类器字段
    otherConf group: 'test', name: 'service', version: '1.0', classifier: 'jdk14'
}</pre>
</td>
</tr>
<tr>
<td>
<p>工程依赖
</td>
<td>在多工程构建时，声明各子工程之间的依赖，例如：<br />
<pre class="crayon-plain-tag">project(':shared')</pre>
</td>
</tr>
<tr>
<td>
<p>文件依赖</p>
</td>
<td>即对位于本地文件系统上一系列文件的依赖，例如：<br />
<pre class="crayon-plain-tag">dependencies {
    runtime files('libs/a.jar', 'libs/b.jar')
    runtime fileTree(dir: 'libs', include: '*.jar')
}</pre>
</td>
</tr>
<tr>
<td>
<p>客户端模块依赖</p>
</td>
<td>类似于外部模块依赖，目标构件位于某个仓库中，但是模块的元数据文件则由当前构建指定。如果需要覆盖外部模块的元数据，可以使用这种依赖。示例：<br />
<pre class="crayon-plain-tag">runtime module(group: 'org.apache.ant', name: 'ant', version: '1.9.4') {
    dependencies "org.apache.ant:ant-launcher:1.9.4@jar",
                     "org.apache.ant:ant-junit:1.9.4"
}</pre>
</td>
</tr>
<tr>
<td>
<p>Gradle API依赖</p>
</td>
<td>针对当前Gradle版本的API的依赖，开发Gradle插件和Task类型时使用：<br />
<pre class="crayon-plain-tag">dependencies {
    compile gradleApi()
}</pre>
</td>
</tr>
<tr>
<td>
<p>本地Groovy依赖</p>
</td>
<td>针对当前Gradle使用的Groovy版本的依赖，开发Gradle插件和Task类型时使用：<br />
<pre class="crayon-plain-tag">dependencies {
    compile localGroovy()
}</pre>
</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">排除传递性依赖</span></div>
<pre class="crayon-plain-tag">configurations {
    //为指定的依赖配置指定传递性依赖的排除规则
    compile.exclude module: 'commons'
    //为所有的依赖配置指定传递性依赖的排除规则
    all*.exclude group: 'org.gradle.test.excludes', module: 'reports'
}

dependencies {
    compile("org.gradle.test.excludes:api:1.0") {
        exclude module: 'shared' //为单个依赖项指定传递性依赖的排除规则
    }
}</pre>
<div class="blog_h3"><span class="graybg">仓库</span></div>
<p>Gradle必须知道从哪里下载外部依赖，这是由仓库配置来指定的：</p>
<pre class="crayon-plain-tag">repositories {
    //Maven本地仓库，寻找本地仓库的逻辑与Maven相同
    mavenLocal()
    //Maven中心仓库
    mavenCentral()
    //JCenter仓库
    jcenter()
    //其它Maven远程仓库
    maven {
        //可以指定身份验证信息
        credentials {
            username 'user'
            password 'password'
        }
        url "http://repo.mycompany.com/maven2"
        //如果上面的URL找不到构件，则在下面找
        artifactUrls "http://repo.mycompany.com/jars"
    }
    //Ivy远程仓库
    ivy {
        url "http://repo.mycompany.com/repo"
    }
    //Ivy本地仓库
    ivy {
        url "../local-repo"
    }
    //扁平布局的文件系统仓库
    flatDir {
        dirs 'lib'
    }
    flatDir {
        dirs 'lib1', 'lib2'
    }
}</pre>
<div class="blog_h3"><span class="graybg">发布构件</span></div>
<p>除了可以发布到本地目录外，Gradle还支持发布构建的产出到Ivy、Maven仓库：</p>
<pre class="crayon-plain-tag">uploadArchives {
    repositories {
        //发布到扁平结构的目录
        flatDir {
            dirs 'repos'
        }
        //发布到Ivy仓库
		ivy {
		    credentials {
		        username "username"
		        password "pw"
		    }
		    url "http://repo.mycompany.com"
		}
		//发布到Maven仓库
		mavenDeployer {
            repository(url: "file://localhost/tmp/myRepo/")
        }
    }
}</pre>
<div class="blog_h1"><span class="graybg">任务详解</span></div>
<div class="blog_h3"><span class="graybg">多种定义Task的方式</span></div>
<p> task关键字本质上是Project对象的一组方法：</p>
<pre class="crayon-plain-tag">Task task(String name) throws InvalidUserDataException
Task task(String name, Closure configureClosure)
Task task(Map&lt;String,?&gt; args, String name) throws InvalidUserDataException
Task task(Map&lt;String,?&gt; args, String name, Closure configureClosure)</pre>
<p> 下面列举了不同风格的Task定义：</p>
<pre class="crayon-plain-tag">// 操作符“&lt;&lt;”被Task重载过，它接受一个闭包，该闭包可以对当前Task进行任意操作
// 直接使用不带引号的hello0，会被解释为this.hello0，Gradle可能进行了Groovy元编程，让不存在的变量指定作为当前project的属性看待

// 不带括号
task hello0 &lt;&lt; {
    description 'Hello task'   // 可以给Task添加描述
    println "hello"
}
// 带括号
task(hello1) &lt;&lt; {
    println "hello1"
}
// 任务名带引号
task('hello2') &lt;&lt;
{
    println "hello2"
}
//指定任务类型
task('copy', type: Copy) {
    //闭包的delegate是task对象，注意Gradle的这种风格，与上面章节dependencies中的闭包delegate指向是一个风格
    from(file('srcDir'))
    into(buildDir)
}
//这样也可以
tasks.create(name: 'hello') &lt;&lt; {
    println "hello"
}</pre>
<div class="blog_h3"><span class="graybg">获得Task的引用</span></div>
<p>首先，任何Task都作为Project的属性看待：</p>
<pre class="crayon-plain-tag">task hello
//作为当前工程的属性来获得Task的引用
println hello.name
println project.hello.name</pre>
<p>也可以通过<pre class="crayon-plain-tag">org.gradle.api.tasks.TaskContainer</pre> 来访问Task：</p>
<pre class="crayon-plain-tag">task hello
//作为TaskContainer的属性访问
println tasks.hello.name
println tasks['hello'].name

project(':projectA') {
    task hello
}

//也可以调用getByPath()方法
//路径根部的:可以省略
println tasks.getByPath('hello').path  //:hello
println tasks.getByPath(':hello').path //:hello
println tasks.getByPath('projectA:hello').path  //:projectA:hello
println tasks.getByPath(':projectA:hello').path //:projectA:hello</pre>
<div class="blog_h3"><span class="graybg">定制Task的类型和属性</span></div>
<p> 下面实例了指定Task的类型为<pre class="crayon-plain-tag">org.gradle.api.tasks.Copy</pre> 并且设置其属性的几种方式：</p>
<pre class="crayon-plain-tag">//Copy任务用来拷贝文件到目标目录中，支持过滤和重命名

//指定任务类型
task myCopy(type: Copy)

//同时设置属性
Copy myCopy = task(myCopy, type: Copy)
myCopy.from 'resources'
myCopy.into 'target'
myCopy.include('**/*.txt', '**/*.xml', '**/*.properties')

//闭包方式，再次注意闭包的delegate指向是当前task，delegate在Minimalist Gradle Editor中常常显示为关键字
task copy(type: Copy) {
   from 'resources'
   into 'target'
   include('**/*.txt', '**/*.xml', '**/*.properties')
}</pre>
<div class="blog_h3"><span class="graybg">为Task添加依赖 </span></div>
<pre class="crayon-plain-tag">//声明任务intro依赖于hello
task intro(dependsOn: 'hello') &lt;&lt; { //如果被依赖任务在后面定义，则这里引用的任务名必须加引号
    println "I'm Gradle"
}
//被依赖的任务可以在后面定义
task hello &lt;&lt; {
    println 'Hello world!'
}
//访问Task的方法也可以添加依赖
intro.dependsOn hello
//也可以通过闭包添加依赖
intro.dependsOn {
    tasks.findAll { task -&gt; task.name.startsWith('lib') }
}

project('projectA') {
    //要依赖其它项目中的任务，必须指定任务的路径
    task taskX(dependsOn: ':projectB:taskY') &lt;&lt; {
        println 'taskX'
    }
}
project('projectB') {
    task taskY &lt;&lt; {
        println 'taskY'
    }
}</pre>
<div class="blog_h3"><span class="graybg">替换一个已经存在的任务</span></div>
<pre class="crayon-plain-tag">task copy(type: Copy)
task copy(overwrite: true) &lt;&lt; { //必须设置overwrite为true
    println('I am the new one.')
}</pre>
<div class="blog_h3"><span class="graybg">跳过任务执行</span></div>
<p>Gradle提供几种方式，来绕过某个任务的执行： </p>
<pre class="crayon-plain-tag">task hello &lt;&lt; {
    println 'hello world'
//只有当工程没有属性“skipHello”的时候，才会执行hello
hello.onlyIf { !project.hasProperty('skipHello') }
//运行gradle hello -q -PskipHello 不会打印任何信息

//也可以使用StopExecutionException异常跳过任务
hello.doFirst{ //添加如下逻辑到Task最前面
     if (true) { throw new StopExecutionException() }
}

//还可以设置enabled属性，为false则不会执行
hello.enabled = false</pre>
<div class="blog_h3"><span class="graybg">跳过up-to-date的任务</span></div>
<p>Gradle判断任务是否已经是最新状态的依据是：如果任务的输入没有变化，那么当前任务不需要执行（也就是up-to-date）。更进一步说，Gradle的判断规则如下：</p>
<ol>
<li>Task执行前，获得输入文件的散列值</li>
<li>Task执行完毕后，存储输出文件的散列值</li>
<li>Task执行前，如果输入、输出的散列值和上一次执行相比，没有变化，那么任务是up-to-date </li>
</ol>
<p>可以看到，如果任务没有输出，那么它永远不会被看做是up-to-date的。如何声明任务的输入输出呢？</p>
<pre class="crayon-plain-tag">task transform {
    ext.srcFile = file('mountains.xml')
    ext.destDir = new File(buildDir, 'generated')
    
    //下面两行定义了当前任务的输入和输出
    inputs.file srcFile
    outputs.dir destDir
    
    doLast {
       //Task的逻辑
    }
}</pre>
<p>上述脚本设置了Task的inputs、outputs属性，如果没必要执行，那么Gradle会自动跳过此任务。</p>
<p>对于复杂的场景，可以调用<pre class="crayon-plain-tag">TaskOutputs.upToDateWhen()</pre> 来判断任务是否up-to-date。</p>
<div class="blog_h3"><span class="graybg">动态创建Task</span></div>
<pre class="crayon-plain-tag">//动态创建4个任务task1 ... task3
4.times { counter -&gt;
    task "task$counter" &lt;&lt; {
        println "I'm task number $counter"
    }
}

//基于规则的动态任务
//对于任何pingXxx，都作为任务看待
tasks.addRule("Pattern: ping&lt;ID&gt;") { String taskName -&gt;
    if (taskName.startsWith("ping")) {
        task(taskName) &lt;&lt; {
            println "Pinging: " + (taskName - 'ping')
        }
    }
}
//执行命令 gradle -q pingServer1
//输出 Pinging: Server1

//这些动态任务甚至还可以用做任务依赖
task groupPing {
    dependsOn pingServer1, pingServer2
}</pre>
<div class="blog_h3"><span class="graybg">添加finally任务</span></div>
<p>类似于Java的<pre class="crayon-plain-tag">finally</pre> 代码块，这种任务用于在目标任务结束后执行，不论目标任务是否执行成功：</p>
<pre class="crayon-plain-tag">task taskX &lt;&lt; {
    throw new RuntimeException()
}
task taskY &lt;&lt; {
    println 'taskY'
}

taskX.finalizedBy taskY
//运行 gradle -q taskX
//输出 taskY</pre>
<div class="blog_h1"><span class="graybg"> 文件操作</span></div>
<p>由于大多数构建都需要操作文件，因此Gradle定义了一些额外的文件操作API。</p>
<pre class="crayon-plain-tag">/************************ 文件定位 ***************************/
// 使用一个相对路径
File configFile = project.file('src/config.xml')
// 使用一个绝对路径
configFile = file('file:/some/path.xml')
configFile = file('F:/config.xml')



/************************ 文件集合 ***************************/
FileCollection collection = files(
    'src/file1.txt',
    new File('src/file2.txt'),
    ['src/file3.txt', 'src/file4.txt'] //会被自动展开
)
//迭代集合
collection.each {File file -&gt;
    println file.name
}
//类型转换
List list = collection as List
String path = collection.asPath
File file = collection.singleFile
//添加、去除文件元素
def union = collection + files('src/file3.txt')
def different = collection - files('src/file3.txt')


/************************ 文件树 ***************************/
//从相对目录创建
FileTree tree = fileTree(dir: 'src/main')
tree.include '**/*.java'
tree.exclude '**/Abstract*'
//从归档文件创建
FileTree zip = zipTree('someFile.zip')
FileTree tar = tarTree('someFile.tar')
//遍历
tree.each {File file -&gt;
    println file
}
//过滤
FileTree filtered = tree.matching {
    include 'org/gradle/api/**'
}
//合并
FileTree sum = tree + fileTree(dir: 'src/test')


/************************ 复制文件 ***************************/
//各种文件来源
task copyTask(type: Copy) {
    //复制目录下所有文件
    from 'src/main/webapp'
    //复制单个单独文件
    from 'src/staging/index.html'
    //从一个任务的输出复制
    from copyTask
    //从一个任务的outputs属性复制
    from copyTaskWithPatterns.outputs
    //复制归档文件中的内容
    from zipTree('src/main/assets.zip')
    //最终指定目标目录
    into { getDestDir() }
}
//文件过滤
task copyTaskWithPatterns(type: Copy) {
    from 'src/main/webapp'
    into 'build/explodedWar'
    include '**/*.html'
    include '**/*.jsp'
    exclude { details -&gt; details.file.name.endsWith('.html') }
}
//复制的过程中重命名
task rename(type: Copy) {
    //闭包指定重命名规则
    rename { String fileName -&gt; fileName.replace('-staging-', '') } 
    //使用正则表达式指定映射规则
    rename(/(.+)-staging-(.+)/, '$1$2')
}
//处理文件内容
task filter(type: Copy) {
    //替换文件中的标记，标记必须是@tokenName@的形式
    expand(copyright: '2009', version: '2.3.1')
    expand(project.properties)
    //使用闭包处理每一行
    filter { String line -&gt; "[$line]" }
    //使用闭包删除行
    filter { String line -&gt;  line.startsWith('-') ? null : line  }
}


/************************ 同步两个目录 ***************************/
task libs(type: Sync) {
    from configurations.runtime
    into "$buildDir/libs"
}


/************************ 创建归档文件 ***************************/
task zip(type: Zip) {
    from 'src/dist'
    into('libs') {
        from configurations.runtime //指定依赖配置类型
    }
    baseName = 'customName' //指定归档文件名
}</pre>
<div class="blog_h1"><span class="graybg">日志记录</span></div>
<div class="blog_h3"><span class="graybg">日志记录级别</span></div>
<p>Gradle支持以下日志记录级别：</p>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;"> 日志级别</td>
<td style="text-align: center;">命令选项</td>
<td style="text-align: center;">说明 </td>
</tr>
</thead>
<tbody>
<tr>
<td>ERROR</td>
<td> </td>
<td>打印错误消息</td>
</tr>
<tr>
<td>QUIET</td>
<td>-q  --quiet</td>
<td>打印重要的、你应当知道的消息 </td>
</tr>
<tr>
<td>WARNING </td>
<td> </td>
<td>打印警告信息</td>
</tr>
<tr>
<td>LIFECYCLE</td>
<td>默认</td>
<td>这是Gradle的默认级别，打印处理进度信息</td>
</tr>
<tr>
<td>INFO </td>
<td>-i  --info</td>
<td>打印一般性的消息 </td>
</tr>
<tr>
<td>DEBUG</td>
<td>-d --debug</td>
<td>打印调试信息</td>
</tr>
</tbody>
</table>
<p>在打印日志的同时，还可以指定命令行参数：</p>
<ol>
<li>-s  --stacktrace：打印调用栈</li>
<li>-S  --full-stacktrace：打印完整调用栈</li>
</ol>
<div class="blog_h3"><span class="graybg">打印自己的日志</span></div>
<p>可以向标准输出打印日志：</p>
<pre class="crayon-plain-tag">println 'A message which is logged at QUIET level'</pre>
<p>或者通过<pre class="crayon-plain-tag">logger</pre> 属性打印日志： </p>
<pre class="crayon-plain-tag">logger.quiet('An info log message which is always logged.')
logger.error('An error log message.')
logger.warn('A warning log message.')
logger.lifecycle('A lifecycle info log message.')
logger.info('An info log message.')
logger.debug('A debug log message.')
logger.trace('A trace log message.')</pre>
<p>还可以使用外部日志工具：</p>
<pre class="crayon-plain-tag">import org.slf4j.Logger
import org.slf4j.LoggerFactory

Logger slf4jLogger = LoggerFactory.getLogger('some-logger')
slf4jLogger.info('An info log message logged using SLF4j')</pre>
<div class="blog_h1"><span class="graybg"> Gradle的插件机制</span></div>
<p>就像Maven一样，Gradle核心本身也不能做多少有意义的事情，实际的构建工作都是由插件完成的。Gradle允许插件：</p>
<ol>
<li>扩展Gradle的DSL模型，例如可以配置新的DSL元素</li>
<li>应用“约定由于配置”原则，为工程提供缺省配置</li>
<li>引入新的默认任务集</li>
</ol>
<p>Gradle插件分为两类：</p>
<ol>
<li>脚本插件：本质上是额外的构建脚本，可以进一步的配置构建逻辑</li>
<li>二进制插件：是接口<pre class="crayon-plain-tag">org.gradle.api.Plugin</pre> 的实现，以编程的的方式操控构建</li>
</ol>
<div class="blog_h3"><span class="graybg">应用插件到工程</span></div>
<p>使用方法<pre class="crayon-plain-tag">Project.apply()</pre> 可以应用插件到工程，应用同一插件多次是安全的：</p>
<pre class="crayon-plain-tag">/** 应用一个脚本插件 **/
// 实际上就是include一段脚本
apply from: 'other.gradle'

/** 应用二进制插件 **/
apply plugin: 'java'  //基于短名
apply plugin: JavaPlugin //基于插件类，org.gradle.api.plugins包内的插件不需要全名
//通过构建脚本块可以引入插件依赖，并使用插件
buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath "com.jfrog.bintray.gradle:gradle-bintray-plugin:0.4.1"
    }
}
apply plugin: "com.jfrog.bintray"</pre>
<div class="blog_h3"><span class="graybg">Gradle现有插件列表</span></div>
<table class=" fixed-word-wrap full-width">
<thead>
<tr>
<td style="width: 15%; text-align: center;">分类</td>
<td style="width: 25%; text-align: center;">插件标识</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td rowspan="5">语言插件</td>
<td>java</td>
<td>为工程添加Java编译、测试功能，是需要其它插件的基础</td>
</tr>
<tr>
<td>groovy</td>
<td>支持Groovy代码的编译</td>
</tr>
<tr>
<td>scala</td>
<td>支持Scala代码的编译</td>
</tr>
<tr>
<td>antlr</td>
<td>增加对ANTLR的生成解析器的支持</td>
</tr>
<tr>
<td colspan="2">此外还有尚在孵化中的：assembler、c、cpp、objective-c、windows-resources</td>
</tr>
<tr>
<td rowspan="6">集成插件</td>
<td>application</td>
<td>支持把Java工程通过命令行运行</td>
</tr>
<tr>
<td>ear</td>
<td>增加对构建J2EE应用程序的支持</td>
</tr>
<tr>
<td>war</td>
<td>
<p>支持构建War包</p>
</td>
</tr>
<tr>
<td>maven</td>
<td>
<p>支持发布构件到Maven仓库</p>
</td>
</tr>
<tr>
<td>sogi</td>
<td>支持构建OSGI工程</td>
</tr>
<tr>
<td>jetty</td>
<td>支持在嵌入式的Jetty容器中部署War包或者工程构建结果（解包）</td>
</tr>
<tr>
<td rowspan="7">开发插件</td>
<td>checkstyle</td>
<td>使用Checksytle对工程的Java代码质量进行检查</td>
</tr>
<tr>
<td>findbugs</td>
<td>使用Findbugs对工程的Java代码质量进行检查</td>
</tr>
<tr>
<td>codenarc</td>
<td>使用Codenarc对工程的Groovy代码质量进行检查</td>
</tr>
<tr>
<td>eclipse</td>
<td>生成Eclipse IDE的元数据，以便导入到Eclipse工作区</td>
</tr>
<tr>
<td>eclipse-wtp</td>
<td>与eclipse类似，支持生成Eclipse WTP工程结构</td>
</tr>
<tr>
<td>idea</td>
<td>生成Intellij IDEA IDE的元数据，以便可以导入</td>
</tr>
<tr>
<td>signing</td>
<td>对生成的归档文件进行签名</td>
</tr>
</tbody>
</table>
<div class="blog_h1"><span class="graybg">Gradle构建生命周期</span></div>
<div class="blog_h3"><span class="graybg">构建的三个阶段 </span></div>
<p>Gradle构建分为三个独立的阶段：</p>
<ol>
<li>Initialization：Gradle支持多工程构建。在初始化阶段，Gradle会确定有哪些工程参与构建，并为它们创建Project实例</li>
<li>Configuration：在此阶段，所有的Project对象被合理配置，每个Project的构建脚本（build script，即build.gradle）被执行</li>
<li>Execution：Gradle依据命令行参数、当前所在目录决定需要执行的Task的子集。Task的全集在Configuration阶段已经生成</li>
</ol>
<p>下面示意构建脚本的不同部分在哪个阶段执行：</p>
<pre class="crayon-plain-tag">println '该代码在配置阶段执行'
task configured {
	println '该代码在配置阶段执行'
}
task test &lt;&lt; {
	println '该代码在执行阶段执行'
}
task testBoth {
	doFirst {
	  println '该代码在执行阶段执行，最先执行'
	}
	doLast {
	  println '该代码在执行阶段执行，最后执行'
	}
	println '该代码在配置阶段执行'
}</pre>
<div class="blog_h3"><span class="graybg">初始化阶段</span></div>
<p>在初始化阶段，Gradle必须确定进行的单工程还是多工程构建（参见下一章）。多工程的设置总是存放在settings.gradle，Gradle自<span style="background-color: #c0c0c0;">动的寻找该文件的</span>顺序如下：</p>
<ol>
<li>在当前目录下寻找</li>
<li>在同级的、名为master的目录中寻找</li>
<li>到父目录中寻找</li>
</ol>
<p>如果找到settings.gradle，Gradle寻找被构建工程是否存在于settings.gradle所声明的工程树中，如果是，则执行多工程构建。否则，执行单工程构建。找不到settings.gradle则一定执行单工程构建。</p>
<p>注意，上述自动寻找settings.gradle的逻辑，仅在多工程满足下面条件之一时发生：</p>
<ol>
<li>工程物理位置与工程树结构对应</li>
<li>或者，使用扁平目录结构，并且用master目录表示主工程</li>
</ol>
<p>否则，你必须在settings.gradle所在目录执行Gradle命令以触发多工程构建。</p>
<div class="blog_h3"><span class="graybg">单工程构建的配置与执行阶段</span></div>
<p>在初始化结束之后，如果时单工程构建，其过程很简单。Gradle会寻找命令行传入的任务名，并<span style="background-color: #c0c0c0;">把每个任务作为单独的构建执行</span>。</p>
<div class="blog_h3"><span class="graybg">生命周期事件</span></div>
<p>在生命周期各关键点<span style="background-color: #c0c0c0;">前后</span>，Gradle会发布多种事件（通知），你可以在构建脚本中对这些事件进行处理：</p>
<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>Project evaluation</td>
<td>在工程被evaluate前后，可以得到一个通知：<br />
<pre class="crayon-plain-tag">allprojects {
	afterEvaluate { project -&gt;
		println '$it evaluated'
	}
}</pre>
</td>
</tr>
<tr>
<td>Task creation</td>
<td>在Task被加入到工程前后，可以得到通知：<br />
<pre class="crayon-plain-tag">tasks.whenTaskAdded { task -&gt;
    task.ext.srcDir = 'src/main/java'
}</pre>
</td>
</tr>
<tr>
<td>Task execution</td>
<td>在任务执行前后，可以得到通知：<br />
<pre class="crayon-plain-tag">gradle.taskGraph.beforeTask { Task task -&gt;
	println "executing $task ..."
}

gradle.taskGraph.afterTask { Task task, TaskState state -&gt;
	if (state.failure) {
		println "FAILED"
	}
}</pre>
</td>
</tr>
</tbody>
</table>
<div class="blog_h1"><span class="graybg">多工程（multi-project）构建</span></div>
<p>所谓多工程构建，是指在执行单次Gradle命令时，同时完成多个工程（一个root工程，外加若干个子工程，每个子工程也可以具有自己的子工程）的构建。必须通过<pre class="crayon-plain-tag">settings.gradle</pre> 文件来指定参与到构建中的工程，该脚本中的this指向<pre class="crayon-plain-tag">org.gradle.api.initialization.Settings</pre> 。Gradle依据该脚本构建工程的树状结构，此树具有<span style="background-color: #c0c0c0;">唯一的根目录</span>，每个子工程都具有一个“路径”用于表示其在树中的位置，通常情况下<span style="background-color: #c0c0c0;">路径与子工程在文件系统中的位置一致</span>。默认的，Gradle认为<span style="background-color: #c0c0c0;">settings脚本所在目录就是根工程</span>，该行为也是可配置的。</p>
<div class="blog_h3"><span class="graybg">简单的例子</span></div>
<p>假设有一个包含四个子工程的工程，结构如下：</p>
<pre class="crayon-plain-tag">multiproject/
  api/                      #生成一个JAR包，提供WebService客户端的功能
  services/webservice/      #WebService服务端
  shared/                   #其它三个工程依赖该工程
  services/shared/</pre>
<p>要启用多工程的支持，必须在工程集的根目录添加<pre class="crayon-plain-tag">settings.gradle</pre> 文件，指定哪些工程需要被构建：</p>
<pre class="crayon-plain-tag">include "shared", "api", "services:webservice", "services:shared"</pre>
<p> 根目录通常作为工程的容器，其中可以通过<pre class="crayon-plain-tag">subprojects</pre> 来设置所有子工程共享的配置信息：</p>
<pre class="crayon-plain-tag">//所有子目录下的工程都获得下列配置信息：
subprojects {
    apply plugin: 'java'
    apply plugin: 'eclipse-wtp'

    repositories {
       mavenCentral()
    }

    dependencies {
        testCompile 'junit:junit:4.12'
    }

    version = '1.0'

    jar {
        manifest.attributes provider: 'gradle'
    }
}
//注意，由于是在subprojects段设置，因此根工程不会获得这些配置</pre>
<p>可以声明子工程之间的依赖：</p>
<pre class="crayon-plain-tag">dependencies {
    compile project(':shared')
}</pre>
<p>上面的设置可以确保shared总是在api前面构建，因为后者依赖于前者。 </p>
<div class="blog_h2"><span class="graybg">跨工程的配置</span></div>
<div class="blog_h3"><span class="graybg" style="font-weight: bold;">按需配置</span></div>
<p>默认的，Gradle使用<span style="background-color: #c0c0c0;">全量配置</span>方式：在任何Task执行之前，所有工程都被配置完毕。如果工程数量达到上百个，这会导致配置阶段耗费的时间过于漫长。对不参与本次构建的工程进行配置是不必要的，因此，Gradle 1.4引入了按需配置（Configuration on demand）模式，按需配置<span style="background-color: #c0c0c0;">仅仅去配置那些与被请求执行任务相关的工程</span>。在未来，按需配置可能变为默认或者唯一的配置模式。</p>
<p>在按需配置模式下，工程按照下面的规则配置：</p>
<ol>
<li>根工程总是被配置，这保证典型的公共配置脚本被支持（allprojects和subprojects脚本块）</li>
<li>如果Gradle命令没有指定目标，则当前目录对应的工程被配置。这保证工程默认任务（Default tasks）可以正常工作</li>
<li>依赖工程被配置，如果projectA在编译时依赖于projectB，那么构建projectA会导致projectB也被配置</li>
<li>依赖任务所在工程被配置，例如<pre class="crayon-plain-tag">taskA.dependsOn(":projectB:taskB1")</pre> 导致构建taskA时projectB被配置</li>
<li>通过命令行指定Task的路径，那么路径中牵涉到的工程都会被配置，例如<pre class="crayon-plain-tag">gradle projectA:projectB:someTask</pre> 导致projectB被配置</li>
</ol>
<p>在配置文件<pre class="crayon-plain-tag">gradle.properties</pre> 中指定<pre class="crayon-plain-tag">org.gradle.configureondemand</pre> ，可以启用按需配置模式。</p>
<div class="blog_h3"><span class="graybg">定义公共行为</span></div>
<p>以一个简单的多工程为例，根工程root包含一个子工程sub0：</p>
<pre class="crayon-plain-tag">root/
  build.gradle
  settings.gradle
  sub0/</pre>
<p>settings.gradle的内容如下：<pre class="crayon-plain-tag">include 'sub0'</pre>  。sub0下面的构建脚本文件不是必须的，其构建脚本可以<span style="background-color: #c0c0c0;">直接放在根工程的build.gradle</span>里面：</p>
<pre class="crayon-plain-tag">Closure cl = { task -&gt; println "I'm $task.project.name" }
task hello &lt;&lt; cl
project(':sub0') {
    task hello &lt;&lt; cl
}
# 在根工程目录运行gradle -q hello输出如下：
# I'm root
# I'm sub0
# 注意，所有子工程同名Task会自动执行</pre>
<p>其实，Gradle允许在任何一个工程的构建脚本中访问所有工程，只要调用<pre class="crayon-plain-tag">project(projectName)</pre> 方法即可。</p>
<p>下面的脚本块可以为所有工程添加公共行为：</p>
<pre class="crayon-plain-tag">allprojects {
    task hello &lt;&lt; { task -&gt; println "I'm $task.project.name" }
}</pre>
<p>下面的脚本则为所有子工程添加公共行为：</p>
<pre class="crayon-plain-tag">subprojects {
    hello &lt;&lt; { println "I'm $it.project.name" }
}</pre>
<p>还可以依据各种条件进行过滤，针对符合条件的工程添加行为：</p>
<pre class="crayon-plain-tag">//根据子工程的名称进行过滤
configure(subprojects.findAll {it.name != 'sub1'}) {
    hello &lt;&lt; { println '$it.project.name' }
}</pre>
<div class="blog_h2"><span class="graybg">多工程构建的Task执行规则 </span></div>
<p>当<span style="background-color: #c0c0c0;">在根工程</span>目录执行<pre class="crayon-plain-tag">gradle taskA</pre>  时，根工程的taskA以及所有子工程的taskA任务<span style="background-color: #c0c0c0;">都会被执行</span>。</p>
<p>Gradle支持指定Task的绝对路径：</p>
<pre class="crayon-plain-tag">gradle -q :hello   //根工程的hello任务
:sub0:hello        //sub0工程的hello任务
hello              //当前工程的hello任务</pre>
<p>如果路径以分号开始，则表示是绝对路径（相当于根）。根工程是唯一不需要以名称指代的工程。 </p>
<div class="blog_h3"><span class="graybg">执行依赖</span></div>
<p>要确定Task的执行顺序，可以使用<pre class="crayon-plain-tag">dependsOn</pre> ，例如：</p>
<pre class="crayon-plain-tag">//跨工程的任务依赖
task consume(dependsOn: ':producer:produce') &lt;&lt; {
    println("Consuming message: ${rootProject.producerMessage}")
}</pre>
<p>被依赖的Task总是先被执行。</p>
<div class="blog_h3 blog_h2"><span class="graybg">库（Lib）工程依赖</span></div>
<p>库依赖（lib  dependency）是一种特殊的执行依赖。以Java构建为例，工程构建时，要求作为库的Jar已经生成好（此Jar可能传递性的依赖于其它Jar）并且添加到当前工程的Classpath中。如果这些库作为多工程构建的子工程存在，这就意味着工程之间存在依赖，这种依赖使用<pre class="crayon-plain-tag">dependencies</pre> 块声明：</p>
<pre class="crayon-plain-tag">subprojects {
    // 子工程公共属性
    apply plugin: 'java'
    group = 'cc.gmem.study.gradle'
    version = '1.0'
}

project(':api') {
    configurations {
        spi  //声明一个依赖配置（dependency configurations）
    }
    dependencies {
        compile project(':shared') //声明库依赖：编译时依赖于shared工程
    }
    task spiJar(type: Jar) {
        baseName = 'api-spi'
        dependsOn classes
        from sourceSets.main.output
        include('org/gradle/sample/api/**')
    }
    //声明产出构件
    artifacts {
        spi spiJar
    }
}

project(':services:personService') {
    dependencies {
        compile project(':shared') //声明库依赖
        compile project(path: ':api', configuration: 'spi') //声明库依赖，并指定配置
        testCompile "junit:junit:4.12", project(':api')
    }
}</pre>
<p>如果要禁止依赖工程的构建，可以使用gradle命令的<pre class="crayon-plain-tag">-a</pre> 选项。</p>
<div class="blog_h1"><span class="graybg">构建Java应用</span></div>
<div class="blog_h2"><span class="graybg">添加Java插件</span></div>
<p>Gradle自带了很多插件，这些插件可以完成不同领域的构建任务。Gradle从一开始就很好的支持Java的构建，支持编译、打包、单元测试都构建动作。 </p>
<p>类似与Maven，Gradle的Java插件也是遵从“约定优于配置”的理念，因而Java插件生成了类似Maven的工程结构。</p>
<p>要添加Java插件的支持，在build.gradle中需要声明：<pre class="crayon-plain-tag">apply plugin: 'java'</pre> 。添加该插件后：</p>
<ol>
<li>自动设置src/main/java为Java源码目录，src的其它子目录布局和Maven一致</li>
<li>自动添加若干可用的Task，运行<pre class="crayon-plain-tag">gradle tasks</pre> 可以看到这些Task的详细说明</li>
</ol>
<p>下表列出Java插件提供的常用任务：</p>
<table class="fixed-word-wrap" style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="width: 15%; text-align: center;">分类</td>
<td style="width: 22%; text-align: center;">任务</td>
<td style="text-align: center;">说明 </td>
</tr>
</thead>
<tbody>
<tr>
<td rowspan="5"> 生命周期任务</td>
<td>build</td>
<td>对工程进行完整构建</td>
</tr>
<tr>
<td>buildNeeded</td>
<td>对工程进行完整构建，同时构建<span style="background-color: #c0c0c0;">该工程依赖的</span>所有其它工程</td>
</tr>
<tr>
<td>buildDependents</td>
<td>对工程进行完整构建，同时构建所有<span style="background-color: #c0c0c0;">依赖于该工程</span>的其它工程</td>
</tr>
<tr>
<td>assemble</td>
<td>打印工程中的所有归档文件</td>
</tr>
<tr>
<td>check</td>
<td>执行所有验证任务。其他的插件会加入更多的检查步骤。其它插件会扩展这个任务的行为，例如Checkstyle插件会引发静态代码检查</td>
</tr>
<tr>
<td rowspan="3">源码集任务  </td>
<td>compileSourceSetJava</td>
<td>使用java编译指定源码集中的Java代码</td>
</tr>
<tr>
<td>processSourceSetResources</td>
<td>拷贝指定源码集中的资源文件到资源目录</td>
</tr>
<tr>
<td>sourceSetClasses</td>
<td>Assembles给定源码集中的类和资源文件</td>
</tr>
<tr>
<td rowspan="8">其它</td>
<td>clean</td>
<td>清空构建结果</td>
</tr>
<tr>
<td>compileJava</td>
<td>编译产品（不是测试）Java代码</td>
</tr>
<tr>
<td>processResources</td>
<td>处理产品（不是测试）资源文件</td>
</tr>
<tr>
<td>classes</td>
<td>Assembles产品类和资源文件</td>
</tr>
<tr>
<td>jar</td>
<td>打Jar包</td>
</tr>
<tr>
<td>javadoc</td>
<td>生成Javadoc</td>
</tr>
<tr>
<td>test</td>
<td>通过TestNG或者JUnit运行测试</td>
</tr>
<tr>
<td>uploadArchives</td>
<td>上传构件</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">工程布局</span></div>
<p>与Maven类似，默认有src/main|test/java|resourcs目录，其它源码集存放在：<pre class="crayon-plain-tag">src/sourceSetName/java</pre> 和<pre class="crayon-plain-tag">src/sourceSetName/resources</pre> 中。要改变此默认布局，可以：</p>
<pre class="crayon-plain-tag">sourceSets {
    main {
        java {
            srcDir 'src/java'
        }
        resources {
            srcDir 'src/resources'
        }
    }
}</pre>
<div class="blog_h2"><span class="graybg">添加外部依赖</span></div>
<p>Gradle维护一个和Maven Repository类似的依赖库，默认位于<pre class="crayon-plain-tag">%USERPROFILE%/.gradle/caches/</pre> 。我们可以指定外部仓库的位置，以便从中下载依赖库：</p>
<pre class="crayon-plain-tag">repositories {
    mavenCentral()
}</pre>
<p>使用下面的方式声明当前工程的依赖库：</p>
<pre class="crayon-plain-tag">dependencies {
    compile group: 'commons-collections', name: 'commons-collections', version: '3.2'
    testCompile group: 'junit', name: 'junit', version: '4.+'
}</pre>
<div class="blog_h2"><span class="graybg">设置工程属性</span></div>
<p>Java插件默认给工程添加了很多属性，通过命令<pre class="crayon-plain-tag">gradle properties</pre> 可以查看这些属性的值。你可以在build.gradle中修改属性：</p>
<pre class="crayon-plain-tag">sourceCompatibility = 1.5
version = '1.0'</pre>
<p>也可以在jar的manifest中添加属性：</p>
<pre class="crayon-plain-tag">jar {
    manifest {
        attributes 'Implementation-Title': 'Gradle Quickstart',
                   'Implementation-Version': version
    }
}</pre>
<p>或者在测试阶段添加一个系统属性：</p>
<pre class="crayon-plain-tag">test {
    systemProperties 'property': 'value'
}</pre>
<div class="blog_h2"><span class="graybg">发布构建结果</span></div>
<p>可以声明把构建结果发布到仓库中，下面示例了如何发布到本地目录：</p>
<pre class="crayon-plain-tag">uploadArchives {
    repositories {
       flatDir {
           dirs 'repos'
       }
    }
}</pre>
<div class="blog_h2"><span class="graybg">转换为Eclipse工程</span></div>
<p>添加插件：<pre class="crayon-plain-tag">apply plugin: 'eclipse'</pre> ，然后运行命令<pre class="crayon-plain-tag">gradle eclipse</pre> ，即可自动依据build.gradle生成对应的Eclipse工程元数据，可以导入到Eclipse工作区中。</p>
<div class="blog_h2"><span class="graybg">支持注解处理器</span></div>
<p>Annotation processing是Java 1.5引入的功能，允许在编译期进行代码检查、代码生成等操作。</p>
<p>要让Gradle能自动调用注解处理器，可以使用net.ltgt.apt插件：</p>
<pre class="crayon-plain-tag">buildscript {
  repositories {
    maven {
      url "https://plugins.gradle.org/m2/"
    }
  }
  dependencies {
    classpath "net.ltgt.gradle:gradle-apt-plugin:0.21"
  }
}

apply plugin: "net.ltgt.apt"</pre>
<p>然后，执行gradle build会调用注解处理器。</p>
<p>如果使用Intellij IDEA作为开发工具，可以将插件更换为：</p>
<pre class="crayon-plain-tag">apply plugin: "net.ltgt.apt-idea"</pre>
<p>这样，如果注解处理器生成了源代码，这些源代码的目录自动变为source root。</p>
<div class="blog_h2"><span class="graybg">可执行自包含JAR</span></div>
<pre class="crayon-plain-tag">task fatJar( type: Jar ) {
    manifest {
        attributes 'Implementation-Title': project.name,
                   'Implementation-Version': version,
                   'Main-Class': 'cc.gmem.yun.alcm.repository.source.GrpcServer'
    }
    baseName = project.name + '-all'
    from {
        configurations.compile.collect {
            it.isDirectory() ? it : zipTree( it ).matching {
                it.exclude(
                    'META-INF/*.SF', 'META-INF/*.DSA', 'META-INF/*.RSA', 'META-INF/DEPENDENCIES', 'META-INF/INDEX.LIST',
                    'META-INF/LICENSE*', 'META-INF/NOTICE*', 'META-INF/README*'
                )
            }
        }
    }
    with jar
} </pre>
<div class="blog_h1"><span class="graybg">Maven支持</span></div>
<div class="blog_h2"><span class="graybg">本地仓库</span></div>
<p>如果你希望把构件安装到Maven本地仓库，则需要此插件：</p>
<pre class="crayon-plain-tag">apply plugin: 'maven'</pre>
<p>安装此插件后，可以使用如下任务：</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>install</td>
<td>
<p>安装构建的产出为Maven构件，并保存到本地仓库</p>
<p>可以使用系统属性指定本地仓库的位置：</p>
<pre class="crayon-plain-tag">-Dmaven.repo.local=the/path/of/the/folder</pre>
</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">远程仓库</span></div>
<p>需要使用到插件：
<pre class="crayon-plain-tag">apply plugin: 'maven-publish'</pre><br />
<pre class="crayon-plain-tag">publishing {
    publications {
        maven( MavenPublication ) {
            // 源码在哪
            from components.java
            // 发布JavaDocs和源码
            artifact javadocJar
            artifact sourcesJar

            // 编辑POM内容
            pom {
                name = project.group + ":" + project.name
                url = 'https://github.com/grpc/grpc-java'
                afterEvaluate {
                    description = project.description
                }

                scm {
                    connection = 'scm:git:https://github.com/grpc/grpc-java.git'
                    developerConnection = 'scm:git:git@github.com:grpc/grpc-java.git'
                    url = 'https://github.com/grpc/grpc-java'
                }

                licenses {
                    license {
                        name = 'Apache 2.0'
                        url = 'https://opensource.org/licenses/Apache-2.0'
                    }
                }

                developers {
                    developer {
                        id = "grpc.io"
                        name = "gRPC Contributors"
                        email = "grpc-io@googlegroups.com"
                        url = "https://grpc.io/"
                        organization = "gRPC Authors"
                        organizationUrl = "https://www.google.com"
                    }
                }

                withXml {
                    if ( !( project.name in
                        [
                            "grpc-stub",
                            "grpc-protobuf",
                            "grpc-protobuf-lite",
                        ] ) ) {
                        asNode().dependencies.'*'.findAll() { dep -&gt;
                            dep.artifactId.text() in [ 'grpc-api', 'grpc-core' ]
                        }.each() { core -&gt;
                            core.version*.value = "[" + core.version.text() + "]"
                        }
                    }
                }
            }
        }
    }
    // 远程仓库配置
    repositories {
        maven {
                credentials {
                    if ( rootProject.hasProperty( 'pacloudUsername' ) &amp;&amp; rootProject.hasProperty( 'pacloudPassword' ) ) {
                        username = rootProject.pacloudUsername
                        password = rootProject.pacloudPassword
                    }
                }
                def releaseUrl = 'https://nexus.pacloud.io/repository/maven-releases/'
                def snapshotUrl = 'https://nexus.pacloud.io/repository/maven-snapshots/'
                url = version.endsWith( 'SNAPSHOT' ) ? snapshotUrl : releaseUrl
            }
        }
    }
}</pre>
<p>执行下面的命令，完成打包和发布：</p>
<pre class="crayon-plain-tag">gradle sourceJar
gradle publishToMavenLocal</pre>
<div class="blog_h1"><span class="graybg">构建Groovy应用</span></div>
<p>Gradle提供了Groovy插件：</p>
<pre class="crayon-plain-tag">apply plugin: 'eclipse'
apply plugin: 'groovy'

repositories {
    mavenCentral()
}

dependencies {
    compile 'org.codehaus.groovy:groovy-all:2.3.7'
    testCompile 'junit:junit:4.11'
}</pre>
<p>该插件引入了以下任务： </p>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="text-align: center;">任务</td>
<td style="text-align: center;">依赖于</td>
<td style="text-align: center;">被依赖</td>
<td style="text-align: center;">说明 </td>
</tr>
</thead>
<tbody>
<tr>
<td>compileGroovy</td>
<td>compileJava</td>
<td>classes</td>
<td>编译产品Groovy代码</td>
</tr>
<tr>
<td>compileTestGroovy</td>
<td>compileTestJava</td>
<td>testClasses</td>
<td>编译测试Groovy代码</td>
</tr>
<tr>
<td>compileSourceSetGroovy</td>
<td>compileSourceSetJava</td>
<td>sourceSetClasses</td>
<td>编译指定的Groovy源码集</td>
</tr>
<tr>
<td>groovydoc</td>
<td> </td>
<td> </td>
<td>生成Groovy文档</td>
</tr>
</tbody>
</table>
<p>Groovy工程的目录布局与Java工程类似，新引入src/main/groovy、src/test/groovy目录存放Groovy源码。</p>
<p>如果要改变默认工程目录布局，可以：</p>
<pre class="crayon-plain-tag">sourceSets {
    main {
        groovy {
            srcDirs = ['src/groovy']
        }
    }

    test {
        groovy {
            srcDirs = ['test/groovy']
        }
    }
}</pre>
<div class="blog_h1"><span class="graybg">构建Web应用</span></div>
<p>Gradle提供了两个和Web应用相关的插件：</p>
<ol>
<li>War插件：在Java插件的基础上进行扩展，支持构建war包 </li>
<li>Jetty插件：允许发布War到Jetty容器</li>
</ol>
<div class="blog_h2"><span class="graybg">使用War插件</span></div>
<p>要添加War插件，可以：</p>
<pre class="crayon-plain-tag">apply plugin: 'war'
//注意，该插件会自动引入Java插件</pre>
<p>运行<pre class="crayon-plain-tag">gradle build</pre>  可以编译、测试并打war包。 </p>
<p>War插件引入以下任务：</p>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="text-align: center;">任务</td>
<td style="text-align: center;">依赖于</td>
<td style="text-align: center;">被依赖</td>
<td style="text-align: center;">说明 </td>
</tr>
</thead>
<tbody>
<tr>
<td>war</td>
<td>compile</td>
<td>assemble</td>
<td>打包Java的Web归档（war）</td>
</tr>
</tbody>
</table>
<p>Web资源，例如JavaScript、CSS、HTML、JSP等，默认存放到src/main/webapp目录中。</p>
<p>War插件引入了两个新的依赖配置：</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>providedCompile</td>
<td rowspan="2">与compile、runtime类似，但是它们不会被打包到war中</td>
</tr>
<tr>
<td>providedRuntime</td>
</tr>
</tbody>
</table>
<p>下面的脚本示例了如何定制War插件的属性：</p>
<pre class="crayon-plain-tag">configurations {
   moreLibs
}

repositories {
   flatDir { dirs "lib" } //把lib目录作为依赖仓库
   mavenCentral()
}
dependencies {
    providedCompile "javax.servlet:servlet-api:2.5"
}

war {
    from 'src/rootContent' // 添加文件集到war中 
    webInf { from 'src/additionalWebInf' } // 添加文件夹到WEB-INF目录下
    classpath fileTree('additionalLibs') // 添加文件夹
    classpath configurations.moreLibs // 添加一个配置到WEB-INF/lib目录下
    webXml = file('src/someWeb.xml') // 拷贝文件为WEB-INF/web.xml
}</pre>
<div class="blog_h2"><span class="graybg">使用Jetty插件</span></div>
<p>Jetty插件继承自War插件，可以通过下面的脚本添加该插件：</p>
<pre class="crayon-plain-tag">apply plugin: 'jetty'</pre>
<p>运行<pre class="crayon-plain-tag">gradle jettyRunWar</pre> 可以打包并部署到Jetty中运行。 </p>
<p>Jetty插件引入以下任务：</p>
<table style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="text-align: center;">任务</td>
<td style="text-align: center;">依赖于</td>
<td style="text-align: center;">被依赖</td>
<td style="text-align: center;">说明 </td>
</tr>
</thead>
<tbody>
<tr>
<td>jettyRun</td>
<td>compile</td>
<td> </td>
<td>运行Jetty实例，并把解开的构建目录部署上去</td>
</tr>
<tr>
<td>jettyRunWar</td>
<td>war</td>
<td> </td>
<td>运行Jetty实例，并把打包后的war部署上去</td>
</tr>
<tr>
<td>jettyStop</td>
<td> </td>
<td> </td>
<td>停止Jetty实例</td>
</tr>
</tbody>
</table>
<p>下面的脚本示例了如何定制Jetty插件的属性：</p>
<pre class="crayon-plain-tag">jetty {
    contextPath '上下文路径，默认war的名称'
    httpPort '监听端口，默认8080'
    stopPort '管理端口'
    stopKey  '要求Jetty停止时，传递给他的key'
}</pre>
<div class="blog_h1"><span class="graybg">构建Android应用</span></div>
<p>Gradle是当前Android官方IDE—— Android Studio内置的构建工具，你可以脱离AS，直接通过命令行调用Gradle，完成Android工程的构建。</p>
<p>通过编写构建脚本， 你可以配置Android构建的如下几个方面：</p>
<ol>
<li>构建变体（Build variants）：可以生成多个不同配置的APK，例如Debug/Release配置，又例如针对不同的体系结构（x86、arm）</li>
<li>Android库依赖</li>
<li>Manifest条目：可以在构建变体配置中<span style="background-color: #c0c0c0;">覆盖</span>AndroidManifest.xml中某些元素的取值。如果需要创建多个具有不同应用名称、最小SDK版本要求、目标SDK版本的APK，可以设置Manifest条目。如果有多个Manifest文件，会根据buildType、productFlavor、/main Manifest、library Manifest的优先顺序自动合并</li>
<li>签名：可以指定数字签名参数，在构建过程中对APK进行签名</li>
<li>ProGuard：允许配置ProGuard，对APK进行混淆、裁剪和优化</li>
</ol>
<div class="blog_h2"><span class="graybg">工程与模块</span></div>
<p>Android Studio<span style="background-color: #c0c0c0;">区分工程（Project）、模块（两个概念）</span>：</p>
<ol>
<li>工程：表示一个顶级的Android开发结构（相当于Gradle的根工程），一个工程中可以包含多个模块（相当于Gradle子工程）</li>
<li>模块：表示一个可以独立构建、测试、调试的组件</li>
</ol>
<p>AS支持以下类型的模块：</p>
<ol>
<li>Android Application Module：包含手机、电视 或手表的程序代码和资源，依赖于Library Module。构建系统为此类模块生成APK归档</li>
<li>Android Library Module：可重用的Android程序代码和资源。构建系统为此类模块生成AAR（Android ARchive）归档</li>
<li>App Engine Module：包含用于与Google App Engine集成的代码和资源</li>
<li>Java Library Module：包含可重用的Java代码。构建系统为此类模块生成JAR归档</li>
</ol>
<p>AS为工程和每个模块都生成Gradle构建脚本（build.gradle），这些脚本完全遵守Gradle的规范。</p>
<p>Android Studio工程包含了一个Gradle Wrapper，此Wrapper包含一个Gradle jar、一个属性文件、Windows/Linux的Shell脚本。使用Gradle Wrapper可以确保总是运行 <pre class="crayon-plain-tag">local.properties</pre> 中声明的Gradle版本。你可以修改此文件，以使用不同的Gradle版本。</p>
<div class="blog_h2"><span class="graybg">Hello World</span></div>
<div class="blog_h3"><span class="graybg">工程构建文件</span></div>
<p>可以指定Android工程的依赖，以及所有模块的公共依赖：</p>
<pre class="crayon-plain-tag">//构建本身的依赖配置
buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        //使用特定版本的Gradle
        classpath 'com.android.tools.build:gradle:1.0.1'
    }
}
//针对Android工程和所有模块的依赖配置
allprojects {
   repositories {
       jcenter()
   }
}</pre>
<div class="blog_h3"><span class="graybg">模块构建文件 </span></div>
<p>每个模块都可以有自己的构建脚本，要对模块启用Android构建的支持，必须应用android插件：</p>
<pre class="crayon-plain-tag">//注意，不得同时应用java插件，会导致构建错误
apply plugin: 'android'
//或者：
apply plugin: 'com.android.application'</pre>
<p>可以在构建脚本中覆盖src/main/AndroidManifest.xml中的设置，或者对打包选项进行定制：</p>
<pre class="crayon-plain-tag">android {
    //Android 基本设置
    compileSdkVersion 16
    buildToolsVersion "23.0.2"
    
    //覆盖AndroidManifest.xml中的manifest properties设置
    defaultConfig {
        applicationId "cc.gmem.androidstudy"
        minSdkVersion 15
        targetSdkVersion 16
        versionCode 1
        versionName "1.0"
    }
    //构建类型设置
    //默认情况下，Android插件同时创建两个构建类型：Debug、Release。两者的主要区别是：
    //Debug使用自动创建的密钥签名，而Release版本在构建时不签名
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
        debug {
            debuggable true
            //设置ApplicationId，这样设备上可以同时安装Debug版本和Release版本的APK
            applicationIdSuffix ".debug"
        }
        //从debug衍生一个构建类型
        jnidebug {
            initWith(buildTypes.debug)
            packageNameSuffix ".jnidebug"
            jniDebuggable true
        }
    }
}
//声明一些依赖项
dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:16.+'
}</pre>
<p>对于每个新创建的构建类型，Android插件自动创建Task：<pre class="crayon-plain-tag">assemble&lt;BuildTypeName&gt;</pre>  </p>
<div class="blog_h2"><span class="graybg">工程结构</span></div>
<p>Android插件默认使用如下的工程结构：</p>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 30%; text-align: center;">目录</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>src/main/java</td>
<td rowspan="2">
<p>主源码集</p>
<p>Android的Manifest文件位于：src/main/AndroidManifest.xml</p>
<p>其它子目录包括res、assets、aidl、rs、jni、jniLibs等</p>
</td>
</tr>
<tr>
<td>src/main/resources</td>
</tr>
<tr>
<td>src/androidTest/java</td>
<td rowspan="2">Android测试源码集</td>
</tr>
<tr>
<td>src/androidTest/resources</td>
</tr>
</tbody>
</table>
<p>如果默认工程结构不能满足需要，可以使用如下脚本定制：</p>
<pre class="crayon-plain-tag">android {
    sourceSets {
        //此闭包修改源码集的默认设置
        main {
            manifest.srcFile 'AndroidManifest.xml' //指定Manif文件名称
            //指定子目录位置
            java.srcDirs = ['src']
            resources.srcDirs = ['src']
            aidl.srcDirs = ['src']
            renderscript.srcDirs = ['src']
            res.srcDirs = ['res']
            assets.srcDirs = ['assets']
        }
        androidTest.setRoot('tests') //把整个androidTest源码集移动到tests目录
    }
}</pre>
<div class="blog_h2"><span class="graybg">构建任务</span></div>
<p>Android插件自动引入以下Task：</p>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">任务</td>
<td style="text-align: center;">任务描述</td>
<td style="width: 30%; text-align: center;">备注</td>
</tr>
</thead>
<tbody>
<tr>
<td>assemble</td>
<td>收集工程的输出文件并打包。对于Android工程，至少输出两个APK包：Debug/Release，它们分别由任务assembleDebug、assembleRelease完成。assemble依赖于这两个任务</td>
<td rowspan="3">
<p>和Java插件一样，Android引入这三个一般性Task。这些Task本身不做实质性的工作，它们只是作为anchor，把工作委托给其它Task执行</p>
<p>不同插件使用风格一致的一般任务命名约定，可以简化构建</p>
</td>
</tr>
<tr>
<td>check</td>
<td>运行所有检查。Android插件支持基于lint的静态检查</td>
</tr>
<tr>
<td>build</td>
<td>同时执行assemble和check</td>
</tr>
<tr>
<td>clean</td>
<td colspan="2">清除工程输出 </td>
</tr>
<tr>
<td>connectedCheck</td>
<td colspan="2">检查是否有连接上的设备或者模拟器  </td>
</tr>
<tr>
<td>deviceCheck</td>
<td colspan="2">检查设备是否支持API </td>
</tr>
<tr>
<td>installDebug</td>
<td colspan="2">安装Debug版本的APK到设备</td>
</tr>
<tr>
<td>installRelease</td>
<td colspan="2">安装Release版本的APK到设备</td>
</tr>
<tr>
<td>uninstallAll</td>
<td colspan="2">从设备上卸载所有版本的APK，依赖于：uninstallDebug、uninstallRelease、uninstallDebugAndroidTest</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">配置APK签名</span></div>
<p>默认的，Debug构建的keystore存放在<pre class="crayon-plain-tag">$HOME/.android/debug.keystore</pre> ，如果不存在会自动创建。下面的脚本块示例了如何创建新的签名配置（signingConfigs ）并且将其应用到某个构建类型：</p>
<pre class="crayon-plain-tag">android {
    signingConfigs {
        debug {
            storeFile file("debug.keystore")
        }
        //创建新的签名配置
        myConfig {
            storeFile file("other.keystore")
            storePassword "android"
            keyAlias "androiddebugkey"
            keyPassword "android"
        }
    }

    buildTypes {
        foo {
            //应用签名配置
            signingConfig signingConfigs.myConfig
        }
    }
}</pre>
</div><p>The post <a rel="nofollow" href="https://blog.gmem.cc/gradle-study-note">Gradle学习笔记</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.gmem.cc/gradle-study-note/feed</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>基于Eclipse ADT和Maven的Android开发</title>
		<link>https://blog.gmem.cc/eclipse-adt-and-mvn-based-android-dev</link>
		<comments>https://blog.gmem.cc/eclipse-adt-and-mvn-based-android-dev#comments</comments>
		<pubDate>Fri, 08 Aug 2014 03:04:52 +0000</pubDate>
		<dc:creator><![CDATA[Alex]]></dc:creator>
				<category><![CDATA[Android]]></category>

		<guid isPermaLink="false">https://blog.gmem.cc/?p=9643</guid>
		<description><![CDATA[<p>Android Studio目前作为Android官方的IDE，它使用Gradle作为构建工具，对Android平台引入的新特性都能够很快的支持。相比之下，Eclipse ADT则更新缓慢，与最常用的构建工具——Maven的集成主要依靠开源界的个人开发者的力量，因而问题较多。 使用Android Studio进行Android开发的确是理想的选择，但是，对于长期习惯于Eclipse + Maven的开发团队来说，Android Studio + Gradle的组合有不小的学习、转换成本。况且很多团队同时负责Android和传统的JavaEE开发，难以摆脱对大量Eclipse、Maven插件的依赖，使用Android Studio后团队需要同时熟悉两套开发工具。 我们团队采用了Eclipse ADT + Maven的组合，本文记录开发环境的搭建过程，开发过程中遇到的问题及其解决方案，备忘并供各位参考。 安装和配置 安装Android SDK 安装Eclipse ADT或者Android Studio之一，都会自动安装Android SDK管理工具。亦可下载独立安装包。 <a class="read-more" href="https://blog.gmem.cc/eclipse-adt-and-mvn-based-android-dev">[...]</a></p>
<p>The post <a rel="nofollow" href="https://blog.gmem.cc/eclipse-adt-and-mvn-based-android-dev">基于Eclipse ADT和Maven的Android开发</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>Android Studio目前作为Android官方的IDE，它使用Gradle作为构建工具，对Android平台引入的新特性都能够很快的支持。相比之下，Eclipse ADT则更新缓慢，与最常用的构建工具——Maven的集成主要依靠开源界的个人开发者的力量，因而问题较多。</p>
<p>使用Android Studio进行Android开发的确是理想的选择，但是，对于长期习惯于Eclipse + Maven的开发团队来说，Android Studio + Gradle的组合有不小的学习、转换成本。况且很多团队同时负责Android和传统的JavaEE开发，难以摆脱对大量Eclipse、Maven插件的依赖，使用Android Studio后团队需要同时熟悉两套开发工具。</p>
<p>我们团队采用了Eclipse ADT + Maven的组合，本文记录开发环境的搭建过程，开发过程中遇到的问题及其解决方案，备忘并供各位参考。</p>
<div class="blog_h1"><span class="graybg">安装和配置</span></div>
<div class="blog_h2"><span class="graybg">安装Android SDK</span></div>
<p>安装Eclipse ADT或者Android Studio之一，都会自动安装Android SDK管理工具。亦可下载<a href="http://developer.android.com/sdk/installing/index.html?pkg=tools">独立安装包</a>。</p>
<p>安装完毕后，设置如下环境变量：</p>
<ol>
<li><pre class="crayon-plain-tag">ANDROID_HOME</pre> 设置为Android SDK安装位置，例如D:\Android\sdk</li>
<li><pre class="crayon-plain-tag">PATH</pre> 中添加 <pre class="crayon-plain-tag">%ANDROID_HOME%\tools</pre> 、<pre class="crayon-plain-tag">%ANDROID_HOME%\platform-tools</pre> </li>
</ol>
<p>%ANDROID_HOME%目录下的AVD Manager.exe、SDK Manager.exe分别是Android模拟器管理工具、SDK管理器。打开SDK管理器，根据需要安装SDK版本和工具。通常需要安装：最新的、以及项目需要的Android版本的<span style="background-color: #c0c0c0;">SDK Platform、ARM/Intel的System Image、Google APIS、Sources for Android SDK。以及Extra下的全部项目</span>。</p>
<p>如果使用模拟器开发调试，并且你的机器是基于Intel的Windows，则应该在下载完毕后，安装位于extras\intel\Hardware_Accelerated_Execution_Manager目录下的HAXM，可以在很大程度上提高模拟器的性能。</p>
<div class="blog_h2"><span class="graybg">Eclipse和Maven插件</span></div>
<table class="fixed-word-wrap" style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="width: 30%; text-align: center;">插件</td>
<td style="text-align: center;">说明 </td>
</tr>
</thead>
<tbody>
<tr>
<td>Android Development Tools</td>
<td>
<p>即ADT，提供Android应用的集成开发环境</p>
<p>Eclipse Marketplace搜索ADT安装</p>
</td>
</tr>
<tr>
<td><a href="http://rgladwell.github.io/m2e-android/">Android for Maven Eclipse</a></td>
<td>
<p>即m2e-android，一个M2E Connector，是Maven插件与ADT、Andmore之间的桥梁。可以依据POM中的配置生成ADT工程结构、维护Android和Maven依赖。</p>
<p>插件Update site：http://rgladwell.github.io/m2e-android/updates/，或者Eclipse Marketplace搜索m2e-android安装</p>
</td>
</tr>
<tr>
<td><a href="http://simpligility.github.io/android-maven-plugin/">Android Maven Plugin</a></td>
<td>
<p>一个Maven插件。通过Maven构建Android应用程序或者库（aar或者apklib格式），该插件支持：</p>
<ol>
<li>运行Android Lint（静态代码分析工具）</li>
<li>通过ProGuard进行混淆或优化</li>
<li>运行Zipalign对程序包进行优化</li>
<li>和连接的设备进行交互：包括部署、解除部署、自动化测试（instrumentation tests）等</li>
</ol>
<p>该插件需要Maven运行时版本不小于3.1.1。通过m2e-android，该插件的大部分功能可以很好的在Eclipse下使用</p>
<p>该插件当前版本为4.3.0，和3.8比起来变动较大，插件的groupId已经改为：com.simpligility.maven.plugins</p>
</td>
</tr>
<tr>
<td><a href="https://maven.apache.org/plugins/maven-jarsigner-plugin/">maven-jarsigner-plugin</a></td>
<td>
<p>一个Maven插件，提供对APK进行数字签名的能力，该插件也可以对jar等其它构件进行数字签名</p>
</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">Maven Android SDK Deployer</span></div>
<p>Maven中心仓库中，更新Android SDK构件的速度非常慢，这可能是因为Android官方不使用Maven作为构建工具的原因。</p>
<p>我们可以使用<a href="https://github.com/simpligility/maven-android-sdk-deployer">Maven Android SDK Deployer</a>补充Android的Maven构件，该工具可以批量的安装Android SDK组件到Maven仓库，其本质上是一个Maven工程，运行时需要Maven 3.1.1+支持。</p>
<p>使用该工具前，设置好ANDROID_HOME，打开%ANDROID_HOME%\SDK Manager.exe：</p>
<ol>
<li>下载所需版本的SDK Platform。检查对应的%ANDROID_HOME%\platforms\android-N目录存在（N为SDK数字版本号）</li>
<li>下载所需版本的Addon Google APIs。检查对应的%ANDROID_HOME%\add-ons\addon-google_apis-google-N目录存在</li>
</ol>
<p>下载完毕后，执行下面的命令安装SDK组件到Maven仓库：</p>
<pre class="crayon-plain-tag">cd D:\Android\tools
rem 从GitHub下载maven-android-sdk-deployer
git clone https://github.com/simpligility/maven-android-sdk-deployer.git maven-android-sdk-deployer
cd maven-android-sdk-deployer
rem 安装4.1版本的SDK组件到Maven仓库
set M2_HOME=D:\JavaEE\maven\3.3
rem 如果要安装所有SDK版本的构件到Maven仓库，你需要预先下载所有版本的Android SDK，并执行mvn install
mvn install -P 4.1</pre>
<div class="blog_h3"><span class="graybg">构件命名规则</span></div>
<p>Maven Android SDK Deployer依照下面的规则命名SDK组件对应的Maven构件</p>
<table class="fixed-word-wrap" style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="width: 20%; text-align: center;">构件</td>
<td style="text-align: center;">groupId</td>
<td style="text-align: center;">artifactId </td>
<td style="text-align: center;">中心仓库等价构件</td>
</tr>
</thead>
<tbody>
<tr>
<td>核心Android API</td>
<td>android</td>
<td>android</td>
<td>com.google.android:android</td>
</tr>
<tr>
<td>Google地图API</td>
<td>com.google.android.maps</td>
<td>maps</td>
<td> </td>
</tr>
<tr>
<td>USB Addon</td>
<td>com.android.future</td>
<td>usb</td>
<td> </td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">Eclipse偏好设置</span></div>
<p>ADT相关的全局设置位于Window ⇨ Preferences ⇨ Android下：</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>Android</td>
<td>SDK Location：设置为Android SDK的安装位置。点击Apply会更新可用SDK列表</td>
</tr>
<tr>
<td>Android/Launch</td>
<td>Default emulator options：设置模拟器默认启动选项：<br />
<pre class="crayon-plain-tag"># 设置模拟器联网时使用的代理 
-http-proxy 192.168.0.89:8086  
# 设置模拟器使用的DNS服务器
-dns-server 178.79.131.110,223.5.5.5</pre>
</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">创建和使用模拟器</span></div>
<p>在Eclipse中，打开Window ⇨ Android Virtual Devices Manager，看到如下界面：</p>
<p><img class="aligncenter size-full wp-image-10312" src="https://blog.gmem.cc/wp-content/uploads/2014/08/avd-mgr.jpg" alt="avd-mgr" width="713" height="300" /></p>
<p>点击右侧按钮Start，可以启动既有的模拟器。</p>
<p>点击右侧按钮Create，创建一个模拟器，参数设置如下：</p>
<p><img class="aligncenter size-full wp-image-10313" src="https://blog.gmem.cc/wp-content/uploads/2014/08/create-avd.jpg" alt="create-avd" width="411" height="695" /></p>
<p>点击OK，即可完成模拟器创建。</p>
<div class="blog_h3"><span class="graybg">模拟器常用快捷键</span></div>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">模拟器按钮</td>
<td style="text-align: center;">对应开发机按键</td>
</tr>
</thead>
<tbody>
<tr>
<td>返回设备主界面</td>
<td>HOME</td>
</tr>
<tr>
<td>后退到上一界面</td>
<td>ESC</td>
</tr>
<tr>
<td>电源按钮</td>
<td>F7</td>
</tr>
<tr>
<td>横屏切换</td>
<td>Ctrl-F12</td>
</tr>
</tbody>
</table>
<div class="blog_h1"><span class="graybg">创建Android开发工程</span></div>
<p>本节介绍两种创建ADT工程的方法，推荐使用第二种方式，以便获得Maven工程的诸多优势。</p>
<div class="blog_h2"><span class="graybg">创建ADT工程</span></div>
<p>在不使用Maven的情况下，可以直接创建Android应用工程。点击Eclipse菜单：File ⇨ New ⇨ Android Application Project，弹出如下对话框：</p>
<p><img class="aligncenter size-full wp-image-9655" src="https://blog.gmem.cc/wp-content/uploads/2013/05/adt-application-wizard.jpg" alt="adt-application-wizard" width="636" height="543" /></p>
<p>各字段说明如下：</p>
<ol>
<li>Application Name：应用程序的名称，会显示在应用商店、设置（Settings）程序中</li>
<li> Project Name：即Eclipse工程的名称</li>
<li>Package Name：作为一个<span style="background-color: #c0c0c0;">应用程序（不管版本如何变迁）</span>的唯一标识符，同时也作为Java的包名</li>
<li>Minimum Required SDK：运行时需要的SDK最低版本，版本越低则可用特性越少</li>
<li>Target SDK：此应用可支持的最高SDK版本，用于提示低于/等于此版本的运行时系统，不去执行兼容性行为</li>
<li>Compile With：使用哪个已安装的SDK版本来编译程序。通常使用最近的SDK，或者支持所有你想直接使用（不通过反射）的API的某个SDK</li>
<li>Theme：应用程序的基本主题风格</li>
</ol>
<p>填写好上述字段后，点击 Next，显示如下对话框：</p>
<p><img class="aligncenter size-full wp-image-9657" src="https://blog.gmem.cc/wp-content/uploads/2013/05/adt-application-wizard-2.jpg" alt="adt-application-wizard-2" width="657" height="590" /></p>
<p>如果取消勾选Create custom launcher icon、Create activity，则可以结束向导，完成工程创建。否则，后面两个步骤分别用来定制应用图标、选择一个指定布局方式的Activity。</p>
<p>勾选Mark this project as a library，则当前工程被作为Android库看待。</p>
<div class="blog_h3"><span class="graybg">ADT工程结构说明</span></div>
<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>gen</td>
<td>ADT自动生成的Java文件</td>
</tr>
<tr>
<td>src</td>
<td>源代码文件，如果使用Maven，默认Java源码放在src/main/java</td>
</tr>
<tr>
<td>assets</td>
<td>资产文件，使用原始的流方式使用该目录中的各种文件</td>
</tr>
<tr>
<td>res</td>
<td>
<p>资源文件，例如图片、图标、音频格式、XML文件等等</p>
<p>每个资源文件都会在R.java中自动分配一个唯一的预编译ID（unique precompiled ID）</p>
</td>
</tr>
<tr>
<td>bin</td>
<td>二进制文件，存放apk文件。使用Maven时，apk默认生成到target而不是bin目录</td>
</tr>
<tr>
<td>project.properties</td>
<td>Android工程属性设置：<br />
<pre class="crayon-plain-tag"># 目标SDK版本，此版本的SDK讲作为当前工程的依赖库
target=android-22
# 当前工程是否为一个Android Library工程
android.library=true
# 使用的Proguard设置
proguard.config=proguard-project.txt</pre>
</td>
</tr>
<tr>
<td>AndroidManifest.xml</td>
<td>
<p>包含Android应用程序最基本的信息，系统在运行应用之前，必须知道这些信息。该文件包含以下方面的内容：</p>
<ol>
<li>作为应用唯一标识的Java包名称</li>
<li>描述应用包含的组件：Activities、Services、Broadcast receivers、Content providers，包括这些组件的类名、capabilities。这些描述信息让系统知道应用包括哪些组件，何时可以启动这些组件</li>
<li>确定什么进程用于运行应用组件</li>
<li>声明应用程序需要哪些权限，以访问受保护的API，或者和其他应用交互</li>
<li>声明其它想和当前应用程序组件交互的应用程序需要的权限</li>
<li>列出Instrumentation类，以便在开发、 测试期间能够获得应用程序的profiling信息</li>
<li>声明应用程序需要的最低Android API版本</li>
<li>列出应用程序必须链接的库</li>
</ol>
</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">创建打包为Android应用的Maven工程</span></div>
<p>可以直接创建Maven Project，由m2e-android负责生成ADT工程结构：</p>
<ol>
<li>点击Eclipse菜单：File ⇨ New ⇨ Maven Project，在第二步选择groupId为de.akquinet.android.archetypes、artifactId为android-quickstart的Maven原型。如果搜索不到此原型，点击Add Archetype添加<img class="aligncenter size-full wp-image-10281" src="https://blog.gmem.cc/wp-content/uploads/2014/08/add-android-archetype.jpg" alt="add-android-archetype" width="525" height="281" /></li>
<li>点击Next，输入Group Id、Artiface Id等信息，并填写原型属性：
<ol>
<li>platform为目标Android SDK版本</li>
<li>android-plugin-version为android-maven-plugin的版本</li>
</ol>
</li>
<li>点击Finish，完成工程创建</li>
</ol>
<p>注意：原型android-quickstart是基于android-maven-plugin 3.x的，android-maven-plugin 4.x工程目录结构变化较大，需要手工调整。调整步骤可以参考<a href="#migrate-to-amp-4">迁移到4.x版本</a>。也参考下面的完整POM手工创建工程。</p>
<p>为便于表述，本节后续内容称此Maven工程为apk工程。</p>
<p>需要注意的地方：</p>
<ol>
<li>如果使用3.8.x版本的android-maven-plugin，Eclipse可能提示错误：“Plugin execution not covered by lifecycle configuration: com.jayway.maven.plugins.android.generation2:android-maven-plugin:3.8.2:consume-aar (execution: default-consume-aar, phase: compile)”。consume-aar是android-maven-plugin添加的用来支持Android新的库归档格式（AAR）的Goal，但是m2e-android、ADT暂时均不支持AAR，这是出现错误的原因。修改工程的POM可以屏蔽此错误<br />
<pre class="crayon-plain-tag">&lt;pluginManagement&gt;
    &lt;plugins&gt;
        &lt;plugin&gt;
            &lt;groupId&gt;org.eclipse.m2e&lt;/groupId&gt;
            &lt;artifactId&gt;lifecycle-mapping&lt;/artifactId&gt;
            &lt;version&gt;1.0.0&lt;/version&gt;
            &lt;configuration&gt;
                &lt;lifecycleMappingMetadata&gt;
                    &lt;pluginExecutions&gt;
                        &lt;pluginExecution&gt;
                            &lt;pluginExecutionFilter&gt;
                                &lt;groupId&gt;com.jayway.maven.plugins.android.generation2&lt;/groupId&gt;
                                &lt;artifactId&gt;android-maven-plugin&lt;/artifactId&gt;
                                &lt;versionRange&gt;${android.plugin.version}&lt;/versionRange&gt;
                                &lt;goals&gt;
                                    &lt;goal&gt;consume-aar&lt;/goal&gt;
                                &lt;/goals&gt;
                            &lt;/pluginExecutionFilter&gt;
                            &lt;action&gt;
                                &lt;ignore /&gt;
                            &lt;/action&gt;
                        &lt;/pluginExecution&gt;
                    &lt;/pluginExecutions&gt;
                &lt;/lifecycleMappingMetadata&gt;
            &lt;/configuration&gt;
        &lt;/plugin&gt;
    &lt;/plugins&gt;
&lt;/pluginManagement&gt;</pre></p>
<p>修改POM完毕后，工程右键 ⇨ Maven ⇨ Update Project，工程不再报错。 </p>
</li>
<li>广泛使用的3.8版本的android-maven-plugin对高版本的Android SDK支持存在问题，如果在POM中修改了<pre class="crayon-plain-tag">&lt;sdk&gt;&lt;platform&gt;16&lt;/platform&gt;&lt;/sdk&gt;</pre> 元素中的版本号，例如修改为22，会无法正常生成/更新ADT工程元数据和AndroidManifest.xml，需要手工介入：
<ol>
<li>修改project.properties：<pre class="crayon-plain-tag">target=android-22</pre> </li>
<li>修改AndroidManifest.xml中的SDK版本声明：<br />
<pre class="crayon-plain-tag">&lt;uses-sdk android:minSdkVersion="8" android:targetSdkVersion="22" /&gt;</pre>
</li>
</ol>
</li>
</ol>
<div class="blog_h3"><span class="graybg">完整POM样例</span></div>
<p>下面是apk工程完整的pom.xml（使用 4.3.0版本的android-maven-plugin）：</p>
<pre class="crayon-plain-tag">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"&gt;
    &lt;modelVersion&gt;4.0.0&lt;/modelVersion&gt;
    &lt;groupId&gt;cc.gmem.study&lt;/groupId&gt;
    &lt;artifactId&gt;cordova-study&lt;/artifactId&gt;
    &lt;version&gt;0.0.1-SNAPSHOT&lt;/version&gt;
    &lt;packaging&gt;apk&lt;/packaging&gt;
    &lt;name&gt;cordova-study&lt;/name&gt;

    &lt;properties&gt;
        &lt;project.build.sourceEncoding&gt;UTF-8&lt;/project.build.sourceEncoding&gt;
        &lt;android.plugin.version&gt;4.3.0&lt;/android.plugin.version&gt;
    &lt;/properties&gt;

    &lt;dependencies&gt;
        &lt;!-- 安卓SDK依赖，原型默认生成的是groupId为com.google.android的依赖项，但是5.0+以上的构件始终没有加入到中心仓库 --&gt;
        &lt;!-- 因此这里手工修改为Maven Android SDK Deployer生成的构件 --&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;android&lt;/groupId&gt;
            &lt;artifactId&gt;android&lt;/artifactId&gt;
            &lt;version&gt;5.1.1_r2&lt;/version&gt;
            &lt;scope&gt;provided&lt;/scope&gt;
        &lt;/dependency&gt;
        &lt;!-- 添加一个apklib/aar依赖，依赖项必须位于当前Workspace中 --&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;org.apache.cordova&lt;/groupId&gt;
            &lt;artifactId&gt;cordova-android&lt;/artifactId&gt;
            &lt;version&gt;5.4.1-SNAPSHOT&lt;/version&gt;
            &lt;type&gt;apklib&lt;/type&gt;
        &lt;/dependency&gt;
    &lt;/dependencies&gt;
    &lt;build&gt;
        &lt;finalName&gt;${project.artifactId}&lt;/finalName&gt;
        &lt;!-- 如果不指定，默认执行的生命周期阶段或者目标 --&gt;
        &lt;defaultGoal&gt;package android:deploy android:run&lt;/defaultGoal&gt;
        &lt;pluginManagement&gt;
            &lt;plugins&gt;
                &lt;plugin&gt;
                    &lt;groupId&gt;com.simpligility.maven.plugins&lt;/groupId&gt;
                    &lt;artifactId&gt;android-maven-plugin&lt;/artifactId&gt;
                    &lt;version&gt;${android.plugin.version}&lt;/version&gt;
                    &lt;extensions&gt;true&lt;/extensions&gt;
                &lt;/plugin&gt;
            &lt;/plugins&gt;
        &lt;/pluginManagement&gt;
        &lt;plugins&gt;
            &lt;plugin&gt;
                &lt;groupId&gt;com.simpligility.maven.plugins&lt;/groupId&gt;
                &lt;artifactId&gt;android-maven-plugin&lt;/artifactId&gt;
                &lt;configuration&gt;
                    &lt;sdk&gt;
                        &lt;!-- 指定Android SDK版本，Update Project可能无效，需要手工修改AndroidManifest.xml、project.properties文件 --&gt;
                        &lt;platform&gt;22&lt;/platform&gt;
                    &lt;/sdk&gt;
                    &lt;!-- 控制dex命令的执行 --&gt;
                    &lt;dex&gt;
                        &lt;jvmArguments&gt;
                            &lt;jvmArgument&gt;-Xms256m&lt;/jvmArgument&gt;
                            &lt;jvmArgument&gt;-Xmx512m&lt;/jvmArgument&gt;
                        &lt;/jvmArguments&gt;
                        &lt;incremental&gt;true&lt;/incremental&gt;
                        &lt;optimize&gt;false&lt;/optimize&gt;
                    &lt;/dex&gt;
                    &lt;!-- 设置android:run目标的行为 --&gt;
                    &lt;run&gt;
                        &lt;debug&gt;true&lt;/debug&gt;
                    &lt;/run&gt;
                    &lt;!-- true使用Debug keystore签名，false不签名，both同时创建签名/不签名的apk --&gt;
                    &lt;sign&gt;
                        &lt;debug&gt;true&lt;/debug&gt;
                    &lt;/sign&gt;
                    &lt;!-- 是否跳过代码精简、混淆处理 --&gt;
                    &lt;proguard&gt;
                        &lt;skip&gt;true&lt;/skip&gt;
                    &lt;/proguard&gt;
                    &lt;!-- 是否跳过测试 --&gt;
                    &lt;test&gt;
                        &lt;skip&gt;true&lt;/skip&gt;
                    &lt;/test&gt;
                    &lt;!-- 部署前先卸载既有版本 --&gt;
                    &lt;undeployBeforeDeploy&gt;true&lt;/undeployBeforeDeploy&gt;
                &lt;/configuration&gt;
            &lt;/plugin&gt;
            &lt;plugin&gt;
                &lt;groupId&gt;org.apache.maven.plugins&lt;/groupId&gt;
                &lt;artifactId&gt;maven-compiler-plugin&lt;/artifactId&gt;
                &lt;version&gt;3.1&lt;/version&gt;
                &lt;configuration&gt;
                    &lt;source&gt;1.6&lt;/source&gt;
                    &lt;target&gt;1.6&lt;/target&gt;
                    &lt;encoding&gt;${project.build.sourceEncoding}&lt;/encoding&gt;
                &lt;/configuration&gt;
            &lt;/plugin&gt;
            &lt;plugin&gt;
                &lt;groupId&gt;org.apache.maven.plugins&lt;/groupId&gt;
                &lt;artifactId&gt;maven-resources-plugin&lt;/artifactId&gt;
                &lt;version&gt;2.6&lt;/version&gt;
                &lt;configuration&gt;
                    &lt;encoding&gt;${project.build.sourceEncoding}&lt;/encoding&gt;
                &lt;/configuration&gt;
            &lt;/plugin&gt;
        &lt;/plugins&gt;
    &lt;/build&gt;
&lt;/project&gt;</pre>
<div class="blog_h3"><span class="graybg">引用apklib库作为Maven依赖</span></div>
<p>Android Maven工程可能引用几种不同格式的库：</p>
<ol>
<li>jar：在Eclipse Package Explorer视图中，显示在Maven Dependencies目录下</li>
<li>apklib：在Eclipse Package Explorer视图中，显示在Android Dependencies目录下</li>
<li>aar：在Eclipse Package Explorer视图中，显示在Android Dependencies目录下</li>
</ol>
<p>下面的片段示例了如何引用apklib格式的库：</p>
<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;5.4.1-SNAPSHOT&lt;/version&gt;
    &lt;type&gt;apklib&lt;/type&gt;
&lt;/dependency&gt;</pre>
<p>注意，ADT不支持直接把apklib库作为lib使用（该库格式并非Android官方提出），你必须：</p>
<ol>
<li>在<span style="background-color: #c0c0c0;">当前Eclipse工作区中</span>创建groupId、artifactId、version与依赖项一致的Android库工程（打包方式设置为apklib）</li>
<li>把Maven仓库中的apklib库作为zip格式解包，提取出资源：src目录内为Java源码，存放到apklib工程的src/main/java中。assets、res、AndroidManifest.xml等目录和文件覆盖到Android库工程根目录下</li>
<li>在apk工程上点击右键，Maven ⇨ Update Project</li>
</ol>
<p>如果不执行上述步骤，在POM中添加apklib依赖后，你可能收到错误提示：dependency=[**:**:apklib:**:compile] not found in workspace。</p>
<div class="blog_h3"><span class="graybg" style="font-weight: bold;">引用aar库作为Maven依赖</span></div>
<p>ADT同样也不支持aar库，你也需要在当前Eclipse工作区中创建Android库工程，步骤和apklib类似：</p>
<ol>
<li>在当前Eclipse工作区中创建groupId、artifactId、version与依赖项一致的Android库工程（打包方式设置为aar）</li>
<li>把Maven仓库中的aar库作为zip格式解包，提取出资源：
<ol>
<li>assets、res、AndroidManifest.xml等目录和文件覆盖到Android库工程根目录下</li>
<li>classes.jar、libs下所有jar一律作为Android库工程的依赖处理，可以在Maven仓库中识别这些jar的对应构件，缺失的手工添加为构件</li>
</ol>
</li>
<li>在apk工程上点击右键，Maven ⇨ Update Project</li>
</ol>
<div class="blog_h2"><span class="graybg">创建打包为Android库的应用工程</span></div>
<p>可重用的Android库有几种打包格式：</p>
<ol>
<li>jar：这种格式只能包含Java类，不能包含Android资源</li>
<li>apklib：maven-compiler-plugin定义的一种打包格式，这种格式可以包含Java类和Android资源</li>
<li>aar：Google官方在2013年提出的库格式，与apklib一样，同时可以包含Java类和Android资源。两者的主要区别是，aar把所有类放在classes.jar中，而apklib把所有类的源码放在src目录下</li>
</ol>
<p>第一种格式不需赘述，下面说明如何创建第二种格式的Maven工程：</p>
<ol>
<li>点击Eclipse菜单：File ⇨ New ⇨ Maven Project，在第二步选择groupId为de.akquinet.android.archetypes、artifactId为android-library-quickstart的Maven原型。如果搜索不到此原型，点击Add Archetype添加：<img class="aligncenter size-full wp-image-10279" src="https://blog.gmem.cc/wp-content/uploads/2014/08/add-android-lib-archetype.jpg" alt="add-android-lib-archetype" width="525" height="281" /></li>
<li>点击Next，输入Group Id、Artiface Id等信息，并填写原型属性，点击完成</li>
</ol>
<div class="blog_h3"><span class="graybg">完整POM样例</span></div>
<p>下面是apklib工程完整的pom.xml（使用 4.3.0版本的android-maven-plugin）：</p>
<pre class="crayon-plain-tag">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"&gt;
    &lt;modelVersion&gt;4.0.0&lt;/modelVersion&gt;
    &lt;groupId&gt;org.apache.cordova&lt;/groupId&gt;
    &lt;artifactId&gt;cordova-android&lt;/artifactId&gt;
    &lt;version&gt;5.4.1-SNAPSHOT&lt;/version&gt;
    &lt;packaging&gt;apklib&lt;/packaging&gt;
    &lt;name&gt;cordova-android&lt;/name&gt;

    &lt;properties&gt;
        &lt;project.build.sourceEncoding&gt;UTF-8&lt;/project.build.sourceEncoding&gt;
        &lt;android.plugin.version&gt;4.3.0&lt;/android.plugin.version&gt;
    &lt;/properties&gt;

    &lt;dependencies&gt;
        &lt;dependency&gt;
            &lt;groupId&gt;android&lt;/groupId&gt;
            &lt;artifactId&gt;android&lt;/artifactId&gt;
            &lt;version&gt;5.1.1_r2&lt;/version&gt;
            &lt;scope&gt;provided&lt;/scope&gt;
        &lt;/dependency&gt;
    &lt;/dependencies&gt;
    &lt;build&gt;
        &lt;finalName&gt;${project.artifactId}&lt;/finalName&gt;
        &lt;pluginManagement&gt;
            &lt;plugins&gt;
                &lt;plugin&gt;
                    &lt;groupId&gt;com.simpligility.maven.plugins&lt;/groupId&gt;
                    &lt;artifactId&gt;android-maven-plugin&lt;/artifactId&gt;
                    &lt;version&gt;${android.plugin.version}&lt;/version&gt;
                    &lt;extensions&gt;true&lt;/extensions&gt;
                &lt;/plugin&gt;
            &lt;/plugins&gt;
        &lt;/pluginManagement&gt;
        &lt;plugins&gt;
            &lt;plugin&gt;
                &lt;groupId&gt;com.simpligility.maven.plugins&lt;/groupId&gt;
                &lt;artifactId&gt;android-maven-plugin&lt;/artifactId&gt;
                &lt;configuration&gt;
                    &lt;sdk&gt;
                        &lt;platform&gt;22&lt;/platform&gt;
                    &lt;/sdk&gt;
                &lt;/configuration&gt;
            &lt;/plugin&gt;
            &lt;plugin&gt;
                &lt;groupId&gt;org.apache.maven.plugins&lt;/groupId&gt;
                &lt;artifactId&gt;maven-compiler-plugin&lt;/artifactId&gt;
                &lt;version&gt;3.1&lt;/version&gt;
                &lt;configuration&gt;
                    &lt;source&gt;1.6&lt;/source&gt;
                    &lt;target&gt;1.6&lt;/target&gt;
                    &lt;encoding&gt;${project.build.sourceEncoding}&lt;/encoding&gt;
                &lt;/configuration&gt;
            &lt;/plugin&gt;
            &lt;plugin&gt;
                &lt;groupId&gt;org.apache.maven.plugins&lt;/groupId&gt;
                &lt;artifactId&gt;maven-resources-plugin&lt;/artifactId&gt;
                &lt;version&gt;2.6&lt;/version&gt;
                &lt;configuration&gt;
                    &lt;encoding&gt;${project.build.sourceEncoding}&lt;/encoding&gt;
                &lt;/configuration&gt;
            &lt;/plugin&gt;
            &lt;plugin&gt;
                &lt;groupId&gt;org.codehaus.mojo&lt;/groupId&gt;
                &lt;artifactId&gt;build-helper-maven-plugin&lt;/artifactId&gt;
                &lt;version&gt;1.9.1&lt;/version&gt;
                &lt;executions&gt;
                    &lt;execution&gt;
                        &lt;phase&gt;package&lt;/phase&gt;
                        &lt;goals&gt;
                            &lt;goal&gt;attach-artifact&lt;/goal&gt;
                        &lt;/goals&gt;
                        &lt;configuration&gt;
                            &lt;artifacts&gt;
                                &lt;artifact&gt;
                                    &lt;!-- 同时打一个jar格式的包 --&gt;
                                    &lt;type&gt;jar&lt;/type&gt;
                                    &lt;file&gt;${project.build.directory}/${project.build.finalName}.jar&lt;/file&gt;
                                &lt;/artifact&gt;
                            &lt;/artifacts&gt;
                        &lt;/configuration&gt;
                    &lt;/execution&gt;
                &lt;/executions&gt;
            &lt;/plugin&gt;
        &lt;/plugins&gt;
    &lt;/build&gt;
&lt;/project&gt;</pre>
<div class="blog_h1"><span class="graybg">Maven插件详解</span></div>
<div class="blog_h2"><span class="graybg">maven-jarsigner-plugin</span></div>
<div class="blog_h3"><span class="graybg">对APK进行数字签名</span></div>
<p>一般只有再正式发布（Release）时才需要对APK进行正式的签名，开发期间只需要使用Debug Keystore即可。因此，最好是新建一个Maven Profile，在必要时启用：</p>
<pre class="crayon-plain-tag">&lt;profiles&gt;
    &lt;profile&gt;
        &lt;id&gt;sign&lt;/id&gt;
        &lt;properties&gt;
            &lt;!-- 密钥对和Keystore可以通过Keystore Explorer进行生成和管理 --&gt;
            &lt;jarsigner.keystore&gt;D:\JavaEE\keystores\gmem.keystore&lt;/jarsigner.keystore&gt;
            &lt;!-- 密码可以通过命令行指定，例如mvn -Djarsigner.storepass=password --&gt;
            &lt;jarsigner.storepass&gt;passwd&lt;/jarsigner.storepass&gt;
            &lt;jarsigner.alias&gt;*.gmem.cc&lt;/jarsigner.alias&gt;
            &lt;jarsigner.keypass&gt;passwd&lt;/jarsigner.keypass&gt;
        &lt;/properties&gt;
        &lt;build&gt;
            &lt;plugins&gt;
                &lt;plugin&gt;
                    &lt;!-- 该插件提供了基于jarsigner签名或者验证签名（verify）工程构件和附件的能力 --&gt;
                    &lt;groupId&gt;org.apache.maven.plugins&lt;/groupId&gt;
                    &lt;artifactId&gt;maven-jarsigner-plugin&lt;/artifactId&gt;
                    &lt;version&gt;1.4&lt;/version&gt;
                    &lt;executions&gt;
                        &lt;execution&gt;
                            &lt;id&gt;signing&lt;/id&gt;
                            &lt;goals&gt;
                                &lt;goal&gt;sign&lt;/goal&gt;
                            &lt;/goals&gt;
                            &lt;phase&gt;package&lt;/phase&gt;
                            &lt;configuration&gt;
                                &lt;!-- 扫描待处理构件的基目录，支持Ant风格的包含/排除模式 --&gt;
                                &lt;archiveDirectory&gt;&lt;/archiveDirectory&gt;
                                &lt;!-- 基目录下哪些构件需要被处理，支持Ant风格的包含/排除模式 --&gt;
                                &lt;includes&gt;
                                    &lt;include&gt;target/*.apk&lt;/include&gt;
                                &lt;/includes&gt;
                                &lt;!-- 是否移除构件上既有的签名 --&gt;
                                &lt;!-- 需要移除既有的、通过Debug keystore进行的签名 --&gt;
                                &lt;removeExistingSignatures&gt;true&lt;/removeExistingSignatures&gt;
                                &lt;!-- Release版的keystore和alias的信息 --&gt;
                                &lt;keystore&gt;${jarsigner.keystore}&lt;/keystore&gt;
                                &lt;storepass&gt;${jarsigner.storepass}&lt;/storepass&gt;
                                &lt;alias&gt;${jarsigner.alias}&lt;/alias&gt;
                                &lt;keypass&gt;${jarsigner.keypass}&lt;/keypass&gt;
                                &lt;!-- 传递给jarsigner命令行的额外参数 --&gt;
                                &lt;!-- JDK 7+出现INSTALL_PARSE_FAILED_NO_CERTIFICATES错误，添加： --&gt;
                                &lt;arguments&gt;
                                    &lt;argument&gt;-sigalg&lt;/argument&gt;
                                    &lt;argument&gt;MD5withRSA&lt;/argument&gt;
                                    &lt;argument&gt;-digestalg&lt;/argument&gt;
                                    &lt;argument&gt;SHA1&lt;/argument&gt;
                                &lt;/arguments&gt;
                                &lt;!-- 是否显示冗余的信息 --&gt;
                                &lt;verbose&gt;true&lt;/verbose&gt;
                            &lt;/configuration&gt;
                        &lt;/execution&gt;
                    &lt;/executions&gt;
                &lt;/plugin&gt;
            &lt;/plugins&gt;
        &lt;/build&gt;
    &lt;/profile&gt;
&lt;/profiles&gt;</pre>
<p>使用下面的命令启用此Profile并执行构建：<pre class="crayon-plain-tag">mvn install -Psign</pre>  </p>
<div class="blog_h2"><span class="graybg">Android Maven Plugin</span></div>
<div class="blog_h3"><a id="migrate-to-amp-4"></a>迁移到4.x版本</div>
<p>如果使用该插件的4.x版本，需要注意：</p>
<ol>
<li>插件的groupId已经改变为<pre class="crayon-plain-tag">com.simpligility.maven.plugins</pre> </li>
<li>lifecycle-mapping的配置不需要了，consume-aar这个Goal已经废弃</li>
<li>AndroidManifest.xml需要存放到src/main/目录下</li>
<li>资产文件需要从assets转移到src/main/assets</li>
<li>资源文件需要从res转移到src/main/res</li>
</ol>
<p>Update Project后，Android Maven Plugin会自动生成文件和目录的链接，以便ADT能够识别：</p>
<p><img class="aligncenter size-full wp-image-10302" src="https://blog.gmem.cc/wp-content/uploads/2014/08/new-structure-of-amp.jpg" alt="new-structure-of-amp" width="509" height="306" /></p>
<div class="blog_h3"><span class="graybg">常用Goal列表</span></div>
<table class="fixed-word-wrap" style="width: 100%;" border="1" cellspacing="0" cellpadding="5">
<thead>
<tr>
<td style="width: 30%; text-align: center;">Goal</td>
<td style="text-align: center;">说明 </td>
</tr>
</thead>
<tbody>
<tr>
<td>android:aar</td>
<td>创建 Android Archive (aar) 文件</td>
</tr>
<tr>
<td>android:apk</td>
<td>创建apk文件，默认使用debug keystore签名</td>
</tr>
<tr>
<td>android:connect</td>
<td>通过外部IP地址连接到ADB服务器</td>
</tr>
<tr>
<td>android:disconnect</td>
<td>解除到ADB服务器的连接</td>
</tr>
<tr>
<td>android:deploy</td>
<td>部署当前工程的apk到所有已连接（attached）的设备，如果没有任何设备，Build结果显示为失败</td>
</tr>
<tr>
<td>android:deploy-apk</td>
<td>部署指定的apk到已连接的设备，可以通过参数device/devices指定目标设备</td>
</tr>
<tr>
<td>android:deploy-dependencies</td>
<td>部署所有POM中声明的、直接的apk依赖到设备</td>
</tr>
<tr>
<td>android:redeploy</td>
<td>删除现有程序，并重新部署指定的apk到已连接的设备，可以通过参数device/devices指定目标设备</td>
</tr>
<tr>
<td>android:redeploy-apk</td>
<td>删除现有程序，并重新部署指定的apk</td>
</tr>
<tr>
<td>android:undeploy</td>
<td>从所有已连接（attached）的设备解除当前工程的apk的部署</td>
</tr>
<tr>
<td>android:undeploy-apk</td>
<td>解除部署指定的apk</td>
</tr>
<tr>
<td>android:devices</td>
<td>列出所有已连接的设备和模拟器</td>
</tr>
<tr>
<td>android:dex</td>
<td>把编译好的class文件转换为Android dex格式</td>
</tr>
<tr>
<td>android:emulator-start</td>
<td>启动指定的Android模拟器（AVD）</td>
</tr>
<tr>
<td>android:emulator-stop</td>
<td>停止指定的Android模拟器</td>
</tr>
<tr>
<td>android:emulator-stop-all</td>
<td>停止所有Android模拟器</td>
</tr>
<tr>
<td>android:generate-sources</td>
<td>根据resources配置参数，生成R.java</td>
</tr>
<tr>
<td>android:proguard</td>
<td>通过ProGuard进行混淆处理</td>
</tr>
<tr>
<td>android:run</td>
<td>运行顶层启动器（top-level launcher）中的第一个Activity</td>
</tr>
<tr>
<td>android:zipalign</td>
<td>对apk运行zipalign命令</td>
</tr>
<tr>
<td>install</td>
<td>该目标除了默认行为以外，还会导致apk在设备上运行</td>
</tr>
</tbody>
</table>
<div class="blog_h1"><span class="graybg">常见问题</span></div>
<div class="blog_h2"><span class="graybg">运行 Maven clean报错：java.lang.NoClassDefFoundError: org/eclipse/aether/spi/connector/Transfer$State</span></div>
<p>可能是android-maven-plugin和Maven运行时的兼容性问题。点击Window ⇨ Preferences，定位到Maven ⇨ Installations，选择一个小于3.2.5版本的Maven即可。</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
</div><p>The post <a rel="nofollow" href="https://blog.gmem.cc/eclipse-adt-and-mvn-based-android-dev">基于Eclipse ADT和Maven的Android开发</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.gmem.cc/eclipse-adt-and-mvn-based-android-dev/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Android知识集锦</title>
		<link>https://blog.gmem.cc/android-faq</link>
		<comments>https://blog.gmem.cc/android-faq#comments</comments>
		<pubDate>Sun, 03 Jul 2011 03:17:21 +0000</pubDate>
		<dc:creator><![CDATA[Alex]]></dc:creator>
				<category><![CDATA[Android]]></category>
		<category><![CDATA[FAQ]]></category>

		<guid isPermaLink="false">https://blog.gmem.cc/?p=15011</guid>
		<description><![CDATA[<p>零散知识 基本概念 术语 说明 smali 可以指： dalvik虚拟机（安卓的JVM实现）的汇编器（编译为dex格式），对应的baksmali则是反汇编器 —— 把dex字节码转换为smali格式 一种文件格式，dex反汇编后的一种语言 dex字节码无法阅读，smali为其提供了较好的可读性 dpi与像素密度 APK包的res目录下，会有一些drawable开头的子目录，用来存放针对不同像素密度屏幕的资源文件： 目录后缀 像素密度范围 机型举例 mdpi 120dpi~160dpi   hdpi 160dpi~240dpi   <a class="read-more" href="https://blog.gmem.cc/android-faq">[...]</a></p>
<p>The post <a rel="nofollow" href="https://blog.gmem.cc/android-faq">Android知识集锦</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>
<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>smali</td>
<td>
<p>可以指：</p>
<ol>
<li>dalvik虚拟机（安卓的JVM实现）的汇编器（编译为dex格式），对应的baksmali则是反汇编器 —— 把dex字节码转换为smali格式</li>
<li>一种文件格式，dex反汇编后的一种语言</li>
</ol>
<p>dex字节码无法阅读，smali为其提供了较好的可读性</p>
</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">dpi与像素密度</span></div>
<p>APK包的res目录下，会有一些drawable开头的子目录，用来存放针对不同像素密度屏幕的资源文件：</p>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">目录后缀</td>
<td style="text-align: center;">像素密度范围</td>
<td style="text-align: center;">机型举例</td>
</tr>
</thead>
<tbody>
<tr>
<td>mdpi</td>
<td>120dpi~160dpi</td>
<td> </td>
</tr>
<tr>
<td>hdpi</td>
<td>160dpi~240dpi</td>
<td> </td>
</tr>
<tr>
<td>xhdpi</td>
<td>240dpi~320dpi</td>
<td> </td>
</tr>
<tr>
<td>xxhdpi</td>
<td>320dpi~480dpi</td>
<td>Sony Xperia Z5</td>
</tr>
<tr>
<td>xxxhdpi</td>
<td>480dpi~640dpi</td>
<td> </td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">常见问题</span></div>
<div class="blog_h3"><span class="graybg">签名后的APK安装报错：INSTALL_PARSE_FAILED_NO_CERTIFICATES</span></div>
<p>可能原因是，APK被重复签名多次。删除掉META-INF目录下的签名文件、签名块文件后，重新签名即可。</p>
<div class="blog_h1"><span class="graybg">常用开发命令</span></div>
<div class="blog_h2"><span class="graybg"><a id="apktool"></a>apktool</span></div>
<p>APK包反向工程工具。</p>
<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>if</td>
<td>
<p>命令格式：<pre class="crayon-plain-tag">apktool if|install-framework [options] &lt;framework.apk&gt;</pre> </p>
<p>安装框架文件到目录中，选项：<br />-p,--frame-path &lt;dir&gt;  安装到的目标目录<br />-t,--tag &lt;tag&gt;  使用tag来标记框架</p>
</td>
</tr>
<tr>
<td>d</td>
<td>
<p>命令格式：<pre class="crayon-plain-tag">apktool [-q OR -v] d[ecode] [options] &lt;file_apk&gt;</pre></p>
<p>解码file_apk，生成以下目录或者文件：<br />apktool.yml  工具私有元数据<br />AndroidManifest.xml  解码后的清单文件<br />original  原始数据，二进制的清单文件、数据签名文件等<br />res  资源文件，包括图片和解码后的XML<br />smali  dex反汇编后的代码，类似于汇编语言/JVM Opcodes</p>
<p>可用选项：<br /> --api &lt;API&gt; 生成的文件使用的API级别，数字<br /> -b,--no-debug-info 不解码调试用信息，例如行号<br /> -f,--force  如果目标目录存在，先删除之<br /> -k,--keep-broken-res 如果出现错误而某些资源被drop时可以使用，但是构建前你需要手工处理<br /> -o,--output &lt;dir&gt;  输出目录，默认 apk.out<br /> -p,--frame-path &lt;dir&gt; 使用dir中的框架文件<br /> -r,--no-res 不解码资源文件<br /> -s,--no-src 不解码源文件<br /> -t,--frame-tag &lt;tag&gt; 使用被标记未tag的框架</p>
</td>
</tr>
<tr>
<td>b</td>
<td>
<p>命令格式：<pre class="crayon-plain-tag">apktool [-q|--quiet OR -v|--verbose] b[uild] [options] &lt;app_path&gt;</pre></p>
<p>对解码后的目录进行重新打包，可用选项：<br />-a,--aapt &lt;loc&gt; 使用位于loc的资产打包工具<br /> -c,--copy-original 复制原始的AndroidManifest.xml 和 META-INF目录<br /> -d,--debug 设置android:debuggable为true<br /> -f,--force-all 跳过变更检测并构建所有文件<br /> -o,--output &lt;dir&gt; 输出apk的未知，默认dist/name.apk<br /> -p,--frame-path &lt;dir&gt; 使用位于dir的框架文件</p>
</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg"><a id="dex2jar"></a>dex2jar</span></div>
<p>进行.dex格式、.smali格式、.class格式之间的转换。</p>
<div class="blog_h3"><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>d2j-dex2jar</td>
<td>
<p>转换.dex为.jar文件</p>
<p>选项：<br />-d,--debug-info 转译调试信息<br /> -e,--exception-file &lt;file&gt; 转换过程中异常信息的保存位置$current_dir/[file-name]-error.zip<br /> -f,--force 强制覆盖目标jar文件<br /> -n,--not-handle-exception 不处理dex2jar抛出的任何异常<br /> -o,--output &lt;out-jar-file&gt; 输出jar文件名称，默认 $current_dir/[file-name]-dex2jar.jar<br /> -os,--optmize-synchronized optmize-synchronized</p>
</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">用法示例</span></div>
<pre class="crayon-plain-tag"># 转换apk中的.dex为Java的class文件，这些class文件打包在apk_to_decompile-dex2jar.jar中
d2j-dex2jar -f ~/path/to/apk_to_decompile.apk </pre>
<div class="blog_h2"><span class="graybg"><a id="adb"></a>adb（Android Debug Bridge）</span></div>
<p>命令格式：<pre class="crayon-plain-tag">adb [子命令] [选项]</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>-a</td>
<td>使adb在所有网络接口上监听连接</td>
</tr>
<tr>
<td>-d</td>
<td>使命令仅针对USB连接的设备有效</td>
</tr>
<tr>
<td>-e</td>
<td>使命令仅针对运行中的模拟器有效</td>
</tr>
<tr>
<td>-s &lt;specific device&gt;</td>
<td>使命令仅针对与特定设备或模拟器，通过serial number或 qualifier指定设备</td>
</tr>
<tr>
<td>-H</td>
<td>adb服务器的主机名，默认localhost</td>
</tr>
<tr>
<td>-P</td>
<td>adb服务器的监听端口，默认5037</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">子命令说明</span></div>
<p>列出设备、连接/断开设备：</p>
<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>devices [-l]</td>
<td>列出所有连接的设备，-l会显示设备的qualifier</td>
</tr>
<tr>
<td>connect &lt;host&gt;[:&lt;port&gt;]</td>
<td>通过TCP/IP连接到某个设备，默认5555端口</td>
</tr>
<tr>
<td>disconnect [&lt;host&gt;[:&lt;port&gt;]]</td>
<td>断开TCP/IP连接，不指定主机和端口，则端口所有通过TCP/IP连接的设备</td>
</tr>
</tbody>
</table>
<p>针对设备的子命令：</p>
<table class=" fixed-word-wrap full-width">
<thead>
<tr>
<td style="width: 35%; text-align: center;">子命令</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>adb push [-p] &lt;localpath&gt; &lt;remotepath&gt;</td>
<td>拷贝目录或者文件到设备，-p显示进度</td>
</tr>
<tr>
<td>adb pull [-p] [-a] &lt;remote&gt; [&lt;local&gt;]</td>
<td>拷贝目录或者文件到本地，-a拷贝时间戳和UNIX文件mode</td>
</tr>
<tr>
<td>adb sync [ &lt;directory&gt; ]</td>
<td>当本地文件变化时，拷贝目录到设备</td>
</tr>
<tr>
<td>adb shell</td>
<td>交互式的运行Linux命令，示例：<br />
<pre class="crayon-plain-tag"># 列出已安装的包
adb shell pm list packages
# 得到指定包的APK路径
adb shell pm path com.huawei.mw
# 拷贝出APK包
adb pull /data/app/com.huawei.mw-1/base.apk</pre>
</td>
</tr>
<tr>
<td>adb shell &lt;command&gt;</td>
<td>运行Linux命令</td>
</tr>
<tr>
<td>adb emu &lt;command&gt;</td>
<td>运行模拟器控制台命令</td>
</tr>
<tr>
<td>adb logcat [ &lt;filter-spec&gt; ]</td>
<td>显示设备日志</td>
</tr>
<tr>
<td>adb install [-lrtsdg] &lt;file&gt;</td>
<td>
<p>安装APK到设备，-r替换已经存在的应用</p>
<p>报错：<br />INSTALL_FAILED_ALREADY_EXISTS  已经存在，使用-r<br />INSTALL_FAILED_PERMISSION_MODEL_DOWNGRADE  删掉重装</p>
</td>
</tr>
<tr>
<td>adb uninstall [-k] &lt;package&gt;</td>
<td>从设备上移除软件包，-k表示保留应用的数据和缓存</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg"><a id="keytool"></a>keytool</span></div>
<p>JRE自带密钥数据库管理工具。用于加密密钥对、X.509证书链、受信任证书的管理。该工具支持基于DES算法的密码管理。</p>
<p>一个密钥数据库（keystore）可以包含多个密钥条目，以别名（alias）进行唯一区分。</p>
<p>命令格式：<pre class="crayon-plain-tag">keytool [commands]</pre></p>
<div class="blog_h3"><span class="graybg">子命令说明</span></div>
<table class="full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 18%; text-align: center;">子命令</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>-gencert</td>
<td>
<p>可用选项：</p>
<pre class="crayon-plain-tag">{-rfc} {-infile infile} {-outfile outfile} {-alias alias} {-sigalg sigalg}
{-dname dname} {-startdate startdate {-ext ext}* {-validity valDays}
[-keypass keypass] {-keystore keystore} [-storepass storepass]
{-storetype storetype} {-providername provider_name}
{-providerClass provider_class_name {-providerArg provider_arg}}
{-v} {-protected} {-Jjavaoption} </pre>
<p>生成一个证书，作为证书请求的响应。该命令从infile（不指定该选项则使用stdin）读取证书请求，使用alias这个私钥进行签名并输出X.509格式的证书到outfile（不指定则使用stdout）</p>
<p>当指定-rfc选项时，输出格式是基于Base64的PEM，否则使用二进制的DER</p>
<p>当指定dname选项时，该选项作为输出证书的subject字段，不指定则使用请求中该字段的值</p>
<p>&nbsp;</p>
</td>
</tr>
<tr>
<td>-genkeypair</td>
<td>
<p>可用选项：</p>
<pre class="crayon-plain-tag">{-alias alias} {-keyalg keyalg} {-keysize keysize} {-sigalg sigalg}
[-dname dname] [-keypass keypass] {-startdate value} {-ext ext}*
{-validity valDays} {-storetype storetype} {-keystore keystore}
[-storepass storepass]
{-providerClass provider_class_name {-providerArg provider_arg}}
{-v} {-protected} {-Jjavaoption}</pre>
<p>别名-genkey，用于生成密钥对（公钥/私钥），并包装公钥为X.509 v3自签名格式的证书，该证书作为单元素证书链存储。这个自签名证书 +  私钥作为keystore中的一个条目保存，以alias唯一识别</p>
<p>-keyalg 指定生成密钥对的算法，默认DSA，可选RSA<br />-keysize 指定密钥长度，使用RSA算法时默认2048；使用1024算法时默认1024 <br />-sigalg  指定签名自签名证书时使用的算法，该算法必须和keyalg算法兼容<br />-dname  关联到alias值的X509可识别名称（Distinguished Name），并且用作自签名证书的issuer、subject这两个字段<br />-keypass  用于保护私钥的密码<br />-startdate  自签名证书有效期开始点</p>
<p>某些选项如果不提供，会交互式的提示</p>
</td>
</tr>
<tr>
<td>-genseckey</td>
<td>生存密钥并存储到keystore中</td>
</tr>
<tr>
<td>-importcert</td>
<td>
<p>可用选项：</p>
<pre class="crayon-plain-tag">{-alias alias} {-file cert_file} [-keypass keypass] {-noprompt} {-trustcacerts}
{-storetype storetype} {-keystore keystore} [-storepass storepass]
{-providerName provider_name}
{-providerClass provider_class_name {-providerArg provider_arg}}
{-v} {-protected} {-Jjavaoption}</pre>
<p>从cert_file文件中读取证书或者证书链，并且存储到keystore中，以alias进行识别</p>
<p>支持导入X509 v1 v2 v3格式的证书，以及这些证书形成的PKCS#7格式的证书链。数据必须是二进制的或者是BASE64编码的。对于BASE64格式，证书必须以-----BEGIN开头-----END结束</p>
<p>之所以需要导入证书，可能出于以下目的：</p>
<ol>
<li>添加被导入证书到受信任证书列表</li>
<li>添加受信任CA签发的证书</li>
</ol>
</td>
</tr>
<tr>
<td>-importpassword</td>
<td>导入一个密码</td>
</tr>
<tr>
<td>-importkeystore</td>
<td>从其它keystore导入内容</td>
</tr>
<tr>
<td>-certreq</td>
<td>制作证书请求</td>
</tr>
<tr>
<td>-exportcert</td>
<td>导出证书</td>
</tr>
<tr>
<td>-list</td>
<td>列出alias对应条目的信息，打印到标准输出</td>
</tr>
<tr>
<td>-printcert</td>
<td>
<p>从文件、SSL服务器、被签名JAR等地方读取证书信息</p>
</td>
</tr>
<tr>
<td>-printcertreq</td>
<td>打印证书请求</td>
</tr>
<tr>
<td>-printcrl</td>
<td>读取证书吊销列表 </td>
</tr>
<tr>
<td>-storepasswd</td>
<td>修改keystore的密码</td>
</tr>
<tr>
<td>-keypasswd</td>
<td>修改某个条目的密码</td>
</tr>
<tr>
<td>-delete</td>
<td>从keystore中删除某个条目</td>
</tr>
<tr>
<td>-changealias </td>
<td>修改alias</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">用法示例</span></div>
<pre class="crayon-plain-tag"># 生成一个密钥对，并添加到数据库中
keytool -genkeypair -v -keystore ~/Android/Keystores/alexwong.keystore 
    -alias development -keyalg RSA -keysize 2048 -validity 36500</pre>
<div class="blog_h2"><span class="graybg"><a id="jarsigner"></a>jarsigner</span></div>
<p>对JAR文件进行签名，或者执行签名校验 —— 签名是否有效、签名后的JAR是否完整。</p>
<p>被签名后的JAR文件，仅仅是在META-INF目录中多了两个额外的文件：</p>
<ol>
<li>以.SF为扩展名的签名文件</li>
<li>以.DSA、.RSA或者.EC为扩展名的签名块文件</li>
</ol>
<p>命令格式：</p>
<pre class="crayon-plain-tag"># 进行签名
jarsigner [ options ] jar-file alias
# 校验签名
jarsigner -verify [ options ] jar-file [alias ...]</pre>
<div class="blog_h3"><span class="graybg">命令示例</span></div>
<pre class="crayon-plain-tag"># 签名一个APK（JAR）文件
jarsigner -keystore ~/Android/Keystores/alexwong.keystore -storepass simple
    -keypass simple -signedjar android-signed.apk android.apk  development</pre>
</div><p>The post <a rel="nofollow" href="https://blog.gmem.cc/android-faq">Android知识集锦</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.gmem.cc/android-faq/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
