现在鸿蒙生态的用户越来越多,但对于大部分APP技术负责人的角度看,开发一个新的鸿蒙APP,难的不是如何从零开发一个新的APP,而是如何和已有的安卓、iOS端的APP做好资源协同,共享已有的代码资源,而不是长期维护三个团队来维护三端APP,一方面是运营成本高,另外协同效率也会变慢。

其实过去很多团队已经沉淀了不少小程序资源:运营活动、客户服务、员工工具、业务查询、预约办理、权益领取等功能,可能早已在微信、企业APP或其他移动入口中稳定运行。现在要进入鸿蒙生态,如果把这些功能全部重新用ArkTS开发一遍,研发周期、测试成本、后续多端维护压力都会上来。

今天分享一个思路是:基于原生代码搭建一个原生宿主APP,然后引入小程序容器来复用已有业务资产。
在这里插入图片描述

借助小程序容器,技术团队可以在鸿蒙APP中引入小程序运行时,让APP具备运行小程序的能力。这样一来,已有小程序可以作为业务模块直接接入,鸿蒙APP不必从零重写所有功能。宿主APP重点处理账号体系、入口导航、原生能力、安全边界和基础体验,小程序继续承载具体业务页面和流程。

而且,业务后续迭代也不必完全依赖鸿蒙APP发版。小程序的上架、下架、灰度发布和版本回滚,可以通过小程序管理平台完成。对于技术负责人来说,这意味着鸿蒙APP的建设不再是一次“重写所有业务”的工程,而可以变成一次“复用小程序资产、补齐宿主能力、建立发布治理体系”的架构升级。
在这里插入图片描述

接下来从技术实战角度,分享如何借助已有小程序资源,快速开发一个具备小程序运行能力的鸿蒙APP,并通过管理平台完成小程序上下架和灰度发布。

整体流程

一个鸿蒙APP拥有运行小程序的能力,大致可以拆成两条线。

第一条是宿主APP侧:

  1. 在小程序管理平台创建宿主应用,获取SDKKey和SDKSecret。
  2. 在鸿蒙工程中引入小程序容器SDK。
  3. 配置必要权限和AppletAbility。
  4. 在EntryAbility或页面中初始化小程序运行时客户端。
  5. 调用startApplet打开线上小程序。

第二条是小程序平台侧:

  1. 创建小程序并补充基础信息。
  2. 配置隐私信息和业务域名。
  3. 使用小程序开发者工具或兼容工具开发、调试、上传代码包。
  4. 提交审核,通过后发布为线上版本。
  5. 将小程序与宿主应用关联。
  6. 后续通过全量发布、比例发布或灰度计划控制版本生效范围。

一、准备凭据:先在平台创建宿主应用

接入前需要先在小程序管理平台里创建宿主应用。平台会为这个宿主应用生成对应的SDKKey和SDKSecret,后面初始化SDK时会用到。

这里有几个信息要提前确认:

  • 鸿蒙APP的BundleID/ApplicationID。
  • 小程序服务端地址,也就是apiServer。
  • SDKKey和SDKSecret。
  • 要打开的小程序AppID。
  • 这个小程序是否已经上架,并且是否已经关联到当前宿主应用。

在实际项目里,我建议把这些配置放在统一的配置文件或环境配置里,不要散落在页面代码里。尤其是SaaS环境、私有化测试环境、生产环境并存时,apiServer和SDKKey很容易配错。

二、引入SDK依赖

小程序容器鸿蒙SDK支持通过oh-package.json5依赖,也可以使用本地HAR包。线上依赖方式更适合快速验证。

首先,在工程根目录的build-profile.json5中,为app/products添加strictMode配置:

{
  "buildOption": {
    "strictMode": {
      "useNormalizedOHMUrl": true
    }
  }
}

下面示例保留接入代码中的包名、仓库地址和类型名称,实际项目以当前SDK版本文档为准。

然后在项目根目录创建.ohpmrc,配置小程序容器SDK的OHPM仓库:

registry=https://ohpm.openharmony.cn/ohpm/
@finclip:registry=https://ohpm.finogeeks.com/repos/ohpm

在oh-package.json5里增加依赖:

{
  "dependencies": {
    "@finclip/sdk": "latest"
  }
}

执行安装:

ohpm install

如果项目需要离线集成或版本锁定,也可以把对应的HAR包放到工程目录中,通过本地file方式引入。生产项目更建议锁定明确版本,避免构建环境因为latest变化而产生不可预期差异。
在这里插入图片描述

三、配置权限

最小可运行场景下,网络权限是必须的。如果小程序会调用定位、相机、麦克风、蓝牙、通讯录、日历、相册等能力,需要按实际业务补充对应权限。

在module.json5中可以这样配置:

{
  "module": {
    "requestPermissions": [
      {
        "name": "ohos.permission.INTERNET"
      },
      {
        "name": "ohos.permission.APPROXIMATELY_LOCATION",
        "usedScene": {
          "when": "inuse"
        }
      },
      {
        "name": "ohos.permission.LOCATION",
        "usedScene": {
          "when": "inuse"
        }
      },
      {
        "name": "ohos.permission.CAMERA"
      },
      {
        "name": "ohos.permission.MICROPHONE"
      }
    ]
  }
}

权限不建议一次性全开。小程序容器通常承载多个业务,权限边界应该跟业务能力对应起来:不使用地图就不要申请定位,不使用扫码或拍摄就不要申请相机。这样对上架审核、隐私说明和用户信任都更友好。

四、注册AppletAbility

如果使用Ability方式启动小程序,需要宿主APP注册一个Ability供小程序容器SDK运行小程序。这个方式比较适合让小程序拥有相对独立的窗口和生命周期。

在module.json5中增加Ability配置:

{
  "module": {
    "abilities": [
      {
        "name": "AppletAbility",
        "srcEntry": "./ets/appletAbility/AppletAbility.ets",
        "description": "$string:EntryAbility_desc",
        "icon": "$media:app_icon",
        "launchType": "specified",
        "label": "$string:EntryAbility_label",
        "startWindowIcon": "$media:app_icon",
        "startWindowBackground": "$color:start_window_background",
        "exported": true,
        "removeMissionAfterTerminate": true
      }
    ]
  }
}

这里重要的是launchType。示例中使用specified,可以支持同时打开多个小程序,并避免重复创建不必要的实例。removeMissionAfterTerminate设置为true后,小程序停止时不会继续留在任务列表里。

接着创建AppletAbility.ets:

import { FinAppClient } from '@finclip/sdk';
import UIAbility from '@ohos.app.ability.UIAbility';
import window from '@ohos.window';

export default class AppletAbility extends UIAbility {
  async onWindowStageCreate(windowStage: window.WindowStage) {
    const client = FinAppClient.getInstance();
    await client?.initContext(this.context, windowStage, this.launchWant.parameters);
  }
}

这段代码的作用,是在小程序Ability创建窗口时,把当前Ability上下文和windowStage交给小程序容器SDK,让小程序可以在这个Ability里完成页面渲染和生命周期管理。

如果项目使用Navigation方式启动小程序,则可以跳过注册AppletAbility这一步,把小程序放到宿主页面的NavPathStack里运行。两种方式都可以,实际选择要看宿主APP的架构和窗口管理要求。

五、初始化小程序运行时客户端

SDK初始化建议放在宿主应用入口Ability的onWindowStageCreate中,或者放在业务入口页初始化逻辑中。重点是只初始化一次,不要在多个页面反复初始化。

如果在页面组件中初始化,可以直接使用this.getUIContext()传入uiContext。下面是一个按Ability方式启动小程序的基础写法:

import common from '@ohos.app.ability.common';
import {
  EAppletStartMode,
  FinAppClient,
  IFinAppConfig,
  IFinAppStartMode
} from '@finclip/sdk';

@Entry
@Component
struct Index {
  private context = getContext(this) as common.UIAbilityContext;
  private client?: FinAppClient;

  initFinClip() {
    const finStoreConfigs: IFinAppConfig.IStoreConfig[] = [
      {
        apiServer: 'https://YOUR_API_SERVER',
        sdkKey: 'YOUR_SDK_KEY',
        sdkSecret: 'YOUR_SDK_SECRET',
        cryptType: 'sm'
      }
    ];

    const finAppConfig: IFinAppConfig.IFinAppConfig = {
      finStoreConfigs,
      currentUserId: 'user_10001',
      appletDebugMode: 'default',
      logLevel: 'info',
      uiConfig: {
        hideShareAppletMenu: true,
        hideFavoriteMenu: true,
        appletText: '小程序'
      }
    };

    const startMode: IFinAppStartMode = {
      startMode: EAppletStartMode.Ability,
      uiContext: this.getUIContext()
    };

    this.client = FinAppClient.init(
      finAppConfig,
      this.context,
      'AppletAbility',
      startMode
    );
  }

  build() {
    Button('初始化FinClip')
      .onClick(() => {
        this.initFinClip();
      });
  }
}

其中:

  • finStoreConfigs可以配置多个服务器,用于同时打开不同环境的小程序。
  • apiServer、SDKKey、SDKSecret需要和平台中创建的宿主应用一致。
  • apmServer是可选项,如果有独立的数据上报服务器再配置;不配置时通常使用apiServer。
  • currentUserId建议传入宿主APP的当前登录用户标识,后续做灰度、收藏、日志、行为分析时会更清楚。
  • appletDebugMode开发阶段可以临时设为enable,方便调试;提测或上线前建议恢复default。
  • uiConfig可以控制小程序右上角胶囊、更多菜单、分享、收藏等交互。

六、在鸿蒙APP里打开一个小程序

当SDK初始化完成、小程序已上架且已关联宿主应用后,就可以通过startApplet打开小程序。

基础打开方式:

import { FinAppClient } from '@finclip/sdk';

async function openApplet() {
  const client = FinAppClient.getInstance();

  await client?.startApplet({
    appId: 'YOUR_APPLET_ID',
    apiServer: 'https://YOUR_API_SERVER'
  });
}

如果需要打开指定页面,并带上业务参数,可以使用startParams:

async function openOrderApplet(orderId: string) {
  const client = FinAppClient.getInstance();

  await client?.startApplet({
    appId: 'YOUR_APPLET_ID',
    apiServer: 'https://YOUR_API_SERVER',
    startParams: {
      path: '/pages/order/detail',
      query: `orderId=${encodeURIComponent(orderId)}&source=harmony`
    },
    forceUpdate: true
  });
}

这里的forceUpdate可以用于需要强制打开最新版本的场景。普通业务不一定要每次都设置,具体取决于小程序版本策略和启动性能要求。

如果是通过平台二维码打开体验版、审核版、开发版或预览版,可以使用二维码方式:

async function openAppletByQrCode(qrCodeContent: string) {
  const client = FinAppClient.getInstance();

  await client?.startAppletByQrCode({
    qrCode: qrCodeContent
  });
}

这里示例使用qrCode字段。不同SDK版本的示例文档中可能会出现大小写不同的写法,实际接入时建议以当前安装SDK的类型定义和IDE提示为准。

这个能力很适合测试阶段。运营或测试人员在平台生成二维码后,鸿蒙APP扫码即可打开对应版本,避免每次都等正式上架。

七、给灰度发布补充宿主侧参数

灰度发布通常会在平台侧配置规则,但规则能否细分,取决于宿主APP能提供哪些匹配信息。

比如我们希望某个小程序版本只对指定用户、指定城市、指定渠道或指定组织开放,就需要在SDK获取小程序信息时,把这些扩展参数带给服务端。小程序运行时通常会提供类似GrayReleaseHandler的扩展点,用来补充灰度发布数据。

示例:

import {
  FinAppProxyHandlerManager,
  IFinProxyHandlerItem
} from '@finclip/sdk';

class AppGrayReleaseHandler extends IFinProxyHandlerItem.GrayReleaseHandler {
  getGrayExtension(appId: string, apiServer: string): Record<string, string> {
    return {
      userId: 'user_10001',
      city: 'shenzhen',
      channel: 'harmony',
      orgCode: 'branch_001'
    };
  }
}

export function registerGrayReleaseHandler() {
  FinAppProxyHandlerManager.grayReleaseHandler = new AppGrayReleaseHandler();
}

实际项目里,这些字段应该来自宿主APP的登录态、组织信息、渠道包配置或设备环境,而不是写死在代码里。这个Handler建议在SDK初始化前后、打开小程序之前完成注册。平台侧配置灰度计划时,可以围绕这些字段做匹配。这样同一个鸿蒙APP内,不同用户可能命中不同的小程序版本。

八、小程序管理平台如何完成上下架

SDK接入解决的是“APP能不能跑小程序”,然后就需要考虑如何进行上下架:
在这里插入图片描述

  1. 在平台创建小程序,补充名称、分类、图标、简介、详细描述等基础信息。
  2. 配置小程序隐私保护指引,说明小程序如何处理用户信息、是否集成第三方插件或SDK。
  3. 配置小程序业务域名。网络请求、上传、下载、Socket等域名需要按平台要求提前配置。
  4. 使用小程序开发者工具或兼容的小程序开发工具完成开发、调试、上传代码包。
  5. 在小程序详情的版本管理中选择代码包,提交为审核版本。
  6. 审核通过后,将审核版本发布为线上版本;如果提交审核时勾选了“审核通过后自动上架”,也可以由平台自动发布。
  7. 在应用管理中创建宿主应用,并把已上架小程序关联到该宿主应用。

小程序上架后,还需要与宿主应用关联。平台会校验SDKKey、BundleID和小程序关联关系。如果小程序没有和当前宿主APP关联,代码里的startApplet参数即使正确,也可能无法正常打开。

九、发布方式:全量、比例、灰度计划

小程序审核通过后,平台通常会提供几种发布方式。

全量发布适合低风险版本,比如文案修复、非核心页面优化、已充分测试的稳定版本。发布后,全体用户都可以打开最新线上版本。

比例发布适合希望先观察一段时间的版本。平台会按发布比例推送给随机用户,并提供观察窗口。这样团队可以先看启动成功率、错误日志、用户反馈,再决定是否扩大范围。

灰度发布计划更适合有明确目标人群的场景。比如:

  • 只给内部员工开放新版工作台。
  • 只给某个城市试点本地服务。
  • 只给某个渠道的鸿蒙用户开放新功能。
  • 只给白名单客户体验新的业务流程。

如果宿主侧已经通过GrayReleaseHandler补充了userId、city、channel、orgCode等字段,平台侧灰度规则就能更精细。这样业务团队不用重新打包鸿蒙APP,就可以控制小程序版本在不同人群里的生效范围。

鸿蒙APP引入小程序容器,最直观的变化是“APP可以打开小程序”。但从工程实践看,它带来的价值更接近一种业务架构调整:把一部分原本依赖APP发版的功能,变成可以独立开发、审核、上架、灰度和下架的小程序。

宿主APP仍然负责稳定的入口、账号、权限和原生能力;小程序负责更灵活的业务模块;小程序管理平台负责版本、审核、关联和灰度。

当这套链路跑通后,鸿蒙APP就不只是多了一个容器,而是拥有了一种更轻量的业务发布方式。后续无论是运营活动、内部工具、客户服务,还是第三方生态接入,都可以在不频繁改动宿主APP的前提下,更快地完成上线和迭代。


感兴趣的话可以在Gitee上详细了解:Gitee 代码地址

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐