文章目录
  1. 1. 前言
  2. 2. 概述
  3. 3. 基本概念
  4. 4. 应用场景
  5. 5. 构建原理图
  6. 6. 使用
    1. 6.1. 多产物
    2. 6.2. 多目标
    3. 6.3. 更多实践
      1. 6.3.1. 自定义代码资源目录及自定义参数
    4. 6.4. hvigorfile脚本实践
      1. 6.4.1. 通过脚本移除三方库依赖
      2. 6.4.2. 通过脚本重命名hap包名称
      3. 6.4.3. 通过脚本动态修改权限
      4. 6.4.4. 通过脚本动态修改hap包桌面显示名称,图标
      5. 6.4.5. 通过脚本动态配置签名
      6. 6.4.6. 通过脚本动态修改包名
  7. 7. 参考资料

原文链接:Harmony多目标产物方案

前言

Android多渠道打包简单介绍

概述

多目标产物在HarmonyOS系统中的应用主要体现在软件开发与分发方面,特别是针对不同用户群体、不同业务场景的需求进行定制化开发。多目标产物为开发者提供了更加灵活和高效的开发方式,使得应用能够更好地适应市场需求和变化。通过定制化开发,还可以更好地满足用户的个性化需求,提升用户体验。

基本概念

target:对应HAR、HSP、HAP的多目标产物。工程内的每一个模块可以定义多个target,每个Target对应一个定制的HAP、HAR包,通过配置可以实现一个模块构建出不同的HAP、HAR包。
product:对应App的多目标产物。一个HarmonyOS工程的构建产物为App包,一个工程可以定义多个product,每个product对应一个定制化应用包,通过配置可以实现一个工程构建出多个不同的应用包。

应用场景

主要应用场景:

不同用户群体:针对不同的用户群体(如国内用户与国际用户等),HarmonyOS系统支持构建不同的应用版本。这些版本在功能、界面、语言等方面可能有所不同,以满足不同用户群体的需求。
不同业务场景:在不同的业务场景中,同一个应用可能需要提供不同的功能或资源。例如,一个在线教育应用可能需要为学生提供学习资料,而为教师提供教学资料。HarmonyOS系统支持通过配置不同的Target来实现这种差异化定制。

  1. Community社区版本,免费,向个人开发者用户提供该应用绝大部分基础功能,但是不提供部分定制化限定功能及技术支持。
  2. Ultimate终极版本,收费,向个人、政企等开发者用户提供该应用全部基础功能,同时提供定制化限定功能及技术支持。

构建原理图

构建原理图

使用

多产物

如在我们的工程下的build-profile.json5下添加两个product

1
2
3
4
5
6
7
8
{
"name": "Community",
"compatibleSdkVersion": "5.0.0(12)"
},
{
"name": "Business",
"compatibleSdkVersion": "5.0.0(12)",
}

Community代表社区版、Business代表商业版。可以看到Product下多了CommunityBusiness两个,当切到CommunityModule Target提示No target to apply是什么原因呢?后面会介绍。
product_target_screen1.png

那么假如现在有个需求,当前社区版我们想跑在HarmonyOS的机器上,但是商用还是OpenHarmony上,需要出不同的包怎么实现呢?
如果当我们没有了解多产物目标方案时,最传统的可能就是注释default下的环境替换了,或者通过hvigorfile脚本编译替换,
而当了解了多产物则比较好实现,我们在不同的product中可以配置不同的编译环境,如Business改为:

1
2
3
4
5
6
{
"name": "Business",
"runtimeOS": "OpenHarmony",
"compileSdkVersion": 11,
"compatibleSdkVersion": 11
}

多目标

接下来我们在模块下的build-profile.json5中定义两个target,如:

1
2
3
4
5
6
{
"name": "free",
},
{
"name": "pay"
}

把target指定给product,工程下的build-profile.json5中当前module节点下的targets增加如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{
"name": "free",
"applyToProducts": [
"default",
"Community"
]
},
{
"name": "pay",
"applyToProducts": [
"default",
"Business"
]
}

此时再点击DevEco Studio中的Product,可以看到有target了:
!(https://upload-images.jianshu.io/upload_images/1834726-df477d6bea792841.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

更多实践

自定义代码资源目录及自定义参数

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
{
"name": "free",
"runtimeOS": "HarmonyOS",
"config": {
"buildOption": {
"arkOptions": {
"buildProfileFields": {
"buildData": "free"
}
}
}
},
"source": {
"sourceRoots": [
"./src/free"
]
},
"resource": {
"directories": [
"./src/main/resources",
"./src/main/resource_free"
]
},
"output": {
"artifactName": "free"
}
},
{
"name": "pay",
"runtimeOS": "OpenHarmony",
"config": {
"buildOption": {
"arkOptions": {
"buildProfileFields": {
"buildData": "pay"
}
}
}
},
"source": {
"sourceRoots": [
"./src/pay"
]
},
"resource": {
"directories": [
"./src/main/resources",
"./src/main/resource_pay"
]
},
"output": {
"artifactName": "pay"
}
}

main同级目录中创建freepay两个文件夹,在mainets下创建component文件夹,其中再创建DemoComponent组件,
resource同级目录创建reource_freeresource_pay两个资源目录,分别放入不同图片命名为product_target_screen,
DemoComponent组件代码示例如下:

1
2
3
4
5
6
7
8
9
@Component
export struct DemoComponent {
build() {
Text('The custom build data is:' + BuildProfile.buildData)
.backgroundImage($r('app.media.product_target_screen'))
.width('100%')
.height('100%')
}
}

然后再在默认的Index.ets中添加该组件,引用包如下:

1
import { DemoComponent } from 'demo/ets/component/DemoComponent';

此时切换不同的target查看Previewer,可以看到文本和图片显示不一样了:
product_target_screen3.png

product_target_screen4.png

hvigorfile脚本实践

通过脚本移除三方库依赖

假如我们想社区版带开源库,商业版不集成三方库,这时我们可以通过hvigor脚本在编译时移除商业版的依赖。
如模块下的hvigorfile.ts文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import { hapTasks, OhosHapContext, OhosPluginId } from '@ohos/hvigor-ohos-plugin';
import { getNode } from '@ohos/hvigor'

const entryNode = getNode(__filename);
// 为此节点添加一个afterNodeEvaluate hook 在hook中修改module.json5的内容并使能
entryNode.afterNodeEvaluate(node => {
// 获取此节点使用插件的上下文对象 此时为hap插件 获取hap插件上下文对象
const hapContext = node.getContext(OhosPluginId.OHOS_HAP_PLUGIN) as OhosHapContext;
hapContext.targets((target: Target)=> {
console.log(target.getCurrentProduct().getProductName())
if (target.getCurrentProduct().getProductName() == 'Business') {
const depOpt = hapContext.getDependenciesOpt()
delete depOpt.utils
hapContext.setDependenciesOpt(depOpt)
}
})
})

export default {
system: hapTasks, /* Built-in plugin of Hvigor. It cannot be modified. */
plugins:[] /* Custom plugin to extend the functionality of Hvigor. */
}

通过脚本重命名hap包名称

如果我们想要定制hap包名称,可以在模块下的build-profile.json5文件进行修改,对应的target节点下添加

1
2
3
"output": {
"artifactName": "freehap"
}

但是假如我们想打出来的包不止名称不一样,还想加时间后缀呢,json配置文件中无法动态获取时间,这时我们就可以在模块下的hvigorfile.ts中添加如下方法:

1
2
3
4
5
6
7
8
9
10
11
function renameHapName(hapContext: OhosHapContext) {
const buildProfile = hapContext.getBuildProfileOpt();
const targets = buildProfile.targets
for (const target of targets) {
console.log('target:' + target);
target['output']={
"artifactName": 'demo_' + target.name + '_'+ getTime()
}
}
hapContext.setBuildProfileOpt(buildProfile);
}

读取配置文件,获取output节点,对其中对artifactName字段自定义赋值,getTime()方法便可以自定义获取时间

通过脚本动态修改权限

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function changePermissions(hapContext: OhosHapContext) {
hapContext.targets((target: Target)=> {
const targetName = target.getTargetName();
console.log(targetName);
if('free' == targetName){
// 通过上下文对象获取从module.json5文件中读出来的obj对象
const moduleJsonOpt = hapContext.getModuleJsonOpt();
moduleJsonOpt['module']['requestPermissions'] = [
{
"name": "ohos.permission.INTERNET"
}
]
hapContext.setModuleJsonOpt(moduleJsonOpt)
}
})
}

通过脚本动态修改hap包桌面显示名称,图标

直接通过脚本修改app.json5中不生效,实际需要修改EntryAbility中的属性,先在string.json中添加EntryAbility_label_free字段,
然后再在上面动态修改权限遍历target判断targetName后添加如下配置:

1
2
3
4
5
let abilities = moduleJsonOpt['module']['abilities']
for (let abilitiesElement of abilities) {
abilitiesElement['label'] = "$string:EntryAbility_label_free"
abilitiesElement['icon'] = "$media:icon_free"
}

通过脚本动态配置签名

在工程的build-profile.json5文件中,我们可以在signingConfigs节点下添加其他不同的签名配置,然后在product节点下配置对应的签名配置.
但是如果我们想在同一个product下针对不同的target生成不同签名的hap包呢,这种方式就可以通过脚本动态配置了,我们判断在编译当前target的时候,
修改product中的signingConfig为指定的签名配置名称:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function reSign(appContext: OhosAppContext, signName) {
// 获取外部参数
const exitParams = hvigor.getParameter().getExtParams();
const module = exitParams['module'];
if (!module) return
if (module.includes('vip') ) {
const buildProfileOpt = appContext.getBuildProfileOpt();
const products = buildProfileOpt.app.products
for (const product of products) {
product['signingConfig']= signName
}
appContext.setBuildProfileOpt(buildProfileOpt);
}
}

通过脚本动态修改包名

1
2
3
4
5
function changePackageName(appContext: OhosAppContext, packageName) {
const appJson5: AppJson.AppOptObj = appContext.getAppJsonOpt();
appJson5.app.bundleName = packageName
appContext.setAppJsonOpt(appJson5);
}

参考资料

文章目录
  1. 1. 前言
  2. 2. 概述
  3. 3. 基本概念
  4. 4. 应用场景
  5. 5. 构建原理图
  6. 6. 使用
    1. 6.1. 多产物
    2. 6.2. 多目标
    3. 6.3. 更多实践
      1. 6.3.1. 自定义代码资源目录及自定义参数
    4. 6.4. hvigorfile脚本实践
      1. 6.4.1. 通过脚本移除三方库依赖
      2. 6.4.2. 通过脚本重命名hap包名称
      3. 6.4.3. 通过脚本动态修改权限
      4. 6.4.4. 通过脚本动态修改hap包桌面显示名称,图标
      5. 6.4.5. 通过脚本动态配置签名
      6. 6.4.6. 通过脚本动态修改包名
  7. 7. 参考资料