自动编译组件
目前Android开发的主流开发工具是 Eclipse 和 IDEA
目前主流的自动化打包工具时 ant,maven,gradle。
maven工具中有自己的依赖仓库维护,很多开源支持包在上面都有维护(国内的除外)
gradle是近年来发展起来自动化构建应用,解决 ant 构建上的繁琐代码,并且也支持读取maven的配置形式,依赖maven的支持包结构
好了,平时你们使用 eclipse 发布的时候,不要说你没用过自动构建,eclipse 的 android项目是用ant的构建方式。如果你要加渠道发布,你就需要自己写 ant 的脚本。
maven自动构建,目前大多数用在 javaweb 项目,安卓项目用的不多。
eclipse 的构建应用大多使用 ant,maven,gradle也有相关支持。Android的项目默认使用 ant 进行构建
在 IDEA 中,可以使用 ant 方式构建 android应用,也能使用 gradle 方式构建,目前主流支持都是 gradle 方式。
IDEA 默认使用 gradle 工具做 android 的构建程序,你试试在新建应用的时候选择 Application Module 而不是 Gradle:Android Module 看看,你会发现,工程项目和 eclipse 没什么区别。而且包含 ant 的脚本文件。(如下图)
Gradle Module原理
IDEA 支持 ant,maven,gradle 工具来构建引用。目前 Android 应用在 as下编码基本使用 gradle 进行构建,本章将基于这个思路讲解那些内容属于 IDEA,那些内容属于 gradle 。
首先,我们建立一个 Gradle:Android Module 来看看 Gradle 项目结构。
得到的目录结构如下:
其中根目录是一个project,下面的app目录是其中一个module。
目录文件 | 作用 |
---|---|
.gradle | gradle项目产生文件(自动编译工具产生的文件) |
.idea | IDEA项目文件(开发工具产生的文件) |
app | 其中一个module,复用父项目的设置,可与父项目拥有相同的配置文件 |
build | 自动构建时生成文件的地方 |
gradle | 自动完成gradle环境支持文件夹 |
.gitignore | git源码管理文件 |
build.gradle | gradle 项目自动编译的配置文件 |
gradle.properties | gradle 运行环境配置文件 |
gradlew | 自动完成 gradle 环境的linux mac 脚本,配合gradle 文件夹使用 |
gradlew.bat | 自动完成 gradle 环境的windows 脚本,配合gradle 文件夹使用 |
local.properties | Android SDK NDK 环境路径配置 |
*.iml | IDEA 项目文件 |
setting.gradle | gradle 项目的子项目包含文件 |
- .gradle .idea 是在分别在 gradle ,IDEA 运行时候会生成的文件,一般这样的文件也不会纳入源代码管理之中。
- app文件夹,是其中一个module,里面的文件内容与父类差不多,若没有定义,则在项目中使用父类的设置(意思就是,里面也能包含build.gradle、gradle.properties、setting.gradle 等相关gradle文件,怎么理解?其实每一层都是一个module,整个项目是一个大的 module 而已)
- gradle 文件夹,用于保存gradle 下载路径的配置文件位置,用于没有gradle环境的环境初始化使用
- build.gradle 项目的编译环境配置,比如制定项目依赖的lib包。
- gradle.properties 配置gradle运行环境的文件,比如配置gradle运行模式,运行时jvm虚拟机的大小
- gradlew && gradlew.bat 代替gradle 命令实现自动完成gradle环境搭建。配合gradle文件夹的内容,会降到IDEA如何搭配gradlew使用。
- local.properties 配置android NDK,SDK的地方,恩,非android项目可能没有这个文件,这个路径根据不同想电脑不同,一般也不会纳入源代码管理之中,一般可以写一个local.properties.simple 文件,告知需要修改该文件名并写上本地SDK NDK 路径。simple文件纳入源码管理之中。
- setting.gradle 子项目包含文件,声明当前目录下含有什么module,当然你的app底下加上这样的文件,也能继续在app底下加module。和我第点说的,整个project就是一个大的module,每个module下面还能包含相应的module。如果你理解这个了,其实app目录单独作为一个项目管理也是可以的,,把相应的配置文件配上而已,相当于主目录应用 android 的gradle plugin (下一点会说到这个)
-
gitignore 该文件是源码管理的配置文件,不在该文讲解。
既然gradle 是多 module形式,那么我们来看看 setting.gradle 配置的内容
从上面目录的配置文件内容来看,整个project也算是一个module,如果改module底下还有module,就可以通过setting.gradle配置进来,使得该module底下的gradle,从app module底下可以看出,module最少要含有 build.gradle文件,这个module的项目编译文件,该module依赖什么插件对该目录进行编译都在此配置,比如android与android-library,其他内容可继承父类的
Gradle 与 IDEA 的关联性
上面介绍了gradle项目的目录结构,以及module的模式,接下来,了解一下IDEA如何与gradle项目关联上来的。
idea的项目,在建立或者导入的时候,就已经确定他是基于什么自动构建工具的项目。新建的时候,使用 gradle androiw module 说明他是 gradle 自动构建的项目,那么导入的时候也是一样。我们看看下面的项目导入图,在选择项目地址之后,我们看到一下的内容。
如果你的项目里面包含了gradle的相关文件,就可以选择 import project form external model 导入IDEA了,如果项目只有源码,没有包含自动构建的相关信息,你只能选择 Create project form existing sources 了,让他生成自动构建工具需要的文件。
你可能会想,想gradle里面的依赖包,我不知道有哪些,怎么办。好吧,你私下里就骂骂作者吧,上传了源码不上传自动构建相关的文件,导致项目依赖不完整,还得找资料自己完善,所以这里也希望广大开源代码爱好者,在分享你的作品的时候,希望能够分享完整的项目信息,别只有源码,让人跑不起来项目(牢骚说多了)。
那么下一步是什么呢:
恩,是不是似曾相识?你没看错,在新建项目的时候也有这个选择,这个是选择自动编译的工具的方式。
如果你到官方下载 gradle 绿色包,解压到某个目录,你可以使用 gradle distribution,并设定 Gradle home 目录,这样 IDEA 构建编译项目的时候,就使用你设定的gradle版本进行。
说到这里,我觉得该说说 use default gradle wrapper 记得新建项目的时候也是使用 use default gradle warpper,这时候,上面目录说到的三个文件。
- gradle/
- gradlew
- gradlew.bat
如果使用这种方法,IDEA会调用项目根目录 gradlew 或者 gradlew.bat (根据linux,windows,osx自动选型)代替原生的 gradle 方法做自动构建。
这两个文件做了什么事情呢:
- 解析 gradle/wrapper/gradle-wrapper.properties 文件,获取项目需要的 gradle 版本下载地址
- 判断本地用户目录下的 ./gradle 目录下是否存在该版本,不存在该版本,走第3点,存在走第4点
- 下载 gradle-wrapper.properties 指定版本,并解压到用户目录的下 ./gradle 文件下
- 利用 ./gradle 目录下对应的版本的 gradle 进行相应自动编译操作
看了上面的原理,应该明白了,如果你自己下载 gradle 让idea 导入项目的时候使用。那么其他人就不知道你使用什么版本的gradle版本进行自动编译,如果使用 项目目录自带的 gradlew 的话,gradlew 就会自动完善 gradle 的安装,若需要更新 gradle 的版本,只需要修改 gradle/wrapper/gradle-wrapper.properties 文件内的下载链接即可。而且gradlew的版本和 android 版本是需要适配的,在自己电脑维护需求不同版本的 gradlew 也是一个麻烦的事情。
而且这样的好处也有一个,当你在新电脑上下载你的源码进行编译时,你完全可以不依赖IDE开发工具,直接在项目目录下使用 ./gradlew build 即可对源码进行编译,因为它会自动下载 gradle 进行调用,可以使得新电脑较快完成项目开发环境适配(对网络依赖较强,希望带上vpn,这就是为什么你们在新建项目时需要去下载gradle,还比较慢的原因,我们一起来 f-u-c-k-g-f-w)
当然,导入项目需要能够选上 use default gradle wrapper 的前提是存在上面一个gradle文件夹与gradlew、gradew.bat
既然可以选择使用 gradlew 来管理 gradle 或者手动指定 gradle 工具,那么已经存在的项目如何更改?
这个问题,我曾在 idea 13 版本上有找到,但是在 idea 14 上面没找到相应的变更设置。谁要是找到了,记得留言告知我一声。
在导入项目或者新建项目的时候,idea 会根据 build.gradle 文件更新 *.iml 项目文件,有时候,你会发现,新增一个jar包,但是无法读取jar包内容,因为对于 gradle 来说,jar包属于项目外依赖,包括maven拓展包,都是属于项目外依赖,需要修改build.gradle 文件,当你加入新的 jar包,或者添加了 maven支持包,在idea上面都会提示需要进行同步
恩,上图你看到的,是我模拟添加一个jar包之后,随便加了一个空格,文件上提示需要进行 gradle 项目与 idea项目文件同步,点击 sync now 之后,idea 会根据 gradle 文件重新更新 .idea 目录以及 *.iml 文件,让idea 可以识别引入的 资源。
这也就是为什么有人说加入一个jar包确没有自动提示,而重新打开idea之后就能够提示使用jar内的方法了,因为重新打开idea,开发工具会重新同步 build.gradle 的内容
Gradle Android Plugin Version与 Gradle Version对应
gradle只是一个自动化编译工具,它需要以来插件来识别这是什么项目,用什么方式去编译的。我们来看看 build.gradle 与 app/build.gradle 的设置看看。
不知道你们会不会奇怪,在app里面的 build.gradle 中,缺少了 buildscript 与 allproject 的设置,恩,没错,我删掉了,因为这个设置是可以继承父项目的 build.gradle
其中父类的整个项目需要依赖插件 com.android.tools.build:gradle:0.14.2 最后是版本号。(目前AS的版本号已经是1.0.0了)
这个插件的版本号与gradle调用编译时是有依赖关系的,插件的版本越高,需要更多gradle的新特性,新的gradle特新就需要新版本的gradle才能支持。
这时候,回到上一节的内容,如果要使用android项目自动编译的新特性,如果选择不同的gradle指向方式,那么你就要做不同的处理
- 下载不同版本的gradle对不同的项目的不同版本做插件与gradle的对应维护,如果其他人使用你的项目,你还要告知他使用什么版本以上的gradle才能使用这个插件。
- 把对应关系一次弄好之后,更新gradle/wrapper/gradle-wrapper.properties下载地址,利用gradlew自动使用相应版本的gradle,这样gradlew版本的需求就跟着源码管理一直保留到其他人的电脑商。
具体的插件依赖,可点击这里
目录格式
先来看看eclipse的完整目录与IDEA的完整目录结构(当然,IDEA看的目录时基于module的目录设置的,而不是根据总项目的目录设置的)
eclipse目录
IDEA(AS)目录
按照Android开发的目录,区分为以下的目录格式:
目录类型 | Eclipse | IDEA(Android Studio) |
---|---|---|
代码根目录 | / | src/main |
adil文件 | /src | [代码根目录]/aidl |
java文件 | /src | [代码根目录]/java |
assets文件 | /assets | [代码根目录]/assets |
jni文件 | /jni | [代码根目录]/jni |
jnilibs文件 | /libs | [代码根目录]/jniLibs |
res文件 | /res | [代码根目录]/res |
AndroidMainfest.xml | AndroidMainfest.xml | [代码根目录]/AndroidMainfest.xml |
上面所展示的内容就是目前Android用到的所有资源文件类型。为什么要把项目根目录列出来?这个到后面渠道包的时候需要用到。可是也可能也会问到,为什么项目根目录会有两级。src 第一级我的理解是项目源码相关都在这里,第二级,认为是主项目源码在 main 目录,根据系统完善性,应该针对主项目添加测试项目的源码,所以新的代码里面在 main 目录同级的地方会有 tests 目录,用于测试项目的目录源码维护。如果有渠道,还可以为渠道包新建项目目录去坐项目自定义。
1
2
3
4
|
1. 其中adil是跨进程通信使用的
2. jni文件夹是存放dnk编译的c或者cpp文件
3. jnilibs文件,就是平时jni接入使用的 *.so库。需要里面是需要包含平台文件夹的。入下图所示
|
当我们要从eclipse里面转移到as的时候,是可以通过gradle来从新定义以上路径的。在module/build.gradle文件里面有这么一段设置默认设置,如果按照缺省,可以不写。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
sourceSets {
main.setRoot('src/main')
main {
manifest.srcFile '[mainRoot]/AndroidManifest.xml'
java.srcDirs = ['[mainRoot]/java']
resources.srcDirs = ['[mainRoot]/java']
aidl.srcDirs = ['[mainRoot]/aidl']
res.srcDirs = ['[mainRoot]/res']
assets.srcDirs = ['[mainRoot]/assets']
jni.srcDirs=['[mainRoot]/jni']
jniLibs.srcDirs = ['[mainRoot]/jniLibs']
}
|
所示需要适应eclipse的目录格式,可以写成:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
sourceSets {
//main.setRoot('src/main') 因为下面的路径全部被定义了,所以这个方法已经不起作用了。
main {
manifest.srcFile 'AndroidManifest.xml'
java.srcDirs = ['src']
resources.srcDirs = ['src']
aidl.srcDirs = ['src']
res.srcDirs = ['res']
assets.srcDirs = ['assets']
jni.srcDirs=['jni']
jniLibs.srcDirs = ['libs']
}
|
你一定奇怪,为什么libs 的jar包没有目录呢?没错,还记得我上面写过的,对于 gradle android 项目来说,jar包和library支持,属于android项目的外部支持,通通由 Gradle 配置文件去管理。上图的最后一段说明这一切。
Jar支持、Library支持、仓库插件
说到 library ,不得不说说项目类型。
在项目根目录,我们引入了 android 插件:com.android.tools.build:gradle:0.14.2
我们具体看上图 build.gradle 的第一行代码:
1
2
|
apply plugin: 'com.android.library'
|
这是说明这个 module 项目说明 android-library 项目,我看过其他的项目,gradle module 是可以依赖多个 plugin 让这个项目成为多种类型的项目。
如果是一个普通android项目,会是这样的代码:
1
2
|
apply plugin: 'com.android.applition'
|
那么 android 项目 gradle 中依赖 jar libs 又三种方法:
1
2
3
4
5
6
7
8
9
10
11
|
#1 依赖项目相对路径的jar包,当然,你可以换成全路径
compile files('libs/something_local.jar')
#或者依赖libs目录下的所有jar包
compile fileTree(dir: 'libs', include: ['*.jar'])
#2 依赖maven仓库中的支持包(目前很多好的都在maven进行管理,比如 v4,v7支持包)
compile 'com.android.support:appcompat-v7:20.0.0'
#3 依赖其他library module
compile project(':jiechic-library')
|
这么一看,我这个 jiechic-library 其实就是一个module,与app同级,使用的是 apply plugin: ‘com.android.library’ 形式的一个安卓 library 。
有时候,你可能新加入一个
很多开源仓库不懂怎么加?maven插件添加不在这篇文章的讨论范围了。你在搜搜可否?
编译过程及渠道模式
ant 的的编译目录基于当前工作目录进行,如果你需要自定义渠道,你需要编写 ant 脚本代码,去替换当前目录的文件,而且当前目录的文件你还需要进行保存一次。若出错了,你还得在本地目录进行恢复。
gradle 的编译方式,是根据基础项目内容,以及渠道信息 ,将相关代码文件拷贝合并到 build 目录下,然后在build 目录下进行编译。在此设计小文件多文件的平凡拷贝更新,则正常编译速度让很多人觉得,IDEA 开发比 Eclipse 的卡,其实这也是他实现的方式造成。
但是 gradle 项目却提供了很多eclipse 不方便提供的功能。比如渠道模式
比如渠道模式:
在build.gradle 中有这样的设定 productFlavors 这样的设定,当改设定存在,则 main 主代码渠道不在进行打包,全部依赖渠道进行编译安装调试
看到我的 productFlavors 中定义了两个版本,一个是线上测试餐饮版,一个是线上测试大众版。你看了,可能举得这么定义没什么用,你看到大括号里面了么?? applicationID,恩,没错,这就是可以重新定义他的包名,在这括号里面能够重新定义defaultConfig里面的所有配置。每个渠道的版本号都能单独维护。不需要你写ant脚本去替换。而且每个渠道也是同时可以编译 debug 和 release 版本
再来看下图:
记得在目录详解的目录上,设定的是 main 渠道的目录,恩,没错,现在我设定的是渠道的目录。这个怎么说?还记得我设定主目录的设定代码么?
1
2
3
4
5
6
7
8
9
10
11
12
13
|
sourceSets {
main.setRoot('src/main')
main {
manifest.srcFile '[mainRoot]/AndroidManifest.xml'
java.srcDirs = ['[mainRoot]/java']
resources.srcDirs = ['[mainRoot]/java']
aidl.srcDirs = ['[mainRoot]/aidl']
res.srcDirs = ['[mainRoot]/res']
assets.srcDirs = ['[mainRoot]/assets']
jni.srcDirs=['[mainRoot]/jni']
jniLibs.srcDirs = ['[mainRoot]/jniLibs']
}
|
没错,你看到的,所有android相关的文件,都在某个root下面。
那么,我们渠道上面设置的的路径也是基于如此路径进行文件分类的。
恩,没错,设定了渠道的内容,编译渠道的时候,不同的渠道就会依据主项目,然后替换自己的内容。 比如上图的String,你需要替换的只要写入你需要替换的String name,这么value的值就会在这个渠道就会替换过来。包括drawable等二进制文件,他会进行替换操作,比如说,你给应用宝打包的应用启动欢迎页是应用宝的,给其他应用商城的是其他图片,就可完全放置在渠道文件夹内。
如何检查是否合并?检查 build 文件内的合并内容,具体的不细说,你应该能找到,比如某个渠道替换的文件,找到那个渠道相应的文件查看即可。如果时Value的值,那就查看value文件内对应的name是否已经变更成功。
说到这个,不得不提,你有时候双击报错信息,打开了某个文件,或者某个图片(.9图较多),想对其进行修改,你发现它是只读状态,不可修改,恩,没错,这个编译文件经过 gradle 拷贝到build 相应渠道目录下才开始进行编译,在那个目录下的文件都是只读状态,并非你的源代码文件。你可以根据提示信息找到你自己的源文件进行修改。
你可能会问,这么多渠道包,那我要调试,究竟默认使用哪个渠道呢?看下图:
恩,你没看错,在项目下面,有个选择编译版本的地方。。这样,你想调试那个渠道的版本都可以轻松进行了。
提速编译
gradle 在编译时候有些东西跟java虚拟机相关,记得一开头的项目文件 gradle.properties 么?这个是设定 gradle 在运行时的编译环境。
在渠道编译的时候,默认情况下,一个渠道会启用一个java 虚拟机进行编译,在java 虚拟机启动,关闭的过程是非常耗时的,gradle 提供了守护进程的模式进行编译,从头到尾就使用一个java 虚拟机(jvm)
至于什么是守护进程模式,资料比较多,我就不解析了,可看这里这里
同时,根据你自己的电脑内存,你还可以定义虚拟机的内存数大小,这个其实会关乎你电脑卡不卡的问题,电脑内存太小,编译虚拟机内存太多,也许你切换个环境都会觉得举步维艰吧。
很多教程,都写编译打包版本,使用命令
1
2
|
./gradlew build
|
可是,都不会告诉你,更好的提高效率。官方提供release 版本与debug版本的区别编译
一般我们要编译所有渠道的 release版本,会使用如下命令:
1
2
3
4
|
./gradlew assembleRelease
#or
./gradlew aR
|
你肯定会问,如果我基于一个稳定版本,新增一个渠道,我只想打包一个渠道,怎么办?之行下面的命令看看
1
2
|
./gradlew task
|
这会列表所有 gradle task。task的概念我就不解释了,很多gradle教程已经说了。
我只要打包 onlinetestcatering 渠道,只要之行这样的命令就可以了。
1
2
|
./gradlew task assembleOnlinetestcateringRelease
|