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

      {
        "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改为:

      {
        "name": "Business",
        "runtimeOS": "OpenHarmony",
        "compileSdkVersion": 11,
        "compatibleSdkVersion": 11
      }

多目标

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

    {
      "name": "free",
    },
    {
      "name": "pay"
    }

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

        {
          "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)

更多实践

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

    {
      "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组件代码示例如下:

@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中添加该组件,引用包如下:

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

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

product_target_screen4.png

hvigorfile脚本实践

通过脚本移除三方库依赖

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

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节点下添加

  "output": {
        "artifactName": "freehap"
      }

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

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()方法便可以自定义获取时间

通过脚本动态修改权限

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后添加如下配置:

      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为指定的签名配置名称:

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);
  }
}

通过脚本动态修改包名

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

参考资料

点赞

发表评论

邮箱地址不会被公开。 必填项已用*标注