博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Gradle for Android(二)——build.gradle基本配置
阅读量:2084 次
发布时间:2019-04-29

本文共 10018 字,大约阅读时间需要 33 分钟。

Gradle Files

对于一个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文件的配置最终会被应用到所有项目中。

setting.gradle解析

当你的app只有一个模块的时候,你的setting.gradle将会是这样子的:

include ':app'

setting.gradle文件将会在初始化时期执行,关于初始化时期,可以查看上一篇博客,并且定义了哪一个模块将会被构建。举个例子,上述setting.gradle包含了app模块,setting.gradle是针对多模块操作的,所以单独的模块工程完全可以删除掉该文件。在这之后,Gradle会为我们创建一个Setting对象,并为其包含必要的方法,你不必知道Settings类的详细细节,但是你最好能够知道这个概念。

根目录的build.gradle

该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 中,但是为了保证每个项目的独立性,我们一般不会在这里面操作太多共有的东西。

模块内的build.gradle

模块内的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{}

这是编译文件中最大的代码块,关于android 的所有特殊配置都在这里,这就是又我们前面的声明的 plugin 提供的。

android{…}表示的是AppExtension这个类,按ctrl+鼠标左键对着android{},点击进去就可以看到这个类了。

该方法包含了所有的Android属性,而唯一必须得属性为compileSdkVersion和buildToolsVersion:

  • compileSdkVersion:编译该app时候,你想使用到的api版本。
  • buildToolsVersion:构建工具的版本号。

构建工具包含了很多实用的命令行命令,例如aapt,zipalign,dx等,这些命令能够被用来产生多种多样的应用程序。你可以通过sdk manager来下载这些构建工具。

  • defaultConfig就是程序的默认配置,该属性会重写在AndroidManifest.xml中的对应属性,注意,如果在AndroidMainfest.xml里面定义了与这里相同的属性,会以这里的为主。
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 {}

buildTypes方法定义了编译类型,针对每个类型我们可以有不同的编译配置,不同的编译配置对应的有不同的编译命令。默认的有debug、release 的类型。详细参见构建变种章节。

productFlavors{}

productFlavors定义了如何使用同一份源码编译不同的程序(包名也不同),比如 免费版和收费版。详细参见构建变种章节。

signingConfigs{}

signingConfigs定义了签名配置,也可以像buildTypes和productFlavors一样定义不同的签名配置。

sourceSets {}

signingConfigs定义了源码的目录,我们可以对其进行更改。

每当创建一个新的build type 的时候,gradle 默认都会创建一个新的source set。我们可以建立与main文件夹同级的文件夹,根据编译类型的不同我们可以选择对某些源码直接进行替换。除了代码可以替换,我们的资源文件也可以替换。

dexOptions{}

//multiDex的一些相关配置,这样配置可以让你的编译速度更快    dexOptions {        preDexLibraries = false          //让它不要对Lib做preDexing        incremental true                 //开启incremental dexing,优化编译效率,这个功能android studio默认是关闭的。        javaMaxHeapSize "4g"     //增加java堆内存大小    }

packagingOptions {}

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 {}

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}

keystore 保护

这里写图片描述

这里直接将 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 任务依赖于我们刚刚设置的任务

这里写图片描述

BuildConfig和resources

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."

Reducing apk file

在编译的时候,我们可能会有很多资源并没有用到,此时就可以通过shrinkResources来优化我们的资源文件,除去那些不必要的资源。

buildTypes {        release {            minifyEnabled true            shrinkResources true        } }

如果我们需要查看该命令帮我们减少了多少无用的资源,我们也可以通过运行shrinkReleaseResources命令来查看log.

某些情况下,一些资源是需要通过动态加载的方式载入的,这时候我也需要像 Progard 一样对我们的资源进行keep操作。方法就是在res/raw/下建立一个keep.xml文件,通过如下方式 keep 资源:

这里写图片描述

Manual shrinking

对一些特殊的文件或者文件夹,比如 国际化的资源文件、屏幕适配资源,如果我们已经确定了某种型号,而不需要重新适配,我们可以直接去掉不可能会被适配的资源。这在为厂商适配机型定制app的时候是很用的。做法如下:

比如我们可能有非常多的国际化的资源,如果我们应用场景只用到了English,Danish,Dutch的资源,我们可以直接指定我们的resConfig:

这里写图片描述

对于尺寸文件我们也可以这样做

这里写图片描述

依赖包:dependencies {}

依赖模块作为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 }

Project properties文件

上述方法是一种办法,当然还有很多办法:

  • ext方法
  • gradle.properties文件
  • -p参数
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各个方法详细的属性参考文章:

参考文章链接:

你可能感兴趣的文章
Spark代码可读性与性能优化——示例九(数据传输与解析)
查看>>
Spark代码可读性与性能优化——示例十(项目结构)
查看>>
Spark优化总结(二)——代码编写
查看>>
Spark优化总结(三)——调参
查看>>
消息队列——RocketMQ示例
查看>>
Spark优化总结(四)——业务与架构设计
查看>>
volatile在JVM内存交互中的操作
查看>>
实现一个具有Stream的链式、惰性特点的容器
查看>>
Spark源码编译
查看>>
分布式一致性算法(Paxos、Raft、ZAB)
查看>>
MPC多方安全计算——比较算法示意
查看>>
Akka事件驱动——模拟Spark注册、心跳
查看>>
Flink示例——Source
查看>>
Flink示例——Sink
查看>>
Flink示例——Connect、CoMapFunction、Split、Select
查看>>
Flink示例——Window、EventTime、WaterMark
查看>>
Flink示例——State、Checkpoint、Savepoint
查看>>
Flink示例——Table、SQL
查看>>
HBase之Rowkey设计
查看>>
推荐算法——ALS模型算法分析、LFM算法
查看>>