写这篇文章主要是因为,公司的项目对打包有多重需求,有哪些需求呢?
1.多渠道,这是最基本的;
2.分为3个版本(内地版,海外版,精简版),每个版本包名不同(这就意味着第三方sdk的各种key都不一样),且logo,部分图标也不一样;
3.开发版本的区别,分为debug版,pre预览版,release正式版;
4.开发版本的区别要通过app名称直接体现在桌面上,即apk安装后,app名称直接是XXdebug版,XXrelease版本
现在我们就分别来实现以上的需求
一.多渠道打包
gradle的productFlavors用来配置渠道,这里主要介绍一下友盟的多渠道打包配置
- 在AndroidManifest.xml中做如下配置
${...}这个占位符很重要,下面我们动态配置一些属性的时候都会用到这个占位符
- 在项目app这个module下的gradle中配置渠道,注意这个productFlavors是配置在android下的
//配置多渠道
flavorDimensions "default"
productFlavors {
wandoujia {dimension "default"}
_360 {dimension "default"}
baidu {dimension "default"}
xiaomi {dimension "default"}
tencent {dimension "default"}
taobao {dimension "default"}
}
productFlavors.all { flavor ->
flavor.manifestPlaceholders = [UMENG_CHANNEL_VALUE: name]
}
-
如上配置后,在AndroidStudio的Build->Select Build Variants ,可以看到每个渠道都有debug和release两个版本
- 4 多版本apk安装在同一手机上,也就是打多版本
这是看到了这位大神的文章,觉得讲的比我写的更详细,下面部分就直接使用大神文章,谢谢作者
这种情况只需要我们提供不同包名的apk即可完成。因为只要应用包名不一样即使签名信息一样还是可以同时安装在同一台手机上的,因此我们应该在打包成apk时修改应用的包名就可以达到目的啦。接下来我们进入实际操作过程。这里我们先介绍一个知识点,请直接看下图:
当然从截图也可以看出,配置多apk打包和上一篇文章配置多渠道打包是一样的,都是在productFlavors中配置的。如上图,我们在productFlavors中配置了两种flavor的apk信息一种是Beta版,一种是Releases版,同时每个flavor中我们都重新配置applicationId这个属性,通过这个属性我们就可以使打包出来的apk包名产生对应的变化啦。至于为什么重新配置了applicationId就行呢,原因图已经说明啦,就是因为defaultConfig是Beta版和Releases版flavor的基础配置,只要我们重写了applicationId这个属性就会覆盖defaultConfig中相对应属性的信息,从而使打包出来的两种apk的包名不一样,达到在同一台手机上安装的目的。那么applicationId又是什么呢?看下图(因此我们更改其实就是package属性)
比如说不同版本要使用不同的icon,这时该如何做呢?实际上还是在productFlavors的每个flavor中通过manifestPlaceholders属性配置即可,manifestPlaceholders是一个类似HashMap的容器,因此在manifestPlaceholders可以配置多个属性,以便在AndroidManifest.xm中使用,比如我们需要为每种版本的apk替换特定的icon和appName这时我们可以这样如下配置:
然后在AndroidManifest.xm中这样使用即可:
defaultConfig {
applicationId "com.zejian.multi_versionapk"
minSdkVersion 10
targetSdkVersion 23
versionCode 1
versionName "1.0"
}
buildTypes {
release {
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.debug4zj
minifyEnabled true
zipAlignEnabled true
}
}
//配置多版本的apk
productFlavors{
Beta{
applicationId "com.zejian.multi_versionapk.beta"
manifestPlaceholders = [app_name:"multi_versionapk.beta" ,icon: "@mipmap/ic_launcher_beta"]
//在java代码中具体的使用方式为:context.getResources().getString(R.string.strKey);
resValue("string" , "strKey","beta版本")
}
Releases{
applicationId "com.zejian.multi_versionapk.release"
manifestPlaceholders = [app_name:"multi_versionapk.release",icon: "@mipmap/ic_launcher_releases"]
resValue("string" , "strKey","release版本")
}
}
}
同时,我们Generate signed APK就可以打出release和debug的每个版本和每个渠道的包
至此,我们就可以通过Build Variants来控制当前的运行环境,来决定当前的包是属于哪个版本和哪个渠道
二. 多渠道包跟多版本包不同的内容,如何来区分,也就是文章开头第二条中的icon,logo,包名等信息不同时候如何来区别对待,那就要用到manifestPlaceholders了,文章上面说过,我们使用${...}占位符能够动态替换AndroidManitest里面的很多东西,如何操作请往下看
wandoujia {
dimension "default"
manifestPlaceholders = [
applicationId :"com.app.test",
app_name : "@string/AppName",
icon : "@string/ic_launcher",
app_key : "1234567",
umeng_channel: "豌豆荚"
]
}
wandoujia_oas {
dimension "default"
manifestPlaceholders = [
applicationId :"com.app.oas",
app_name : "@string/AppNameOas",
icon : "@string/ic_launcher",
app_key : "910JQKA",
umeng_channel: "豌豆荚海外版"
]
}
这样第三方SDK的不同key都可以采用占位符来动态替换,就像上面,当我们打海外版包时,就会将app_key和logo替换成另一套
三. 针对debug版本和release版本一些控制,就要用到 buildConfigField 了,buildConfigField 能够在debug和release中生成不同的BuildConfig,我们在开发中的是否需要打印日志的控制就可以使用它
buildTypes {
release {
minifyEnabled false
debuggable false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
buildConfigField("boolean","show_log","false")
}
//预览版
pre {
minifyEnabled false
debuggable false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
buildConfigField("boolean","show_log","true")
}
debug {
minifyEnabled false
debuggable true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
buildConfigField("boolean","show_log","true")
}
}
配置之后我们得到的BuildConfig如下,在代码中使用直接BuildConfig.xxx就可以
public final class BuildConfig {
public static final boolean DEBUG = false;
public static final String APPLICATION_ID = "kotlin.app.com.gradledemo";
public static final String BUILD_TYPE = "release";
public static final String FLAVOR = "baidu";
public static final int VERSION_CODE = 1;
public static final String VERSION_NAME = "1.0";
// Fields from build type: release
public static final boolean show_log = false;
}
除了日志开关,其他用的最多的地方还有url环境的切换,比如debug版本一般我们会用内网的url,那么release版本就需要用生产环境的url
四. 要实现开篇的不同版本的apk名称不一样,就需要用到resValue了,它能够定义资源,并且可以在debug和release等不同版本中来分别定义,例如resValue "string" 就是字符串资源,可以用R.String 来引用对应的字符串资源
//在debug下设置如下
resValue "string","AppName","测试DEV"
resValue "string","AppNameOas","测试海外版DEV"
resValue "string","AppNameSimple","测试1精简版DEV"
这时候一编译,就会自动在R.String下生成AppName,AppNameOas,AppNameSimple三个字符串,通过R.String.xxx就可以引用到;
注意:需要将之前values下string下的appName删除,否则会报资源重复异常
五. 全局配置文件
project的build.gradle中的ext可以为各位module进行全局配置参数,防止各个module之间的不统一,不可控。而且当我们升级sdk、build tool、target sdk等,几个module都要更改,非常的麻烦。
在项目根目录建立gradleConfig,然后
ext {
//统一管理依赖版本
android = [
compileSdkVersion: 25,
buildToolsVersion: '25.0.2',
applicationId : "com.test.xxx",
minSdkVersion : 17,
targetSdkVersion : 24,
versionCode : 2
versionName : "0.1"
]
def dependVersion = [
support: "25.1.0"
]
dependencies = [
"appcompat-v7" : "com.android.support:appcompat-v7:${dependVersion.support}",
"cardview-v7" : "com.android.support:cardview-v7:${dependVersion.support}",
"recyclerview-v7": "com.android.support:recyclerview-v7:${dependVersion.support}",
"design" : "com.android.support:design:${dependVersion.support}",
"support-v4" : "com.android.support:support-v4:${dependVersion.support}"
]
}
然后在每个module中引用
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
}
}
六. module 调整目录结构sourceSets
默认情况下,java文件和resource文件分别在src/main/java和src/main/res目录下,在build.gradle文件,andorid{}里面添加下面的代码,便可以将java文件和resource文件放到src/java和src/resources目录下。
sourceSets {
main {
java {
srcDir 'src/java'
}
resources {
srcDir 'src/resources'
}
}
}
简便写法
sourceSets {
min.java.srcDirs = ['src/java']
min.resources.srcDirs = ['src/resources']
}