本文共 10018 字,大约阅读时间需要 33 分钟。
对于一个gradle 项目,最基础的文件配置如下:
MyApp ├── build.gradle ├── settings.gradle └── app └── build.gradle
一个项目有一个setting.gradle、包括一个顶层的 build.gradle文件、每个Module 都有自己的一个build.gradle文件。
setting.gradle:这个 setting 文件定义了哪些module 应该被加入到编译过程,对于单个module 的项目可以不用需要这个文件,但是对于 multimodule 的项目我们就需要这个文件,否则gradle 不知道要加载哪些项目。这个文件的代码在初始化阶段就会被执行。
顶层的build.gradle:顶层的build.gradle文件的配置最终会被应用到所有项目中。
当你的app只有一个模块的时候,你的setting.gradle将会是这样子的:
include ':app'
setting.gradle文件将会在初始化时期执行,关于初始化时期,可以查看上一篇博客,并且定义了哪一个模块将会被构建。举个例子,上述setting.gradle包含了app模块,setting.gradle是针对多模块操作的,所以单独的模块工程完全可以删除掉该文件。在这之后,Gradle会为我们创建一个Setting对象,并为其包含必要的方法,你不必知道Settings类的详细细节,但是你最好能够知道这个概念。
该gradle文件是定义在这个工程下的所有模块的公共属性,它默认包含二个方法:
buildscript { repositories { jcenter() } dependencies { classpath 'com.android.tools.build:gradle:1.2.3' }}allprojects { repositories { jcenter() }}
buildscript方法是定义了全局的相关属性,repositories定义了jcenter作为仓库。一个仓库代表着你的依赖包的来源,例如maven仓库。
dependencies用来定义构建过程。这意味着你不应该在该方法体内定义子模块的依赖包,你仅仅需要定义默认的Android插件就可以了,因为该插件可以让你执行相关Android的tasks。
allprojects中定义的属性会被应用到所有 module 中,但是为了保证每个项目的独立性,我们一般不会在这里面操作太多共有的东西。
模块内的gradle文件只对该模块起作用,而且其可以重写任何的参数来自于根目录下的gradle文件。该模块文件应该是这样:
apply plugin: 'com.android.application' android { compileSdkVersion 22 buildToolsVersion "22.0.1" defaultConfig { applicationId "com.gradleforandroid.gettingstarted" minSdkVersion 14 targetSdkVersion 22 versionCode 1 versionName "1.0" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile ('proguard-android.txt'), 'proguard-rules.pro' } } } dependencies { compile fileTree(dir: 'libs', include: ['*.jar']) compile 'com.android.support:appcompat-v7:22.2.0' }
第一行代码应用了Android 程序的gradle插件,作为Android 的应用程序,这一步是必须的,因为plugin中提供了Android 编译、测试、打包等等的所有task。
这是编译文件中最大的代码块,关于android 的所有特殊配置都在这里,这就是又我们前面的声明的 plugin 提供的。
android{…}表示的是AppExtension这个类,按ctrl+鼠标左键对着android{},点击进去就可以看到这个类了。
该方法包含了所有的Android属性,而唯一必须得属性为compileSdkVersion和buildToolsVersion:
- compileSdkVersion:编译该app时候,你想使用到的api版本。
- buildToolsVersion:构建工具的版本号。
构建工具包含了很多实用的命令行命令,例如aapt,zipalign,dx等,这些命令能够被用来产生多种多样的应用程序。你可以通过sdk manager来下载这些构建工具。
defaultConfig { applicationId "com.gradleforandroid.gettingstarted" minSdkVersion 14 targetSdkVersion 22 versionCode 1 versionName "1.0"}
第一个属性是applicationId,该属性复写了AndroidManifest文件中的包名package
name,但是关于applicationId和package name有一些不同。在gradle被用来作为Android构建工具之前,package name在AndroidManifest.xml有两个作用:其作为一个app的唯一标示,并且其被用在了R资源文件的包名。在以前我们修改这个ID会导致所有用引用R资源类的地方都要修改。但是现在我们如果修改applicationId只会修改当前程序的ID,而不会去修改源码中资源文件的引用。
Gradle能够很轻松的构建不同版本的app,使用构建变种。举个例子,其能够很轻松的创建一个免费版本和付费版本的app。这两个版本需要分隔的标示码,所以他们能够以不同的app出现在各大应用商店,当然他们也能够同时安装在一个手机中。资源代码和R文件必须拥有相同的包名,否则你的资源代码将需要改变,这就是为什么Android开发团队要将package name的两大功能拆分开。在AndroidManifest文件中定义的package name依然被用来作为包名和R文件的包名。而applicationid将被用在设备和各大应用商店中作为唯一的标示。
接下来将是minSdkVersion和targetSdkVersion。这两个和AndroidManifest中的很像。minSdkVersion定义为最小支持api。
versionCode将会作为版本号标示,而versionName毫无作用。
所有的属性都是重写了AndroidManifest文件中的属性,所以你没必要在AndroidManifest中定义这些属性了。
buildTypes方法定义了编译类型,针对每个类型我们可以有不同的编译配置,不同的编译配置对应的有不同的编译命令。默认的有debug、release 的类型。详细参见构建变种章节。
productFlavors定义了如何使用同一份源码编译不同的程序(包名也不同),比如 免费版和收费版。详细参见构建变种章节。
signingConfigs定义了签名配置,也可以像buildTypes和productFlavors一样定义不同的签名配置。
signingConfigs定义了源码的目录,我们可以对其进行更改。
每当创建一个新的build type 的时候,gradle 默认都会创建一个新的source set。我们可以建立与main文件夹同级的文件夹,根据编译类型的不同我们可以选择对某些源码直接进行替换。除了代码可以替换,我们的资源文件也可以替换。
//multiDex的一些相关配置,这样配置可以让你的编译速度更快 dexOptions { preDexLibraries = false //让它不要对Lib做preDexing incremental true //开启incremental dexing,优化编译效率,这个功能android studio默认是关闭的。 javaMaxHeapSize "4g" //增加java堆内存大小 }
packagingOptions :设置APK包的相关属性。
packagingOptions { exclude 'LICENSE.txt' }
打包时移除LICENSE.txt。
- excludes The list of excluded paths.
- pickFirsts The list of paths where the first occurrence is packaged in the APK.
- exclude(path) Adds an excluded paths.
- pickFirst(path) Adds a firstPick path. First pick paths do get packaged in the APK, but only the first occurrence gets packaged.
Repositories 就是代码仓库,这个相信大家都知道,我们平时的添加的一些 dependency 就是从这里下载的。
android.applicationVariants.all { variant -> variant.outputs.each { output -> def outputFile = output.outputFile if (outputFile != null && outputFile.name.endsWith('.apk')) { def fileName = outputFile.name; if (android.defaultConfig.versionName != null) { fileName = fileName.replace("aaaa-", "bbbb_") fileName = fileName.replace("-release.apk", "_${android.defaultConfig.versionName}.apk") } output.outputFile = new File(outputFile.parent, fileName) } } }//这种方式更简单易懂applicationVariants.all { variant -> variant.outputs.each { output -> output.outputFile = new File(rootProject.ext.appReleaseDir + getDate() + "_v" + rootProject.ext.defaultConfig.versionName + "_" + variant.productFlavors[0].name + rootProject.ext.appSuffixName) }}
new File中的内容就是我们的路径跟文件名,这个相信大家都能理解。我们在之前的config.gradle中新增一个appReleaseDir作为文件目录,然后用打包时间+版本号+渠道名称+文件后缀名作为文件名
config.gradle新增内容如下appReleaseDir = 'C:\\Users\\renyu\\Desktop\\'appSuffixName = "_release.apk"
build.gradle中的getDate()方法
def getDate() { def date = new Date() def formattedDate = date.format('yyyyMMddHHmm') return formattedDate}
这里直接将 store 的密码明文写在这里对于产品的安全性来说不太好,特别是如果该源码开源,别人就可以用你的 id 去发布app。我们可以将这些高危参数放到专门的配置文件中,并且在提交代码的时候将其从版本管理中忽略。
我们将配置信息写在gradle.properties中
# keystore配置信息storeFile_=android.keystorestorePassword_=abcd1234keyAlias_=android.keystorekeyPassword_=abcd1234
必要信息写完之后,即可修改build.gradle中的signingConfigs,直接引用gradle.properties中的配置信息
storeFile file(storeFile_)storePassword storePassword_keyAlias keyAlias_keyPassword keyPassword_
其实你可以在任何目录放置这个签名文件。如果签名信息没有放到gradle.properties或者local.properties里,那就需要自己通过代码来读取配置信息,从而获取相应的数值。这里示例是keystore.txt文件,目录与gradle.properties同级
如何加载
//加载签名配置文件Properties props = new Properties()props.load(new FileInputStream(file("../gradle.properties")))android { signingConfigs { release { storeFile file(props['storeFile_']) storePassword props['storePassword_'] keyAlias props['keyAlias_'] keyPassword props['keyPassword_'] } }}
更安全一点的方法就是密码验证了,对于这种情况,我们需要构建一个动态加载任务,在编译release 源码的时候从本地文件(未加入git)获取keystore 信息,如下:
你还可以设置一个保险措施,万一我们的没有找到对应的文件需要用户从控制台输入密码 最后设置最终值然后设置release 任务依赖于我们刚刚设置的任务
android { buildTypes { debug { buildConfigField "String", "API_URL", "\"http://test.example.com/api\"" buildConfigField "boolean", "LOG_HTTP_CALLS", "true" } release { buildConfigField "String", "API_URL", "\"http://example.com/api\"" buildConfigField "boolean", "LOG_HTTP_CALLS","false" } }
类似这些定义的常量,当定义了这些属性后,你完全可以在代码中使用:BuildConfig.API_URL和BuildConfig.LOG_HTTP
最近,Android tools team也让其里面定义string变为可能:
android { buildTypes { debug { resValue "string", "app_name", "Example DEBUG" } release { resValue "string", "app_name", "Example" } }}
你可以在代码中使用这些string。其中“”不是必须得。这里单引号和双引号的区别在于双引号可以包含插入语句。插入是计算一个字符串包含placeholders的过程,并将placeholders的值替换,这些placeholder可以是变量甚至是方法。Placeholders必须包含一个方法或者变量,并且其被{}包围,且其前面有 修饰。如果其只有一个单一的变量,可以只需要 。下面是一些基本的用法:
def name = 'Andy'def greeting = "Hello, $name!"def name_size "Your name is ${name.size()} characters long."
在编译的时候,我们可能会有很多资源并没有用到,此时就可以通过shrinkResources来优化我们的资源文件,除去那些不必要的资源。
buildTypes { release { minifyEnabled true shrinkResources true } }
如果我们需要查看该命令帮我们减少了多少无用的资源,我们也可以通过运行shrinkReleaseResources命令来查看log.
某些情况下,一些资源是需要通过动态加载的方式载入的,这时候我也需要像 Progard 一样对我们的资源进行keep操作。方法就是在res/raw/下建立一个keep.xml文件,通过如下方式 keep 资源:
对一些特殊的文件或者文件夹,比如 国际化的资源文件、屏幕适配资源,如果我们已经确定了某种型号,而不需要重新适配,我们可以直接去掉不可能会被适配的资源。这在为厂商适配机型定制app的时候是很用的。做法如下:
比如我们可能有非常多的国际化的资源,如果我们应用场景只用到了English,Danish,Dutch的资源,我们可以直接指定我们的resConfig:
对于尺寸文件我们也可以这样做
依赖模块作为gradle默认的属性之一(这也是为什么其放在了Android的外面),为你的app定义了所有的依赖包。默认情况下,我们依赖了所有在libs文件下的jar文件,同时包含了AppCompat这个aar文件。详细内容参见依赖管理章节。
如果你有很多模块在一个工程下,你可以这么定义你的project文件。
allprojects { apply plugin: 'com.android.application' android { compileSdkVersion 22 buildToolsVersion "22.0.1" } }
这只会在你的所有模块都是Android app应用的时候有效。你需要添加Android 插件才能访问Android的tasks。更好的做法是你在全局的gradle文件中定义一些属性,然后再模块中运用它们。比如你可以在根目录下这么定义:
ext { compileSdkVersion = 22 buildToolsVersion = "22.0.1"}
那么你在子模块中就可以使用这些属性了:
android { compileSdkVersion rootProject.ext.compileSdkVersion buildToolsVersion rootProject.ext.buildToolsVersion }
上述方法是一种办法,当然还有很多办法:
ext { local = 'Hello from build.gradle'} task printProperties << { println local // Local extra property println propertiesFile // Property from file if (project.hasProperty('cmd')) { println cmd // Command line property }}
当然你可以在gradle.properties中定义:
propertiesFile = Hello from gradle.properties
你也可以输入命令行:
$ gradlew printProperties -Pcmd='Hello from the command line':printPropertiesHello from build.gradleHello from gradle.propertiesHello from the command line
build.gradle各个方法详细的属性参考文章:
参考文章链接: