Gradle是一个基于Apache Ant和Apache Maven概念的项目自动化建构工具。它使用一种基于Groovy的特定领域语言(DSL)来声明项目设置,抛弃了基于XML的各类繁琐配置。html
面向Java应用为主。当前其支持的语言限于Java、Groovy和Scala,计划将来将支持更多的语言。java
projects 和 tasks是Gradle中最重要的两个概念,任何一个Gradle构建都是由一个或者多个project组成,每一个project能够是一个jar包,一个web应用,或者一个Android app等,每一个project又由多个task构成,一个task其实就是构建过程当中一个原子性的操做,好比编译、拷贝等。linux
一个build.gradle文件是一个构建脚本,当运行gradle命令的时候会从当前目录查找build.gradle文件来执行构建。下面咱们来看下gradle的Hello World。在build.gradle构建文件中输入如下构建脚本:android
task hello { doLast { println 'Hello world!' } }
task定义了一个任务,这个任务名字是hello。doLast是Task的方法,意思是在该hello任务执行以后做的事情,能够用一个闭包配置它,这里是输出Hello world!字符串。咱们在终端里执行以下命令运行查看结果:git
$gradle hello -q Hello world!
这里新建一个android项目,选择Project结构模式,下面是项目的结构示意图web
├── ApplicationName #项目路径 │ ├── .gradle │ ├── .idea │ ├── app #Android App目录 │ │ ├── build #构建输出目录 │ │ ├── libs#so相关库 │ │ ├── src #源代码,资源等 │ │ └── .gitignore │ │ └── app.im │ │ └── buidle.gradle#构建脚本 │ │ └── proguard-rules.pro#proguard混淆配置 │ ├── build │ │ ├── intermediates │ ├── gradle │ │ ├── wrapper │ └── .gitignore │ └── buidle.gradle #工程构建文件 │ └── gradle.properties#gradle的配置 │ └── gradlew #gradle wrapper linux shell脚本 │ └── gradlew.bat │ └── local.properties #配置Androod SDK位置文件 │ └── MyApplication.iml │ └── settings.gradle #工程配置 ├── External Liraies#类库、jar等
settings.gradle用于配置project,标明其下有几个module,好比这里p一个:ape
moduleshell
include ':app'
build.gradle(Project:projectName)是一个顶级的build配置文件,在这里能够为全部project以及module配置一些经常使用的配置。服务器
// Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { repositories { //使用jcenter库 jcenter() } dependencies { // 依赖android提供的1.3.0的gradle build classpath 'com.android.tools.build:gradle:1.3.0' // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files } } //为全部的工程的repositories配置为jcenter allprojects { repositories { jcenter() } } task clean(type: Delete) { delete rootProject.buildDir }
build.gradle(Module:moduleName)用于module的配置,也是最重要的部分闭包
apply plugin: 'com.android.application' android { compileSdkVersion 23 buildToolsVersion "23.0.2" defaultConfig { applicationId "com.example.zqw.myapplication" minSdkVersion 15 targetSdkVersion 23 versionCode 1 versionName "1.0" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) testCompile 'junit:junit:4.12' compile 'com.android.support:appcompat-v7:23.1.0' compile 'com.android.support:design:23.1.0' }
这些配置放在上面提到的build.gradle(Module:moduleName)里面app
signingConfigs {//签名配置这里配置了release 对应的还能够有debug release { storeFile file("keystore.jks")//这个文件须要放在modele所在根目录下 keyAlias "testapp" keyPassword "111111" storePassword "111111" } } buildTypes { release { signingConfig signingConfigs.release//不要忘了要在release的时候加入我么的签名配置信息 minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } }
第一种方式是使用Gradle工具,双击下图选择的命令便可,在控制台看到BUILD SUCCESSFUL就代表签名成功了,由于咱们没有配置文件的生成路径因此会在默认地址里面C:\project\demo\MyApplication\app\build\outputs\apk
第二种方式使用命令。打开Terminal选项卡,用最下方标签切换。打开以后咱们先敲一下gradle –help命令验证一下是否能够执行gradle命令。若是不能够的话,在环境变量里边配置一下gradle的path。
gradle assembleRelease
咱们能够为不一样的buildTypes选择是否启用混淆,通常release发布版本是须要启用混淆的,这样别人反编译以后就很难分析你的代码,而咱们本身开发调试的时候是不须要混淆的,因此debug不启用混淆。对release启用混淆的配置以下:
android { buildTypes { release { minifyEnabled true proguardFile 'proguard.cfg' } } }
minifyEnabled为true表示启用混淆,proguardFile是混淆使用的配置文件,这里是module根目录下的proguard.cfg文件
在上面咱们配置签名信息采用的是默认写法。下面是对默认写法的解释
proguard-android.txt是sdk中groguard默认的文件,具体地址在:/opt/sdk/tools/proguard/proguard-android.txt
proguard-rules.pro是AS中专用的proguard配置文件,其实只是后缀名不一样,与Eclipse中的proguard-project.txt是同样的,配置规则相同
老版本开启混淆的命令是runProguard,如今统一用minifyEnabled命令了,将其设为true就行了。
zipalign是Android SDK中包含一个的工具,它可以对打包的应用程序进行优化。在你的应用程序上运行zipalign,使得在运行时Android与应用程序间的交互更加有效率。所以,这种方式可以让应用程序和整个系统运行得更快。强烈推荐在新的和已经发布的程序上使用zipalign工具来获得优化后的版本——即便你的程序是在老版本的Android平台下开发的。
在Android中,每一个应用程序中储存的数据文件都会被多个进程访问:安装程序会读取应用程序的manifest文件来处理与之相关的权限问题;Home应用程序会读取资源文件来获取应用程序的名和图标;系统服务会由于不少种缘由读取资源(例如,显示应用程序的Notification);此外,就是应用程序自身用到资源文件。
在Android中,当资源文件经过内存映射对齐到4字节边界时,访问资源文件的代码才是有效率的。可是,若是资源自己没有进行对齐处理(未使用zipalign工具),它就必须回到老路上,显式地读取它们——这个过程将会比较缓慢且会花费额外的内存。
对于应用程序开发者来讲,这种显式读取方式是至关便利的。它容许使用一些不一样的开发方法,包括正常流程中不包含对齐的资源,所以,这种读取方式具备很大的便利性(本段的原始意思请参考原文)。
遗憾的是,对于用户来讲,这个状况偏偏是相反的——从未对齐的apk中读取资源比较慢且花费较多内存。最好的状况是,Home程序和未对齐的程序启动得比对齐后的慢(这也是惟一可见的效果)。最坏的状况是,安装一些未对齐资源的应用程序会增长内存压力,并所以形成系统反复地启动和杀死进程。最终,用户放弃使用如此慢又耗电的设备。
开启zipAlign配置以下
android { buildTypes { release { zipAlignEnabled true } } }
在项目屡次版本迭代后有一部分资源文件可能不在使用了,若是之前负责的人离开或者不在负责这个了,那么后面接收的人就不敢删除,这样安装包就会愈来愈大,之前使用工具能够找到没被使用的资源。如今android gradle也直接支持,配置以下
android { buildTypes { release { shrinkResources true } } }
多渠道这个也是android平台特有的,由于有360手机助手、应用宝、小米应用市场等等大量相似AppStore的应用商城。在项目发布之后须要对后台数据分渠道统计,因此才有了多渠道打包。Android Gradle给咱们提供了productFlavors,让咱们能够对生成的APK包进行定制
android { productFlavors { dev{ } google{ } baidu{ } } }
这样当咱们运行assembleRelease的时候就会生成3个release包,分别是dev、google以及baidu的。目前看这三个包除了文件名没有什么不同,由于咱们尚未定制,使用的都是defaultConfig配置。这里的flavor和defaultConfig是同样的,能够自定义其applicationId、versionCode以及versionName等信息,好比区分不一样包名:注意实际发布项目APP的报名应该是惟一的,这里这是为了说明能够改
android { productFlavors { dev{ applicationId "org.flysnow.demo.dev" } google{ applicationId "org.flysnow.demo.google" } baidu{ applicationId "org.flysnow.demo.baidu" } } }
在咱们打包发版的时候,一次性打几十个包,这时候咱们就想让生成的apk文件名有区分,好比一眼就能看出这个apk是哪一个版本的,哪一个渠道的,是哪天打的包等等,这就须要咱们在生成apk文件的时候动态修改生成的apk文件名达到这一目的。
def buildTime() { def date = new Date() def formattedDate = date.format('yyyyMMdd') return formattedDate } android { buildTypes { release { applicationVariants.all { variant -> variant.outputs.each { output -> if (output.outputFile != null && output.outputFile.name.endsWith('.apk') &&'release'.equals(variant.buildType.name)) { def apkFile = new File( output.outputFile.getParent(), "testapp_${variant.flavorName}_v${variant.versionName}_${buildTime()}.apk") output.outputFile = apkFile } } } } } }
以baidu渠道为例,以上的代码会生成一个名字为testapp_baidu_v9.5.2.6_20150330.apk安装包。下面咱们分析一下,Android Gradle任务比较复杂,它的不少任务都是自动生成的,为了能够更灵活的控制,Android Gradle提供了applicationVariants、libraryVariants以及testVariants,他们分别适用于app、library、app和library都适用。
这里是循环处理每一个applicationVariant,当他们的输出文件名以apk结尾而且buildType是release时,从新设置新的输出文件名,这样就达到了咱们批量修改生成的文件名的目的。
这里用到的占位符也是让动态打包的APK包内配置信息和渠道对于,用于第三方的统计分析,以友盟统计为例
<meta-data android:value="${UMENG_CHANNEL_VALUE}" android:name="UMENG_CHANNEL"/>
若是是单一渠道咱们能够直接给value赋值好比value=“baidu”,这样在项目代码中去获取UMENG_CHANNEL值就知道是哪个渠道了,由于打的渠道包很是的多因此咱们能够采用上面占位符方式
下面是gradle的配置
android { defaultConfig { manifestPlaceholders = [UMENG_CHANNEL_VALUE: 'dev'] } }
咱们的默认配置里AndroidManifest的${UMENG_CHANNEL_VALUE}占位符会被dev这个字符串所替换,也就说默认运行的版本是一个开发板。以此类推,咱们其余渠道的版本就能够这样定义:
android { productFlavors { google{ applicationId "org.flysnow.demo.google" manifestPlaceholders.put("UMENG_CHANNEL_VALUE",'google') } baidu{ applicationId "org.flysnow.demo.baidu" manifestPlaceholders.put("UMENG_CHANNEL_VALUE",'baidu') } } }
这样写依然过于繁琐,咱们能够直接用渠道名称来给这个占位符赋值
productFlavors.all { flavor -> manifestPlaceholders.put("UMENG_CHANNEL_VALUE",name) }
在咱们实际项目开发中通常会有测试环境和正式环境的区分(和服务器有交互的APP),通常咱们会写一个配置文件,根据不一样状况来访问不一样的环境。之前的时候咱们经过把不一样的配置文件打包进APK中来控制,Android Gradle能够动态生成BuildConfig.java,咱们只须要填写信息便可,使用起来更加方便。
android { defaultConfig { buildConfigField 'String','API_SERVER_URL','"http://www.myweb.com/"' } productFlavors { google{ buildConfigField 'String','API_SERVER_URL','"http://www.google.com/"' } baidu{ buildConfigField 'String','API_SERVER_URL','"http://www.baidu.com/"' } } }
buildConfigField 一共有3个参数,第一个是数据类型,就是你定义的常量值是一个什么类型,和Java的类型是对等的,这里是String。第二个参数是常量名,这里是API_SERVER_URL。第三个参数是常量值。如此定义以后,就会在BuildConfig.java中生成一个常量名为API_SERVER_URL的常量定义。默认配置的生成是:
public final static String API_SERVER_URL = "http://www.myweb.com/"
当是baidu和google渠道的时候生成的就是http://www.myweb.com/了。这个常量能够在咱们编码中引用。在咱们进行打包的时候会根据Gradle配置动态替换。
咱们发现通常渠道版本都是用来发布的,确定用的是生产服务器,因此咱们可使用批处理来搞定这个事情,而不用在一个个渠道里写这些配置。
productFlavors.all { flavor -> buildConfigField 'String','API_SERVER_URL','"http://www.flysnow.org/"' }
此外,好比Gradle的resValue,也是和buildConfigField,只不过它控制生成的是资源,好比咱们在android的values.xml定义生成的字符串。能够用它来动态生成咱们想要的字符串,好比应用的名字,可能一些渠道会不同,这样就能够很灵活的控制自动生成,关于resValue详细介绍请参考相关文档,这里再也不举例说明。
在Gradle 进行dex的可能会遇到内