Android 有不同的应用市场,也就是不同的渠道,需要为每个应用市场打一个安装包,但主要的代码是一样的,可能部分资源不一样,部分代码不一样,如果每个渠道都需要修改,然后打包,非常耗时。所以 AS 是提供了多渠道打包的。
可能遇到的需求
- 不同渠道
applicationId
不一样; - 不同渠道配置参数不一样;
- 不同渠道签名文件不一样;
- 不同渠道资源文件不一样;
- 不同渠道部分代码不一样;
- 不同渠道依赖不一样;
这里会先说一下初级版配置,再说升级版配置—— Grovvy
进行自动化构建。
初级版多渠道配置
productFlavors
:不同产品口味,就是AS自带的不同渠道打包关键字。可以进行多渠道配置,有两种方式。
1、在 app 模块下的 build.gradle
配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137// 读取不同的签名文件 def getSignProperties(filename){ File signConfigFile = file("${rootProject.rootDir}/app/keystore/${filename}.properties") Properties signProperties = new Properties() signProperties.load(new FileInputStream(signConfigFile)) return signProperties } android { compileSdk 32 defaultConfig { applicationId "com.XXX" minSdk 21 targetSdk 32 versionCode 5 versionName "3.0" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } // 不同渠道的签名 signingConfigs { release { def signProperties = getSignProperties('signing') storeFile file(signProperties['KEYSTORE_FILE']) storePassword signProperties['KEY_PASSWORD'] keyAlias signProperties['KEY_ALIAS'] keyPassword signProperties['KEY_PASSWORD'] } //不同的渠道,定义不同的签名文件 huawei { def signProperties = getSignProperties('signing-huawei') storeFile file(signProperties['KEYSTORE_FILE']) storePassword signProperties['KEY_PASSWORD'] keyAlias signProperties['KEY_ALIAS'] keyPassword signProperties['KEY_PASSWORD'] } xiaomi { def signProperties = getSignProperties('signing-xiaomi') storeFile file(signProperties['KEYSTORE_FILE']) storePassword signProperties['KEY_PASSWORD'] keyAlias signProperties['KEY_ALIAS'] keyPassword signProperties['KEY_PASSWORD'] } } // 配置不同渠道参数 productFlavors{ huawei{ applicationId ="com.xxx" //渠道参数 buildConfigField "String", "token", ""XXXX"" // manifest 读取的参数,在 manifest 里如何使用,见后文 manifestPlaceholders=[ "app_name":"CCCCC" ] } // 其他渠道类似 } // 配置打包签名 buildTypes { debug { minifyEnabled false debuggable true shrinkResources false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' signingConfig signingConfigs.release } release { minifyEnabled true debuggable false shrinkResources false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' //signingConfig signingConfigs.release productFlavors.xiaoxing236.signingConfig signingConfigs.huawei productFlavors.xiaoxing238.signingConfig signingConfigs.xiaomi } } // 指定打包输出的路径 applicationVariants.all { variant -> // 打包完成后输出路径 def name = variant.flavorName + "_" + variant.buildType.name + "_" + variant.versionName + "_" + new Date().format('yyyyMMddhhmm') + ".apk" //相对路径app/build/outputs/apk/huawei/release/ def path = "../../../../../apk/" //相当于路径 app/apk/ variant.outputs.each { output -> def outputFile = output.outputFile if (outputFile != null && outputFile.name.endsWith('.apk') && outputFile.name.contains('release')) { //指定路径输出 output.outputFileName = new File(path, name) } } } //不同渠道不同资源文件 // sourceSets{ } 源文件目录设置 sourceSets { // 公共代码及资源 main { jniLibs.srcDirs = ['libs'] } // 不同资源 huawei.res.srcDirs 'src/huawei/res' xiaomi.res.srcDirs 'src/xiaomi/res' // 其他渠道类似,以下不再重复 //不同代码 huawei.java.srcDirs 'src/huawei/java' xiaomi.java.srcDirs 'src/xiaomi/java' // 不同渠道 manifest 文件 huawei.manifest.srcFile 'src/huawei/AndroidManifest.xml' xiaomi.manifest.srcFile 'src/xiaomi/AndroidManifest.xml' } } // 不同渠道的依赖 dependencies { // 公共的依赖 implementation 'ccccc' // 不同渠道依赖 xiaomiApi('xxxxxxx') huaweiImplementation('xxxxxxxx') }
不同渠道配置的参数需要在 manifest 里使用
1
2
3
4
5
6
7
8
9
10<application android:name="${applicationId}.GlobalApplication" android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="${app_name}" android:roundIcon="@mipmap/ic_launcher" android:supportsRtl="true"> </application>
在代码里使用 buildconfig 参数
1
2private final String TOKEN = BuildConfig.token;
2、可以把以上不同渠道的配置单独放在一个 flavor.gradle
文件里,该文件与 setting.gradle
目录同级。 然后在 app 模块的 build.gradle
引用 flavor.gradle
文件即可。
1
2apply from: ("${rootProject.rootDir}/flavor.gradle")
升级版渠道配置
按照以上配置方式,每增加一个渠道,就得每个渠道重新写一遍 huawei.manifest.srcFile
等这种操作,会让 build.gradle
显得非常臃肿。可以通过固定规则,写脚本解决以上问题。
1、在项目中创建出打包脚本文件夹 buildSrc,在此文件夹下创建 src/resource/**META-INF/gradle-plugins
路径及文件夹名固定。**
2、定义自动构建插件路径,在 src/resource/**META-INF/gradle-plugins
路径下创建一个 xxx.properties 文件,文件内定义构建脚本路径。**
1
2
3// 路径是写脚本的文件路径 implementation-class=com.xxx.plugin.PackagePlugin
3、在 build.gradle 里引入相关仓库
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24//依赖 groovy 插件,这个是 Gradle 内置的插件 plugins { `kotlin-dsl` `java-gradle-plugin` groovy } val androidGradlePlugin = "com.android.tools.build:gradle:4.2.2" val kotlin_version = "1.6.10" //引入相关的仓库 dependencies { // 导入androidGradlePlugin,这样buildSrc可以使用gradle相关api implementation(androidGradlePlugin) // Depend on the kotlin plugin, since we want to access it in our plugin implementation("org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlin_version}") // Depend on the default Gradle API since we want to build a custom plugin implementation(gradleApi()) implementation(localGroovy()) }
4、在主模块(app)模块的 build.gradle
中引入插件。
1
2
3// plugin 的名字是第 2 步创建 properties 的名字 apply plugin: 'PackPlugin'
5、在 PackagePlugin
中开始编写自动构建脚本
编写脚本用的是 groovy 语法,可以参考这篇文章:Gradle插件从入门到进阶
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63class PackagePlugin : Plugin<Project> { // plugin 必须实现的方法 override fun apply(target: Project) { // 获取 android extension var appExtension = target.extensions.getByName("android") as AppExtension // 多渠道构建 appExtension.productFlavors { var channelList = getChannelList() channelList.forEach { channelModel -> register(channelModel.channelName) { // 每个渠道的需要配置的参数,可以根据自己的规则订 applicationId = channelModel.packageName versionCode = channelModel.versionCode versionName = "${channelModel.versionCode}.0" // manifest 需要配置的参数 manifestPlaceholders["ads_id"] = channelModel.adsId manifestPlaceholders["app_name"] = channelModel.appName // 代码里需要使用的不同渠道配置参数 buildConfigField("String", "XXX", ""${channelModel.定义的属性名}"") buildConfigField("String", "XXX", ""${channelModel.定义的属性名}"") } } } // 签名文件 appExtension.signingConfigs { var channelList = getChannelList() channelList.forEach { channelModel -> var channelName = channelModel.channelName register(channelName) { // 可以单独处理不一样的包 storeFile(getKeyStoreFile(channelName, target)) storePassword(channelName) keyAlias(channelName) keyPassword(channelName) } } } // 不同渠道配置不同的签名文件,签名文件的名字、别名、密码可以自行定义 appExtension.signingConfigs.forEach { signingConfig -> println("PackagePlugin signing:${signingConfig.keyAlias.toString()}") appExtension.productFlavors.getByName(signingConfig.keyAlias.toString()).signingConfig = signingConfig } // 不同渠道的不同代码、资源、和 manifest appExtension.sourceSets { var channelList = getChannelList() channelList.forEach { channelModel -> var channelName = channelModel.channelName getByName(channelName) { res.srcDirs("src/${channelName}/res") java.srcDirs("src/${channelName}/java") manifest.srcFile("src/${channelName}/AndroidManifest.xml") } } } } }
最后
以上就是跳跃飞机最近收集整理的关于Android Studio多渠道打包及自动化构建的全部内容,更多相关Android内容请搜索靠谱客的其他文章。
发表评论 取消回复