上次咱们说到gradle的原理,主要是偏理论上的知识点,直通车在这Android Gradle系列-原理篇。此次咱们来点实战的,随便巩固下以前的知识点。java
在app module下的gradle.build中都有一个android闭包,主要配置都在这里设置。例如默认配置项:defaultConfig;签名相关:signingConfig;构建变体:buildTypes;产品风格:productFlavors;源集配置:sourceSets等。android
对于defaultConfig其实它是也一个productFlavor,只不过这里是用来提供默认的设置项,若是以后的productFlavor没有特殊指定的配置都会使用defaultConfig中的默认配置。git
public class DefaultConfig extends BaseFlavor { @Inject public DefaultConfig( @NonNull String name, @NonNull Project project, @NonNull ObjectFactory objectFactory, @NonNull DeprecationReporter deprecationReporter, @NonNull Logger logger) { super(name, project, objectFactory, deprecationReporter, logger); } } public abstract class BaseFlavor extends DefaultProductFlavor implements CoreProductFlavor { ... }
能够看到defaultConfig的超级父类就是DefaultProductFlavor。而在DefaultProductFlavor中定义了许多咱们常常见到的配置:VersionCode、VersionName、minSdkVersion、targetSdkVersion与applicationId等等。github
有了上面的基础,那么在defaultConfig中咱们要配置的变量就显而易见了。api
defaultConfig { applicationId "com.idisfkj.androidapianalysis" minSdkVersion 16 targetSdkVersion 26 versionCode 1 versionName "1.0" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" }
signingConfig是用来配置keyStore,咱们能够针对不一样的版本配置不一样的keyStore,例如安全
signingConfigs { config { //默认配置 storeFile file('key.store') storePassword 'android123' keyAlias 'android' keyPassword 'android123' } dev { //dev测试版配置 storeFile file('xxxx') storePassword 'xxx' keyAlias 'xxx' keyPassword 'xxx' } }
有人可能会说这不安全的,密码都是明文,都暴露出去了。是的,若是这项目发布到远程,那么这些秘钥就泄露出去了。因此为了安全起见,咱们能够对其进一些特殊处理。网络
storePassword System.getenv("KSTOREPWD") keyPassword System.getenv("KEYPWD")
storePassword System.console().readLine("\nKeystore password: ") keyPassword System.console().readLine("\nKey password: ")
上面两种是Android Develop官网提供的,但通过测试都会报null异常,查了下资料都说是gradle不支持(若是有成功的能够告知我),因此仍是推荐下面的这种方法闭包
在项目的根目录下(settings.gradle平级)建立keystore.properties文件,咱们在这个文件中进行存储秘钥,它是支持key-value模式的键值对数据app
storePassword = android123 keyPassword = android123
以后就是读取其中的password,在build.gradle经过afterEvaluate回调进行读取与设置学习
afterEvaluate { def propsFile = rootProject.file('keystore.properties') def configName = 'config' if (propsFile.exists() && android.signingConfigs.hasProperty(configName)) { def props = new Properties() props.load(new FileInputStream(propsFile)) android.signingConfigs[configName].keyPassword = props['keyPassword'] android.signingConfigs[configName].storePassword = props['storePassword'] } }
咱们已经经过动态读取了password,因此在以前的signingConfigs中就无需再配置password
signingConfigs { config { storeFile file('key.store') keyAlias 'android' } }
最后一步,为了保证秘钥的安全性,在.gitignore中添加keystore.properties的忽略配置,防止上传到远程仓储暴露秘钥。
构建变体主要用来配置shrinkResources:资源是否须要压缩、zipAlignEnabled:压缩是否对齐、minifyEnabled:是否代码混淆与signingConfig:签名配置等等。新建项目时,默认有一个release配置,但咱们实际开发中可能须要多个不一样的配置,例如debug模式,为了方法调试,通常都不须要对其进行代码混淆、压缩等处理。或者outer模式,须要的签名配置不一样,因此最终的配置能够是这样:
buildTypes { debug { minifyEnabled false zipAlignEnabled false shrinkResources false signingConfig signingConfigs.config } outer { minifyEnabled false zipAlignEnabled false shrinkResources false signingConfig signingConfigs.outConfig } release { minifyEnabled true zipAlignEnabled true shrinkResources true signingConfig signingConfigs.config proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } }
Sync Now以后,打开Android Studio 右边的Gradle,找到app->Tasks->build,发现已经添加了assembleDebug与assembleOuter构建task。
一个项目可能有不一样的版本环境,例如开发功能中的开发版、项目上线的正式版。开发版与正式版请求的数据api可能不一样,对于这种状况咱们就可使用productFlavor来构建不一样的产品风格,能够看下面的dev与prod配置
flavorDimensions "mode" productFlavors { dev { applicationIdSuffix ".dev" dimension "mode" manifestPlaceholders = [PROJECT_NAME: "@string/app_name_dev", APP_ID : "21321843"] buildConfigField 'String', 'API_URL', '"https://dev.idisfkj.android.com"' buildConfigField 'String', 'APP_KEY', '"3824yk32"' } prod { applicationIdSuffix ".prod" dimension "mode" manifestPlaceholders = [PROJECT_NAME: "@string/app_name", APP_ID : "12932843"] buildConfigField 'String', 'API_URL', '"https://prod.idisfkj.android.com"' buildConfigField 'String', 'APP_KEY', '"32143dsk2"' } }
对于判断是否为同一个app,手机系统是根据app的applicationId来识别的,默认applicationId是packageName。因此为了让dev与prod的版本都能共存在一个手机上,能够经过applicationIdSuffix来为applicationId增长后缀,改变安装包的惟一标识。
还有能够经过manifestPlaceholders来配置可用于AndroidManifest中的变量,例如根据不一样的产品风格显示不一样的app名称
dev与prod网络请求时使用不一样的api host,能够设置buildConfigField,这样咱们就能够在代码中经过BuildConfig获取
fun getApiUlr(): String { return BuildConfig.API_URL }
这里的BuildConfig会根据你构建的产品风格返回不一样的值,它位于build->generated->source->buildConfig->变体,大体内容以下:
public final class BuildConfig { public static final boolean DEBUG = Boolean.parseBoolean("true"); public static final String APPLICATION_ID = "com.idisfkj.androidapianalysis.dev"; public static final String BUILD_TYPE = "debug"; public static final String FLAVOR = "devMinApi21"; public static final int VERSION_CODE = 20001; public static final String VERSION_NAME = "1.0-minApi21"; public static final String FLAVOR_mode = "dev"; public static final String FLAVOR_api = "minApi21"; // Fields from product flavor: dev public static final String API_URL = "https://dev.idisfkj.android.com"; public static final String APP_KEY = "3824yk32"; }
Sync Now以后,打开Android Studio 右边的Gradle,找到app->Tasks->build,发现新添加了assembleDev与assembleProd构建task。
flavorDimensions是用来设置多维度的,上面的例子只展现了一个维度,因此dimension为mode的形式。咱们新增一个api维度,构建不一样的minSkdVerison版本的apk
flavorDimensions "mode", "api" productFlavors { dev { applicationIdSuffix ".dev" dimension "mode" manifestPlaceholders = [PROJECT_NAME: "@string/app_name_dev", APP_ID : "21321843"] buildConfigField 'String', 'API_URL', '"https://dev.idisfkj.android.com"' buildConfigField 'String', 'APP_KEY', '"3824yk32"' } prod { applicationIdSuffix ".prod" dimension "mode" manifestPlaceholders = [PROJECT_NAME: "@string/app_name", APP_ID : "12932843"] buildConfigField 'String', 'API_URL', '"https://prod.idisfkj.android.com"' buildConfigField 'String', 'APP_KEY', '"32143dsk2"' } minApi16 { dimension "api" minSdkVersion 16 versionCode 10000 + android.defaultConfig.versionCode versionNameSuffix "-minApi16" } minApi21 { dimension "api" minSdkVersion 21 versionCode 20000 + android.defaultConfig.versionCode versionNameSuffix "-minApi21" } }
gradle建立的构建变体数量等于每一个风格维度中的风格数量与你配置的构建类型数量的乘积,因此上面例子的构建变体数量为12个。在gradle为每一个构建变体或对应apk命名时,属于较高优先级风格维度的产品风格首先显示,以后是较低优先级维度的产品风格,再以后是构建类型。而优先级的判断则以flavorDimensions的值顺序为依据,以上面的构建配置为例:
构建变体:dev, prod[debug, outer, release]
对应apk:app-[dev, prod]-[minApi16, minApi21]-[debug, outer, release].apk
构建变体有这么多,但有时咱们并不所有须要,例如你不须要mode为dev,api为minApi16的变体,这时你就可使用variantFilter方法来过滤
variantFilter { variant -> def names = variant.flavors*.name if (names.contains("minApi16") && names.contains("dev")) { setIgnore(true) } }
你再回到app->Tasks中查看变体,会发现已经将devMinApi16相关的变体过滤了。
你不只能够过滤构建变体,还能够改变默认的apk输出名称。例如你想修改buildType为release的apk名称,这时你可使用android.applicationVariants.all
android.applicationVariants.all { variant -> if (variant.buildType.name == buildTypes.release.name) { variant.outputs.all { outputFileName = "analysis-release-${defaultConfig.versionName}.apk" } } }
这样在release下的包名都是以analysis打头
Android Studio会帮助咱们建立默认的main源集与目录(位于app/src/main),用来存储全部构建变体间的共享资源。因此你能够经过设置main源集来更改默认的配置。例如如今你想将res的路径修改为src/custom/res
sourceSets { main { res.srcDirs = ['src/custom/res'] } }
这样res资源路径就定位到了src/custom/res下,固然你也能够修改其它的配置,例如java、assets、jni等。
若是你配置了多个路径,即路径集合:
sourceSets { main { res.srcDirs = ['src/custom/res', 'scr/main/res'] } }
这时你要保证不能有相同的名称,即每一个文件只能惟一存在其中一个目录下。
你也能够查看因此的构建变体的默认配置路径: 点击右边gradle->app->android->sourceSets,你将会看到以下相似信息
------------------------------------------------------------ Project :app ------------------------------------------------------------ androidTest ----------- Compile configuration: androidTestCompile build.gradle name: android.sourceSets.androidTest Java sources: [app/src/androidTest/java] Manifest file: app/src/androidTest/AndroidManifest.xml Android resources: [app/src/androidTest/res] Assets: [app/src/androidTest/assets] AIDL sources: [app/src/androidTest/aidl] RenderScript sources: [app/src/androidTest/rs] JNI sources: [app/src/androidTest/jni] JNI libraries: [app/src/androidTest/jniLibs] Java-style resources: [app/src/androidTest/resources] ...
上面是androidTest变体的默认路径,首先它会去查找相应的构建变体的默认位置,若是没有找到,就会使用main源集下的默认配置。也就是咱们所熟悉的app/src/main路径下的资源。
由于它是跟构建变体来搜索的,因此它有个优先级:
对于源集的建立,以下所示在app/src下右键新建,但它只会帮你建立源集下的java文件夹,其它的都要你本身逐个建立
咱们自定义一个debug源集,因此进去以后Target Source Set选择debug,再点击finish结束。这时你将会在src下看到debug文件夹
如今你已经有了debug的源集目录,假设你如今要使debug下的app名称展现成Android精华录debug(默认是Android精华录)。这时你能够右键debug新建values
在values目录下新建strings.xml,而后在其中配置app_name
<?xml version="1.0" encoding="utf-8"?> <resources> <string name="app_name">Android精华录debug</string> </resources>
最后你再去构建debug相关的变体时,你安装的app展现的名称将是Android精华录debug。
因此经过修改mian源集或者配置其它的变体源集,能够实现根据变体加载不一样的数据源。这样系统化的配置加载资源将更加方便项目测试与版本须要的配置。
dependencies闭包上用来配置项目的第三方依赖,若是你根据上面的配置有设置变体,那么你将能够根据变体来选择性的依赖第三方库
dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version" implementation 'com.android.support:appcompat-v7:26.1.0' implementation 'com.android.support.constraint:constraint-layout:1.0.2' testImplementation 'junit:junit:4.12' androidTestImplementation 'com.android.support.test:runner:1.0.1' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1' //根据变体选择性依赖 outerImplementation '...' prodMinApi21Implementation '...' }
关于dependencies,这只是简单的配置方式,以后我还会单独抽出一篇文章来写系统化的配置dependencies,感兴趣的能够关注下。
gradle相关的配置还有不少,这里只是冰山一角,但个人建议是根据你的实际需求去学习与研究,相信你也会有意想不到的成长。
最后附上源码地址:https://github.com/idisfkj/an...
博客地址:https://www.rousetime.com/