当前位置: 首页 > news >正文

Android Studio多渠道打包及自动化构建

Android 有不同的应用市场,也就是不同的渠道,需要为每个应用市场打一个安装包,但主要的代码是一样的,可能部分资源不一样,部分代码不一样,如果每个渠道都需要修改,然后打包,非常耗时。所以 AS 是提供了多渠道打包的。

可能遇到的需求

  1. 不同渠道 applicationId 不一样;
  2. 不同渠道配置参数不一样;
  3. 不同渠道签名文件不一样;
  4. 不同渠道资源文件不一样;
  5. 不同渠道部分代码不一样;
  6. 不同渠道依赖不一样;

这里会先说一下初级版配置,再说升级版配置—— Grovvy 进行自动化构建。

初级版多渠道配置

productFlavors :不同产品口味,就是AS自带的不同渠道打包关键字。可以进行多渠道配置,有两种方式。

1、在 app 模块下的 build.gradle 配置

// 读取不同的签名文件
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 32defaultConfig {applicationId "com.XXX"minSdk 21targetSdk 32versionCode 5versionName "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 falsedebuggable trueshrinkResources falseproguardFiles getDefaultProguardFile('proguard-android-optimize.txt'),'proguard-rules.pro'signingConfig signingConfigs.release}release {minifyEnabled truedebuggable falseshrinkResources falseproguardFiles getDefaultProguardFile('proguard-android-optimize.txt'),'proguard-rules.pro'//signingConfig signingConfigs.releaseproductFlavors.xiaoxing236.signingConfig signingConfigs.huaweiproductFlavors.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.outputFileif (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 里使用


<applicationandroid: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 参数

private final String TOKEN = BuildConfig.token;

2、可以把以上不同渠道的配置单独放在一个 flavor.gradle 文件里,该文件与 setting.gradle 目录同级。 然后在 app 模块的 build.gradle 引用 flavor.gradle 文件即可。

apply 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 文件,文件内定义构建脚本路径。**

// 路径是写脚本的文件路径
implementation-class=com.xxx.plugin.PackagePlugin

3、在 build.gradle 里引入相关仓库

//依赖 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相关apiimplementation(androidGradlePlugin)// Depend on the kotlin plugin, since we want to access it in our pluginimplementation("org.jetbrains.kotlin:kotlin-gradle-plugin:${kotlin_version}")// Depend on the default Gradle API since we want to build a custom pluginimplementation(gradleApi())implementation(localGroovy())
}

4、在主模块(app)模块的 build.gradle 中引入插件。

// plugin 的名字是第 2 步创建 properties 的名字
apply plugin: 'PackPlugin'

5、在 PackagePlugin 中开始编写自动构建脚本

编写脚本用的是 groovy 语法,可以参考这篇文章:Gradle插件从入门到进阶

class PackagePlugin : Plugin<Project> {// plugin 必须实现的方法override fun apply(target: Project) {// 获取 android extensionvar appExtension = target.extensions.getByName("android") as AppExtension// 多渠道构建appExtension.productFlavors {var channelList = getChannelList()channelList.forEach { channelModel ->register(channelModel.channelName) {// 每个渠道的需要配置的参数,可以根据自己的规则订applicationId = channelModel.packageNameversionCode = channelModel.versionCodeversionName = "${channelModel.versionCode}.0"// manifest 需要配置的参数manifestPlaceholders["ads_id"] = channelModel.adsIdmanifestPlaceholders["app_name"] = channelModel.appName// 代码里需要使用的不同渠道配置参数buildConfigField("String", "XXX", "\"${channelModel.定义的属性名}\"")buildConfigField("String", "XXX", "\"${channelModel.定义的属性名}\"")}}}// 签名文件appExtension.signingConfigs {var channelList = getChannelList()channelList.forEach { channelModel ->var channelName = channelModel.channelNameregister(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}// 不同渠道的不同代码、资源、和 manifestappExtension.sourceSets {var channelList = getChannelList()channelList.forEach { channelModel ->var channelName = channelModel.channelNamegetByName(channelName) {res.srcDirs("src/${channelName}/res")java.srcDirs("src/${channelName}/java")manifest.srcFile("src/${channelName}/AndroidManifest.xml")}}}}}

相关文章:

Android Studio多渠道打包及自动化构建

Android 有不同的应用市场&#xff0c;也就是不同的渠道&#xff0c;需要为每个应用市场打一个安装包&#xff0c;但主要的代码是一样的&#xff0c;可能部分资源不一样&#xff0c;部分代码不一样&#xff0c;如果每个渠道都需要修改&#xff0c;然后打包&#xff0c;非常耗时…...

基于MATLAB的MIMO信道估计(附完整代码与分析)

目录 一. 介绍 二. MATLAB代码 三. 运行结果与分析 一. 介绍 本篇将在MATLAB的仿真环境中对比MIMO几种常见的信道估计方法的性能。 有关MIMO的介绍可看转至此篇博客&#xff1a; MIMO系统模型构建_唠嗑&#xff01;的博客-CSDN博客 在所有无线通信中&#xff0c;信号通过…...

Python代码游戏————星球大战

♥️作者:小刘在C站 ♥️个人主页:小刘主页 ♥️每天分享云计算网络运维课堂笔记,努力不一定有收获,但一定会有收获加油!一起努力,共赴美好人生! ♥️夕阳下,是最美的绽放,树高千尺,落叶归根人生不易,人间真情 目录 一.Python介绍 二.游戏效果呈现 三.主代码 四....

java向Word模板中替换书签数据,插入图片,插入复选框,插入Word中表格的行数据,删除表格行数据

java向Word模板中替换书签数据&#xff0c;插入图片&#xff0c;插入复选框&#xff0c;插入Word中表格的行数据&#xff0c;删除表格行数据 使用插件&#xff1a;spire.doc 创建工具类&#xff0c;上代码&#xff1a; import com.spire.doc.Document; import com.spire.doc.…...

Java基础知识快速盘点(二)

一&#xff0c;类型转换 隐式转换 将一个类型转换为另一个类型时&#xff0c;系统默认转换常量优化机制算术运算时类型的隐式转换&#xff08;byte&#xff0c;short在算术运算时都会转换为int&#xff09;char类型在进行运算时会根据其编码值进行运算 显式转换 二&#xff0…...

企业降本增效的催化剂:敏捷迭代

伴随着开源技术的大爆发&#xff0c;新一代的软件技术如雨后春笋般层出不穷。每家企业在硬件及软件开发上都有许多开源技术可选&#xff0c;目的还是在于提高效率&#xff0c;降低开发成本。 本篇文章&#xff0c;带大家了解下促进企业降本增效的重要理念&#xff1a;敏捷迭代…...

MySQL入门篇-MySQL高级窗口函数简介

备注:测试数据库版本为MySQL 8.0 这个blog我们来聊聊MySQL高级窗口函数 窗口函数在复杂查询以及数据仓库中应用得比较频繁 与sql打交道比较多的技术人员都需要掌握 如需要scott用户下建表及录入数据语句&#xff0c;可参考:scott建表及录入数据sql脚本 分析函数有3个基本组成…...

什么是 API(应用程序接口)?

API&#xff08;应用程序接口&#xff09;是一种软件中介&#xff0c;它允许两个不相关的应用程序相互通信。它就像一座桥梁&#xff0c;从一个程序接收请求或消息&#xff0c;然后将其传递给另一个程序&#xff0c;翻译消息并根据 API 的程序设计执行协议。API 几乎存在于我们…...

如何在外网访问内网的 Nginx 服务?

计算机业内人士对Nginx 并不陌生&#xff0c;它是一款轻量级的 Web 服务器/反向代理服务器及电子邮件&#xff08;IMAP/POP3&#xff09;代理服务器&#xff0c;除了nginx外&#xff0c;类似的apache、tomcat、IIS这几种都是主流的中间件。 Nginx 是在 BSD-like 协议下发行的&…...

vue2中defineProperty和vue3中proxy区别

区别一&#xff1a;defineProperty 是对属性劫持&#xff0c;proxy 是对代理对象 下面我们针对一个对象使用不同的方式进行监听&#xff0c;看写法上有什么不同。 // 原始对象 const data {name: Jane,age: 21 }defineProperty defineProperty 只能劫持对象的某一个属性&…...

将bean注入Spring容器的五种方式

前言 我们在项目开发中都用到Spring&#xff0c;知道对象是交由Spring去管理。那么将一个对象加入到Spring容器中&#xff0c;有几种方法呢&#xff0c;我们来总结一下。 ComponentScan Component ComponentScan可以放在启动类上&#xff0c;指定要扫描的包路径&#xff1b;…...

C生万物 | 常量指针和指针常量的感性理解

文章目录&#x1f4da;引言✒常量指针&#x1f50d;介绍与分析&#x1f4f0;小结与记忆口诀✒指针常量&#x1f50d;介绍与分析&#x1f4f0;小结与记忆口诀&#x1f449;一份凉皮所引发的故事&#x1f448;总结与提炼&#x1f4da;引言 本文我们来说说大家很困惑的两个东西&am…...

python 打包工具 pyinstaller和Nuitka区别

1.1 使用需求 这次也是由于项目需要&#xff0c;要将python的代码转成exe的程序&#xff0c;在找了许久后&#xff0c;发现了2个都能对python项目打包的工具——pyintaller和nuitka。 这2个工具同时都能满足项目的需要&#xff1a; 隐藏源码。这里的pyinstaller是通过设置key来…...

Python解题 - CSDN周赛第28期

上一期周赛问哥因为在路上&#xff0c;无法参加&#xff0c;但还是抽空登上来看了一下题目。4道题都挺简单的&#xff0c;有点遗憾未能参加。不过即使参加了&#xff0c;手速也未必能挤进前十。 本期也是一样&#xff0c;感觉新增的题目都偏数学类&#xff0c;基本用不到所谓的…...

DNS记录类型有哪些,分别代表什么含义?

DNS解析将域名指向IP地址&#xff0c;是互联网中的一项重要服务。而由于业务场景不同&#xff0c;在设置DNS解析时&#xff0c;需要选择不同的记录类型。网站管理人员需要准确了解每一种DNS记录类型所代表的含义和用途&#xff0c;才能满足不同场景的解析需求。本文中科三方简单…...

ICLR 2022—你不应该错过的 10 篇论文(上)

CV - 计算机视觉 | ML - 机器学习 | RL - 强化学习 | NLP 自然语言处理 ICLR 2023已经放榜&#xff0c;但是今天我们先来回顾一下去年的ICLR 2022&#xff01; ICLR 2022将于2022年 4 月 25 日星期一至 4 月 29 日星期五在线举行&#xff08;连续第三年&#xff01;&#xf…...

HydroD 实用教程(三)环境数据

目 录一、前言二、Location三、Wind Profile四、Directions五、Water5.1 Wave Spectrums5.2 Current Profile5.3 Frequency Set5.4 Phase Set5.5 Wave Height5.6 Regular Wave Set六、参考文献一、前言 SESAM &#xff08;Super Element Structure Analysis Module&#xff09;…...

第四章 统计机器学习

机器学习&#xff1a;从数据中学习知识&#xff1b; 原始数据中提取特征&#xff1b;学习映射函数f&#xff1b;通过映射函数f将原始数据映射到语义空间&#xff0c;即寻找数据和任务目标之间的关系&#xff1b; 机器学习&#xff1a; 监督学习&#xff1a;数据有标签&#x…...

Redis第一讲

目录 一、Redis01 1.1 NoSql 1.1.1 NoSql介绍 1.1.2 NoSql起源 1.1.3 NoSql的使用 1.2 常见NoSql数据库介绍 1.3 Redis简介 1.3.1 Redis介绍 1.3.2 Redis数据结构的多样性 1.3.3 Redis应用场景 1.4 Redis安装、配置以及使用 1.4.1 Redis安装的两种方式 1.4.2 Redi…...

Java面试题-消息队列

消息队列 1. 消息队列的使用场景 六字箴言&#xff1a;削峰、异步、解耦 削峰&#xff1a;接口请求在某个时间段内会出现峰值&#xff0c;服务器在达到峰值的情况下会奔溃&#xff1b;通过消息队列将请求进行分流、限流&#xff0c;确保服务器在正常环境下处理请求。异步&am…...

【第二十一章 SDIO接口(SDIO)】

第二十一章 SDIO接口 目录 第二十一章 SDIO接口(SDIO) 1 SDIO 主要功能 2 SDIO 总线拓扑 3 SDIO 功能描述 3.1 SDIO 适配器 3.2 SDIOAHB 接口 4 卡功能描述 4.1 卡识别模式 4.2 卡复位 4.3 操作电压范围确认 4.4 卡识别过程 4.5 写数据块 4.6 读数据块 4.7 数据流…...

人工智能(大型语言模型 LLMs)对不同学科的影响以及由此产生的新学习方式

今天是关于AI如何在教学中增强学生的学习体验&#xff0c;我把重要信息标红了。人文学科的价值被低估了 ⬇️ 转型与必要性 人工智能正在深刻地改变教育&#xff0c;这并非炒作&#xff0c;而是已经发生的巨大变革。教育机构和教育者不能忽视它&#xff0c;试图简单地禁止学生使…...

Git常用命令完全指南:从入门到精通

Git常用命令完全指南&#xff1a;从入门到精通 一、基础配置命令 1. 用户信息配置 # 设置全局用户名 git config --global user.name "你的名字"# 设置全局邮箱 git config --global user.email "你的邮箱example.com"# 查看所有配置 git config --list…...

关于uniapp展示PDF的解决方案

在 UniApp 的 H5 环境中使用 pdf-vue3 组件可以实现完整的 PDF 预览功能。以下是详细实现步骤和注意事项&#xff1a; 一、安装依赖 安装 pdf-vue3 和 PDF.js 核心库&#xff1a; npm install pdf-vue3 pdfjs-dist二、基本使用示例 <template><view class"con…...

MySQL:分区的基本使用

目录 一、什么是分区二、有什么作用三、分类四、创建分区五、删除分区 一、什么是分区 MySQL 分区&#xff08;Partitioning&#xff09;是一种将单张表的数据逻辑上拆分成多个物理部分的技术。这些物理部分&#xff08;分区&#xff09;可以独立存储、管理和优化&#xff0c;…...

QT开发技术【ffmpeg + QAudioOutput】音乐播放器

一、 介绍 使用ffmpeg 4.2.2 在数字化浪潮席卷全球的当下&#xff0c;音视频内容犹如璀璨繁星&#xff0c;点亮了人们的生活与工作。从短视频平台上令人捧腹的搞笑视频&#xff0c;到在线课堂中知识渊博的专家授课&#xff0c;再到影视平台上扣人心弦的高清大片&#xff0c;音…...

海云安高敏捷信创白盒SCAP入选《中国网络安全细分领域产品名录》

近日&#xff0c;嘶吼安全产业研究院发布《中国网络安全细分领域产品名录》&#xff0c;海云安高敏捷信创白盒&#xff08;SCAP&#xff09;成功入选软件供应链安全领域产品名录。 在数字化转型加速的今天&#xff0c;网络安全已成为企业生存与发展的核心基石&#xff0c;为了解…...

轻量级Docker管理工具Docker Switchboard

简介 什么是 Docker Switchboard &#xff1f; Docker Switchboard 是一个轻量级的 Web 应用程序&#xff0c;用于管理 Docker 容器。它提供了一个干净、用户友好的界面来启动、停止和监控主机上运行的容器&#xff0c;使其成为本地开发、家庭实验室或小型服务器设置的理想选择…...

如何通过git命令查看项目连接的仓库地址?

要通过 Git 命令查看项目连接的仓库地址&#xff0c;您可以使用以下几种方法&#xff1a; 1. 查看所有远程仓库地址 使用 git remote -v 命令&#xff0c;它会显示项目中配置的所有远程仓库及其对应的 URL&#xff1a; git remote -v输出示例&#xff1a; origin https://…...

【java面试】微服务篇

【java面试】微服务篇 一、总体框架二、Springcloud&#xff08;一&#xff09;Springcloud五大组件&#xff08;二&#xff09;服务注册和发现1、Eureka2、Nacos &#xff08;三&#xff09;负载均衡1、Ribbon负载均衡流程2、Ribbon负载均衡策略3、自定义负载均衡策略4、总结 …...