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

news2024/10/3 6:35:52

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 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 里使用


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

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相关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 中引入插件。

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

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

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

class 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")
                }
            }
        }
    }
	
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/347717.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

思科三层交换机配置

三层交换机是什么为什么会有三层交换机三层交换机的工作原理三层交换机的应用1.三层交换机是什么 三层交换机就是具有部分路由器功能的交换机&#xff0c;工作在OSI网络标准模型的第三层&#xff1a;网络层。三层交换机的最重要目的是加快大型局域网内部的数据交换&#xff0c;…

操作系统-处理机调度

1.处理机调度的概念、层次1.1调度的基本概念制定某种规则来决定处理任务的顺序。1.2调度的三个层次高级调度&#xff08;作业调度&#xff09;中级调度&#xff08;内存调度&#xff09;进程的挂起态与七状态模型低级调度&#xff08;进程调度&#xff09;小结2.进程调度的时机…

VMware安装Linux虚拟机后忘记root密码处理方法

OS版本&#xff1a;Red Hat 7.7 问题说明&#xff1a; 之前用VMWare安装了一台Linux虚机&#xff0c;由于长期没使用&#xff0c;导致忘记了root密码。所以需要修改root密码。 Root密码修改 现将修改root密码的操作步骤记录如下。 1.启动虚拟机&#xff0c;出现启动倒计时…

2023年,智能家居老板如何低成本多开10家店?

作者 | 启明 编辑 | 小沐 出品 | 智哪儿 zhinaer.cn2023年智能家居老板需要一些什么样的新思维来助力业绩增长呢&#xff1f;智哪儿从今天起推出系列观察文章与各位老板共勉。在《三体》电视剧中&#xff0c;三体人在乱纪元通过“脱水”来延续生命&#xff0c;在恒纪元则通过“…

mac 安装、配置、卸载mysql

文章目录mac 安装、配置、卸载mysql安装下载mysql dmg包安装mysql服务启动mysql服务配置系统环境变量配置修改默认密码配置运行远程连接配置my.cnf文件卸载mac 安装、配置、卸载mysql 安装 下载mysql dmg包 下载地址&#xff1a;https://downloads.mysql.com/archives/commu…

面试必问:进程和线程的区别(从操作系统层次理解)

1.什么是进程&#xff1f;为什么要有进程&#xff1f; 进程有一个相当精简的解释&#xff1a;进程是对操作系统上正在运行程序的一个抽象。 这个概念确实挺抽象&#xff0c;仔细想想却也挺精准。 我们平常使用计算机&#xff0c;都会在同一时间做许多事&#xff0c;比如边看…

springboot(6)之前端传递参数的方式 普通 集合 数组

实体类传递 首先我们在后端定义一个实体类&#xff0c;通过lombok插件重写 有参 无参 get set toString 方法, 然后前端发送数据&#xff0c;后端就会自动收到&#xff0c;然后属性填写 后端代码如下 AllArgsConstructor Data NoArgsConstructor public class role …

【Pygame实战】有趣又益智的游戏:看图猜四字成语,最后一个几乎没人会,哪位学霸来猜猜?这么难的嘛?(猜个锤子)

导语 看图猜成语&#xff0c;是考验一个人的反应能力&#xff0c;也考验一个人的右脑思维。 据说越聪明的人&#xff0c;这道题的完成率越高。你想试一试嘛&#xff1f;今天就给你这次机会啦&#xff01; 所有文章完整的素材源码都在&#x1f447;&#x1f447; 粉丝白嫖源码…

RocketMQ5.0.0消息存储<四>_刷盘机制

目录 一、刷盘概览 二、Broker刷盘机制 1. 同步刷盘 2. 异步刷盘 1)&#xff1a;未开启堆外内存池 2)&#xff1a;开启堆外内存池 三、参考资料 一、刷盘概览 RocketMQ存储与读写是基于JDK NIO的内存映射机制&#xff08;MappedByteBuffer&#xff09;&#xff0c;消息存储…

深度解析 JavaScript 严格模式:利弊长远的考量

前言 ECMAScript 5首次引入严格模式的概念。严格模式用于选择以更严格的条件检查JavaScript代码错误&#xff0c;可以应用到全局&#xff0c;也可以应用到函数内部。 严格模式的好处是可以提早发现错误&#xff0c;因此可以捕获某些 ECMAScript 问题导致的编程错误。 理解严格…

前端vue实现系统拦截跳转外链并进入跳转询问界面

跳转询问界面如下图所示&#xff1a; 给自己挖坑的实现方式&#xff0c;最终解决方案请看最底下 思路&#xff1a;正常情况下我们有2种方式跳转外链 第一种非a标签&#xff0c;我们手动添加事件进行跳转 <div class"dingdan public-padding p-item" click&quo…

Python 条件语句是什么?

Python条件语句是通过一条或多条语句的执行结果&#xff08;True或者False&#xff09;来决定执行的代码块。 可以通过下图来简单了解条件语句的执行过程: Python程序语言指定任何非0和非空&#xff08;null&#xff09;值为true&#xff0c;0 或者 null为false。 Python 编程…

家政服务小程序实战教程12-详情页

我们的家政服务小程序已经完成了首页和分类展示页面的开发&#xff0c;接下来就需要开发详情页了。在详情页里我们展示我们的各项服务内容&#xff0c;让用户可以了解每项家政服务可以提供的内容。 低码开发不像传统开发&#xff0c;如果开发详情页需要考虑每个字段的类型&…

【TPC证书报错--箱码校验失败】

证书管理—>交易证书管理—>编辑&#xff0c;然后就报错了。 1.这个报错&#xff0c;一般是指一个箱码&#xff0c;【产出/报工】接口失败了&#xff0c;但是【成品入库】和【成品出口】成功了。 2.大概就是【成品出库】接口&#xff0c;会传【销售单号】和【箱码】2个…

ArcGIS与地理加权回归【三】

开 工 大 急 原址链接&#xff1a; ArcGIS与地理加权回归【三】https://mp.weixin.qq.com/s/x85EXKImSHio1IZovW9qdA 接着5个月之前.......ArcGIS与地理加权回归GWR【二】以及MGWR软件下载 在ASU下载了样例“关于影响佐治亚州受教育水平”的数据。在上一篇已简单介绍…

类和对象(下)(一)

类和对象&#xff08;下&#xff09;&#xff08;一&#xff09;1.再谈构造函数1.1构造函数体赋值1.2初始化列表1.3explicit关键字2.static成员2.1概念2.2特性3.匿名对象&#x1f31f;&#x1f31f;hello&#xff0c;各位读者大大们你们好呀&#x1f31f;&#x1f31f; &#x…

Spring框架源码(五) @configuration源码深度解析

Configuration 注解是spring-context模块提供的一个给开发者使用的配置类注解&#xff0c;开发者可以通过Configuration注解来定义配置类&#xff0c;也可以使用xml形式注入。 例如配置数据库配置&#xff0c;定义一个配置类&#xff0c;注入数据源DataSource, 事务管理器Trans…

类和对象(下)(二)

类和对象&#xff08;下&#xff09;&#xff08;二&#xff09;1.友元1.1友元函数1.2友元类2.内部类3.拷贝对象时的一些编译器优化&#xff08;vs2022&#xff09;&#x1f31f;&#x1f31f;hello&#xff0c;各位读者大大们你们好呀&#x1f31f;&#x1f31f; &#x1f680…

【c#】反射学习笔记01

c#反射学习01反射学习一、反射原理二、那么我们是如何通过metadata来实现反射呢&#xff1f;三、反射的好处四、反射创建对象&#xff08;利用配置文件简单工厂反射&#xff09;五、反射的黑科技&#xff08;多构造函数调用、破坏单例、创建泛型&#xff09;六、反射调用实例方…

环境配置完整指导——Installing C++ Distributions of PyTorch

目录一、前言二、动手开始做1. 安装cuda 11.42. 安装visual studio 2019 community3. 安装libtorch4. 安装mingw-w645. 配置环境变量6. 打开vscode开始写程序7. 运行程序8. 其他报错信息文章简介&#xff1a;这篇文章用于介绍在windows10 vscode中&#xff0c;跑通如下代码的全…