Only sync the active variant
”。android {
...
productFlavors {
dev {
...
// The following configuration limits the "dev" flavor to using
// English stringresources and xxhdpi screen-density resources.
resConfigs "en", "xxhdpi"
}
...
}
}
int MILLIS_IN_MINUTE = 1000 * 60
int minutesSinceEpoch = System.currentTimeMillis() / MILLIS_IN_MINUTE
android {
...
defaultConfig {
// Making either of these two values dynamic in the defaultConfig will
// require a full APK build and reinstallation because the AndroidManifest.xml
// must be updated.
versionCode 1
versionName "1.0"
...
}
// The defaultConfig values above are fixed, so your incremental builds don't
// need to rebuild the manifest (and therefore the whole APK, slowing build times).
// But for release builds, it's okay. So the following script iterates through
// all the known variants, finds those that are "release" build types, and
// changes those properties to something dynamic.
applicationVariants.all { variant ->
if (variant.buildType.name == "release") {
variant.mergedFlavor.versionCode = minutesSinceEpoch;
variant.mergedFlavor.versionName = minutesSinceEpoch + "-" + variant.flavorName;
}
}
}
若动态修改versionCode 1和versionName "1.0"
,会导致整个apk编译,延长编译时间。可以仅在release版本时,动态改变需要改变的值。
version和‘+’
的组合出现,比如com.android.tools.build:gradle:2.+
。使用version和‘+’
方式的动态依赖,会导致不必要的更新和延长编译时间。android {
buildTypes {
release {
// Disables PNG crunching for the release build type.
crunchPngs false
}
}
// If you're using an older version of the plugin, use the
// following:
// aaptOptions {
// cruncherEnabled false
// }
}
由于编译类型和风味版本中没有该属性,因此编译release版本时,需要手动设置为true。
当一个大型工程,或者对编译脚本做了很多客制化,我们需要对编译过程做一个深入分析,分析出编译瓶颈在哪里。比如,如果编译过程花费大量时间在配置工程,我们应该把编译脚本中的自定义逻辑部分移出配置脚本;如果合并资源文件时间过久,需要转图像为webP或者关闭PNG压缩功能。
--rerun-tasks
。再次编译,不需要更改的不会再编译,编译的log会标记为UP-TO-DATE,因此可以通过多次编译,对比哪个没有被标记为UP-TO-DATE。没有被标记的,可能存在优化的地方。例如,:app:processDevUniversalDebugManifest没有被标记为UP-TO-DATE,表明每次编译都存在动态更新manifest的情况。注意:有一些task,是需要每次编译执行的,如:app:checkDevDebugManifest。通过以上操作,分析编译过程报告,找出可优化地方。有一些配置需求需要根据实际情况确定,比如一个有很多代码量的工程,使用code shrinking(代码压缩)去除不使用的代码,压缩apk大小;但是当代码量很少时,使用code shrinking可能会得不偿失。在低内存的编译设备上,增大org.gradle.jvmargs
配置,会改善编译性能。
修改影响编译性能的地方后,再次测试,通过对比编译过程报告,确定修改有效。
如果想要通过build.gradle插入一个变量到AndroidManifest.xml中,我可以使用脚本的manifestPlaceholders
属性。该属性携带一个键值对,如下示例
android {
defaultConfig {
manifestPlaceholders = [hostName:"www.example.com"]
}
...
}
然后,在Manifest中使用属性
{ hostName}" ... />
...
默认情况下,编译工具提供了applicationId
属性。这个属性的值为当前编译的编译变体的application ID,该值可以唯一的标识一个编译变体app。例如,一个编译变体想要一个唯一的标识用户intent action。可以如下方式实现
android {
defaultConfig {
applicationId "com.example.myapp"
}
productFlavors {
free {
applicationIdSuffix ".free"
}
pro {
applicationIdSuffix ".pro"
}
}
}
在manifest中
{ applicationId}.TRANSMOGRIFY" />
...
编译free变体后,manifest中最终为
android:name="com.example.myapp.free.TRANSMOGRIFY" />
...
在一个包含多个模块的项目工程中,可能存在需要一个全局属性,所有模块都可以使用。可以在project的build.gradle中的ext块中添加额外属性(extra properties)
buildscript {...}
allprojects {...}
// This block encapsulates custom properties and makes them available to all
// modules in the project.
ext {
// The following are only a few examples of the types of properties you can define.
compileSdkVersion = 28
// You can also use this to specify versions for dependencies. Having consistent
// versions between modules can avoid behavior conflicts.
supportLibVersion = "28.0.0"
...
}
...
在项目中的模块的build.gradle中使用
android {
// Use the following syntax to access properties you define at the project level:
// rootProject.ext.property_name
compileSdkVersion rootProject.ext.compileSdkVersion
...
}
...
dependencies {
implementation "com.android.support:appcompat-v7:${rootProject.ext.supportLibVersion}"
...
}
在module中的build.gradle中通过lintOptions块配置lint选项。更多关于使用lint改善代码,详见Improve Your Code with Lint
android {
...
lintOptions {
// Turns off checks for the issue IDs you specify.
disable 'TypographyFractions','TypographyQuotes'
// Turns on checks for the issue IDs you specify. These checks are in
// addition to the default lint checks.
enable 'RtlHardcoded', 'RtlCompat', 'RtlEnabled'
// To enable checks for only a subset of issue IDs and ignore all others,
// list the issue IDs with the 'check' property instead. This property overrides
// any issue IDs you enable or disable using the properties above.
check 'NewApi', 'InlinedApi'
// If set to true, turns off analysis progress reporting by lint.
quiet true
// if set to true (default), stops the build if errors are found.
abortOnError false
// if true, only report errors.
ignoreWarnings true
}
}
...
当编译一个测试apk时,编译脚本会自动生成AndroidManifest.xml,使用
节点配置。我们可以自创建一个AndroidManifest.xml,通过资源集(sourceSet)配置指定,也可以在模块中的build.gradle中改变默认 的配置,示例如下
android {
...
// Each product flavor you configure can override properties in the
// defaultConfig block. To learn more, go to Configure Product Flavors.
defaultConfig {
...
// Specifies the application ID for the test APK.
testApplicationId "com.test.foo"
// Specifies the fully-qualified class name of the test instrumentation runner.
testInstrumentationRunner "android.test.InstrumentationTestRunner"
// If set to 'true', enables the instrumentation class to start and stop profiling.
// If set to false (default), profiling occurs the entire time the instrumentation
// class is running.
testHandleProfiling true
// If set to 'true', indicates that the Android system should run the instrumentation
// class as a functional test. The default value is 'false'
testFunctionalTest true
}
}
...
注意: 测试app非debug版本的可提供完整功能的app,是使用Android提供的测试机制实现对app功能、性能等测试的app;下同。
默认情况下,测试app的编译类型都是debug,如果新增了不同的编译类型,想要测试app编译的是新增的类型,可以通过如下方式修改
android {
...
testBuildType "staging"
}
通过gradle配置测试选项,告诉系统如何运行测试app。在module中的build.gradle的testOptions
块中配置
android {
...
// Encapsulates options for running tests.
testOptions {
// Changes the directory where Gradle saves test reports. By default, Gradle saves test reports
// in the path_to_your_project/module_name/build/outputs/reports/ directory.
// '$rootDir' sets the path relative to the root directory of the current project.
reportDir "$rootDir/test-reports"
// Changes the directory where Gradle saves test results. By default, Gradle saves test results
// in the path_to_your_project/module_name/build/outputs/test-results/ directory.
// '$rootDir' sets the path relative to the root directory of the current project.
resultsDir "$rootDir/test-results"
}
}
设置单元测试app的测试选项,在testOptions.unitTests
块中配置
android {
...
testOptions {
...
// Encapsulates options for local unit tests.
unitTests {
// By default, local unit tests throw an exception any time the code you are testing tries to access
// Android platform APIs (unless you mock Android dependencies yourself or with a testing
// framework like Mockito). However, you can enable the following property so that the test
// returns either null or zero when accessing platform APIs, rather than throwing an exception.
returnDefaultValues true
// Encapsulates options for controlling how Gradle executes local unit tests. For a list
// of all the options you can specify, read Gradle's reference documentation.
all {
// Sets JVM argument(s) for the test JVM(s).
jvmArgs '-XX:MaxPermSize=256m'
// You can also check the task name to apply options to only the tests you specify.
if (it.name == 'testDebugUnitTest') {
systemProperty 'debug', 'true'
}
}
}
}
}
keystore.properties
,文件中内容如storePassword=myStorePassword
keyPassword=myKeyPassword
keyAlias=myKeyAlias
storeFile=myStoreFileLocation
keystore.properties
文件// Creates a variable called keystorePropertiesFile, and initializes it to the
// keystore.properties file.
def keystorePropertiesFile = rootProject.file("keystore.properties")
// Initializes a new Properties() object called keystoreProperties.
def keystoreProperties = new Properties()
// Loads the keystore.properties file into the keystoreProperties object.
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
android {
...
}
...
android {
signingConfigs {
config {
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
storeFile file(keystoreProperties['storeFile'])
storePassword keystoreProperties['storePassword']
}
}
...
}
...
buildConfigField
在BuildConfig文件中添加自定义字段,在运行时app可以从BuildConfig中获取配置的值。同理,在编译脚本中可以使用resValue
添加资源。示例如下android {
...
buildTypes {
release {
// These values are defined only for the release build, which
// is typically used for full builds and continuous builds.
buildConfigField("String", "BUILD_TIME", "\"${minutesSinceEpoch}\"")
resValue("string", "build_time", "${minutesSinceEpoch}")
...
}
debug {
// Use static values for incremental builds to ensure that
// resource files and BuildConfig aren't rebuilt with each run.
// If these rebuild dynamically, they can interfere with
// Apply Changes as well as Gradle UP-TO-DATE checks.
buildConfigField("String", "BUILD_TIME", "\"0\"")
resValue("string", "build_time", "0")
}
}
}
...
resValue通过编译脚本修改strings.xml中的内容
2. manifest中使用编译脚本中定义的变量
详见前面章节