场景:在应用中牵扯到第三方平台集成的话,因为很多第三方要求正式签名后生成的包才可以正常使用。所以,在debug模式下配置正式签名包,就可以解决问题。
配置示例:
android {
...
signingConfigs {
myConfig {
//storeFile file("你的storeFile")当签名文件在app目录下的时候不用写路径。也可以放在别的目录下,写出路径即可:storeFile file("D:/wh/wh-keystore.jks")
storeFile file("你的storeFile")
storePassword "你的storePassword"
keyAlias "你的keyAlias"
keyPassword "你的keyAlias"
}
}
buildTypes {
debug {//android studio直接运行的时候是安装的这里生成的apk,不配置这里相当于运行时用的还是临时签名
signingConfig signingConfigs.myConfig//签名
}
release {
signingConfig signingConfigs.myConfig//签名
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
...
}
对于签名相关的信息,直接写在gradle当然不好,特别是一些开源项目,可以添加到local.properties,然后从中读取需要的信息:
示例:
key.file=D\:/wh/wh-keystore.jks
storePassword=xxx
keyAlias=xxx
keyPassword=xxx
然后在build.gradle中引用即可:
android {
...
signingConfigs {
myConfig {
//加载资源
Properties properties = new Properties()
InputStream inputStream = project.rootProject.file('local.properties').newDataInputStream() ;
properties.load( inputStream )
//读取文件
def sdkDir = properties.getProperty('key.file')
storeFile file( sdkDir )
//读取字段
def key_storePassword = properties.getProperty( 'storePassword' )
def key_keyAlias = properties.getProperty( 'keyAlias' )
def key_keyPassword = properties.getProperty( 'keyPassword' )
storePassword key_storePassword
keyAlias key_keyAlias
keyPassword key_keyPassword
}
}
...
}
在build.gradle中动态的替换掉java中的变量。
在你的 gradle 文件 buildTypes 或者 productFlavors 下面,debug、release等体内写buildConfigField 设置,示例如下:
android{
...
buildTypes {
debug {
buildConfigField ("String", "APP_HOST ", "\"http://example.com1/\"")
buildConfigField ("boolean", "LOG_DEBUG", "false")
}
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
buildConfigField ("String", "APP_HOST ", "\"http://example.com2/\"")
buildConfigField ("boolean", "LOG_DEBUG", "false")
}
}
...
}
gradle sync 一下后,BuildConfig.APP_HOST 就会被赋值为 http://example.com 就可以供 Java 代码调用了。使用例子:
有时会不同步,重新build一下。
public static final String MY_URL= BuildConfig.APP_HOST + "xxx";//显示http://example.com/xxx
if (BuildConfig.LOG_DEBUG) {
//不打日志
}else{
//打日志
}
场景:例如:百度地图等服务 在多渠道打包的时候,不同的包得需要配置不同的API_KEY(包名不一样平台会生成不同的API_KEY)。
在AndroidManifest中定义一个变量,在build.gradle中动态的替换掉,十分方便,语法也十分简单。
例子:
在AndroidManifest中定义一个变量
<meta-data
android:name="UMENG_APPKEY"
android:value="${umeng_app_key}"/>
<meta-data
android:name="UMENG_SECRET"
android:value="${umeng_app_secret}"/>
接着,我们在build.gradle文件中根据不同的环境或者不同的包(多渠道打包,内容有差别,详看后文)中,生成不同appkey的apk。
buildTypes {
debug {
/*一博客里看到的可以这样用,记下备忘:
manifestPlaceholders = [app_label:"@string/app_name_debug"]*/
manifestPlaceholders = [
umeng_app_key: "你替代的内容",
umeng_app_secret:"你要替换的内容"]
}
release {
manifestPlaceholders = [
umeng_app_key: "你替代的内容",
umeng_app_secret:"你要替换的内容"]
}
develop {
manifestPlaceholders = [
umeng_app_key: "你替代的内容",
umeng_app_secret:"你要替换的内容"]
}
}
注意:这里的“你替代的内容”,不能为特殊关键词,比如:TRUE,否则在Java代码中获取不到meta-data中的值
不止在meta-data中,其他地方也可以配置,如
QQ的APP_ID,个推的APP_ID。
<activity
android:name="com.mob.tools.MobUIShell"
...>
<intent-filter>
<data android:scheme="tencent${QQ_APP_ID}" />
...
intent-filter>
activity>
<receiver
...
>
<action android:name="com.igexin.sdk.action.${GETUI_APP_ID}" />
intent-filter>
receiver>
在多渠道打包中使用示例:
android{
...
//多渠道打包
productFlavors {
one {
...
//个推配置参数
manifestPlaceholders = [
//个推
GETUI_APP_ID : "xxxxxxxx",
//qq的APPID
QQ_APP_ID : "xxxxxxxx",
]
return true
}
two {
...
manifestPlaceholders = [
GETUI_APP_ID : "xxxxxxxxxx",
QQ_APP_ID : "xxx",
]
return true
}
...
}
随着产品渠道的铺开,往往一套代码需要支持多个产品形态,这就需要抽象出主要代码到一个Library,然后基于Library扩展几个App Module。
相信每个module的build.gradle都会有这个代码:
android {
compileSdkVersion 22
buildToolsVersion "23.0.1"
defaultConfig {
minSdkVersion 10
targetSdkVersion 22
versionCode 34
versionName "v2.6.1"
}
}
当升级sdk、build tool、target sdk等,几个module都要更改,非常的麻烦。最重要的是,很容易忘记,最终导致app module之间的差异不统一,也不可控。
强大的gradle插件在1.1.0支持全局变量设定,一举解决了这个问题。
先在project的根目录下的build.gradle定义ext全局变量:
ext {
compileSdkVersion = 22
buildToolsVersion = "23.0.1"
minSdkVersion = 10
targetSdkVersion = 22
versionCode = 34
versionName = "v2.6.1"
}
然后在各module的build.gradle中引用如下:
android {
compileSdkVersion rootProject.ext.compileSdkVersion
buildToolsVersion rootProject.ext.buildToolsVersion
defaultConfig {
applicationId "com.xxx.xxx"
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
versionCode rootProject.ext.versionCode
versionName rootProject.ext.versionName
}
}
然后每次修改project级别的build.gradle即可实现全局统一配置。
static def releaseTime() {
return new Date().format("MMdd", TimeZone.getTimeZone("UTC"))
}
android{
...
//不同的打包生成自定义的名称
applicationVariants.all { variant ->
variant.outputs.all { output ->
def outputFile = output.outputFile
if (outputFile != null && outputFile.name.endsWith('.apk')) {
def huanJing;
if (variant.buildType.name == 'release') {
huanJing = "s";
} else if (variant.buildType.name == 'debug') {
huanJing = "c";
} else {
huanJing = "${variant.buildType.name}";
}
def apkName
// 如果有渠道,显示渠道名""
if (variant.flavorName != "") {
apkName = huanJing + "${releaseTime()}${variant.productFlavors[0].name}${variant.versionName}.apk"/*名字示例:s0613one1.2.apk*/
} else {
apkName = huanJing + "${releaseTime()}v${variant.versionName}.apk"
}
outputFileName = apkName
}
}
}
...
}
根据业务的需求,有时我们需要打不同的包,发布不同的版本,可在gradle中进行配置。
android{
...
defaultConfig {
...
flavorDimensions "default"
...
}
//多渠道打包
productFlavors {
one {
applicationId "com.hjqjl.testdemo"
versionCode 2
versionName "1.2"
//配置参数
manifestPlaceholders = [
PACKAGE_NAME : applicationId,
//百度
BAIDU_API_KEY: "0",
]
//buildConfigField ("String", "APP_PRODUCTID", "\"my1\"")
//如果想在代码里面使用BAIDU_API_KEY,如下:
//buildConfigField "String", "BAIDU_API_KEY", "\"" + manifestPlaceholders.BAIDU_API_KEY + "\""
return true
}
two {
applicationId "com.hjqjl.two"
versionCode 3
versionName "1.3"
manifestPlaceholders = [
PACKAGE_NAME : applicationId,
BAIDU_API_KEY: "0",
]
return true
}
}
...
}
多渠道打包的时候不同的版本可能有一部分代码不同或布局或资源文件,需要在src下创建与渠道名相同的文件夹,将资源文件放进去。运行时会自动获取相应文件下的资源文件。
apply plugin: 'com.android.application'
static def releaseTime() {
return new Date().format("MMdd", TimeZone.getTimeZone("UTC"))
}
android {
compileSdkVersion 29
defaultConfig {
minSdkVersion 21
targetSdkVersion 29
flavorDimensions "default"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
//多渠道打包
productFlavors {
one {
applicationId "com.hjqjl.testdemo"
versionCode 2
versionName "1.2"
//配置参数
manifestPlaceholders = [
PACKAGE_NAME : applicationId,
//百度
BAIDU_API_KEY: "0",
]
buildConfigField("String", "APP_PRODUCTID", "\"my1\"")
//如果想在代码里面使用BAIDU_API_KEY,如下:
//buildConfigField "String", "BAIDU_API_KEY", "\"" + manifestPlaceholders.BAIDU_API_KEY + "\""
return true
}
two {
applicationId "com.hjqjl.two"
versionCode 3
versionName "1.3"
manifestPlaceholders = [
PACKAGE_NAME : applicationId,
BAIDU_API_KEY: "0",
]
buildConfigField("String", "APP_PRODUCTID", "\"my2\"")
return true
}
}
buildTypes {
debug {
buildConfigField("String", "APP_HOST ", "\"http://example.com/\"")
buildConfigField("boolean", "LOG_DEBUG", "false")
}
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
buildConfigField("String", "APP_HOST ", "\"http://example.com/\"")
buildConfigField("boolean", "LOG_DEBUG", "false")
}
}
/* //注释部分为配置签名信息(实现运行的时候用真实签名,不是临时签名),
//因为签名文件等 位于本地,不上传Gitee,clone下来要进行调试,所以这一部分进行注释了。
signingConfigs {
//从本地读取签名信息,配置签名信息
myConfig {
//加载资源
Properties properties = new Properties()
InputStream inputStream = project.rootProject.file('local.properties').newDataInputStream()
properties.load(inputStream)
//读取文件
def sdkDir = properties.getProperty('key.file')
storeFile file( sdkDir )
//读取字段
def key_storePassword = properties.getProperty( 'storePassword' )
def key_keyAlias = properties.getProperty( 'keyAlias' )
def key_keyPassword = properties.getProperty( 'keyPassword' )
storePassword key_storePassword
keyAlias key_keyAlias
keyPassword key_keyPassword
}
}
buildTypes {
debug {
//android studio直接运行的时候是安装的这里签名生成的apk,不配置这里相当于运行时用的还是临时签名
// (myConfig的名字如果是debug的话,因为默认名字的关系,不配置这里也会用你的签名)
signingConfig signingConfigs.myConfig//签名
}
release {
signingConfig signingConfigs.myConfig//签名
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
*/
//不同的打包生成自定义的名称
applicationVariants.all { variant ->
variant.outputs.all { output ->
def outputFile = output.outputFile
if (outputFile != null && outputFile.name.endsWith('.apk')) {
def huanJing;
if (variant.buildType.name == 'release') {
huanJing = "s";
} else if (variant.buildType.name == 'debug') {
huanJing = "c";
} else {
huanJing = "${variant.buildType.name}";
}
def apkName
// 如果有渠道,显示渠道名""
if (variant.flavorName != "") {
apkName = huanJing + "${releaseTime()}${variant.productFlavors[0].name}${variant.versionName}.apk"
/*名字示例:s0613one1.2.apk*/
} else {
apkName = huanJing + "${releaseTime()}v${variant.versionName}.apk"
}
outputFileName = apkName
}
}
}
buildFeatures {
viewBinding = true
}
}
dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
implementation 'com.gitee.hjqjl:AndroidUtils:1.0.2-beta'
}
源码:https://gitee.com/hjqjl/WhDemo