前言

随着鸿蒙生态的愈渐成熟,越来越多的应用厂商会考虑hos和ohos双平台的兼容适配及商业化落地。

虽然hos和ohos基于同一套开源底座,但是也有着其天然的隔离性(hms、定制api等等)。因此,基于同一套应用代码,如何使用最小的工作量以及尽可能不使用重复代码并且能快速编译构建双平台hap就是许多开发者阻塞的一个问题。

下文会从多个维度来讲述如何进行最佳的双平台适配。

开发环境

hos设备: HUAWEI Mate 60 Pro

hos系统: 5.0.0.103(SP32DEVC00E103R4P5log)

ohos设备: DAYU200

ohos系统: OpenHarmony 5.1.0.59

IDE: DevEco Studio 5.1.0 Beta1(Build Version: 5.1.0.420)

OpenHarmony SDK: ohos-sdk-full(5.1.0.60) (api18)

适配方案

架构设计

由于hos作为定制化闭源系统,因此有其独有api和能力。在应用开发时,从产品设计和代码架构设计就需要进行分层和隔离。

鸿蒙提供了HSP来支持hos独有功能的按需加载。比如在应用的登录页面,期望支持华为一键登录功能,这个功能只有hos上提供。因此,可以在开发时,将该功能封装到仅包含hos独有功能的HSP内提供api给主hap调用,主hap调用时通过动态加载引入,并且判断api是否可用或者实例是否存在来增加健壮性。

开发适配

新建工程并新增hos独有hsp

新建应用工程,右键工程根目录,依次点击New -> Module -> Shared Library -> Next,在Module name里输入HosHSP,点击Finish。

配置双平台product

由于单hap无法同时安装运行到双平台上,因此需要基于鸿蒙的多目标构建产物来构建双hap。

在工程根目录的build-profile.json5里进行如下配置:

{
    "app": {
        ···
        "products": [
            {
                "name": "default",// 因为一个应用工程必须有一个name为default的product,因此这里用default表示hos
                "compatibleSdkVersion": "5.1.0(18)",
                "runtimeOS": "HarmonyOS",
            },
            {
                "name": "ohos",
                "compatibleSdkVersion": 18,
                "compileSdkVersion": 18,
                "runtimeOS": "OpenHarmony",
            }
        ]
        ···
    },
    "modules": [
        {
            "name": "entry",
            "srcPath": "./entry",
            "targets": [
                {
                    "name": "default",
                    "applyToProducts": [
                        "default",
                        "ohos"
                    ]
                }
            ]
        },
        {
            "name": "HosHSP",
            "srcPath": "./HosHSP",
            "targets": [
                {
                    "name": "default",
                    "applyToProducts": [
                        "default",
                        "ohos"
                    ]
                }
            ]
        }
    ]
}

在上述配置里,entry模块为双平台共有能力,HosHSP为hos独有能力。

依次点击菜单栏Build -> Build Hap(s)/App(s) -> Build Hap(s),待构建成功后可以看到entry\build\default\outputs\default路径下已经生成了hos对应的hap。

切换product为ohos,按照上述操作构建成功后可以看到entry\build\ohos\outputs\default路径下已经生成了ohos对应的hap。

签名

开发过程中,许多开发者会使用自动签名去进行应用签名。但是在应用发布时,由于hos和各个ohos发行版内置的签名文件不同,并且签名文件下发的方式也不同,因此需要配置两套签名。

在工程根目录的build-profile.json5里进行如下配置:

{
    "app": {
        "signingConfigs": [
            {
                "name": "hos",
                "type": "HarmonyOS",
                "material": {
                    "certpath": "xxx_hos.cer",
                    "keyAlias": "xxx",
                    "keyPassword": "xxx",
                    "profile": "xxx_hos.p7b",
                    "signAlg": "xxx",
                    "storeFile": "xxx_hos.p12",
                    "storePassword": "xxx"
                }
            },
            {
                "name": "ohos",
                "type": "OpenHarmony",
                "material": {
                    "certpath": "xxx_ohos.cer",
                    "keyAlias": "xxx",
                    "keyPassword": "xxx",
                    "profile": "xxx_ohos.p7b",
                    "signAlg": "xxx",
                    "storeFile": "xxx_ohos.p12",
                    "storePassword": "xxx"
                }
            }
        ],
        "products": [
            {
                "name": "default",
                "signingConfig": "hos",
                ···
            },
            {
                "name": "ohos",
                "signingConfig": "ohos",
                ···
            }
        ]
    }
}

HSP依赖

entry模块的oh-package.json5里进行如下配置:

{
    ···
    {
        "dependencies": {
            "hoshsp": "file:../HosHSP"
        }
    }
}

sync后成功安装HosHSP依赖。

HSP调用hms独有api

HosHSP\src\main\ets\utils\Calc.ets下新增代码

import { rcp } from '@kit.RemoteCommunicationKit';

export function httpRequest() {
  console.info(`httpRequest enter`)
  let session = rcp.createSession();
  console.info(`httpRequest createSession end`)
}

并将HosHSP\Index.ets里的代码修改为

export { httpRequest } from './src/main/ets/utils/Calc';

ohos产品下IDE预编译会报错

Cannot find module '@kit.RemoteCommunicationKit' or its corresponding type declarations. <ArkTSCheck>

以及

Use explicit types instead of "any", "unknown" (arkts-no-any-unknown) <ArkTSCheck>

这是因为在OpenHarmonySDK中无法找到独属于hms的api,因此为了IDE能校验通过并且能正常编译,需要将相关的声明文件从IDE自带的HarmonyOSSdK里复制到OpenHarmonySDK里。

复制SDK声明文件

将IDE的sdk\default\hms\ets\kits\@kit.RemoteCommunicationKit.d.ts文件复制到OpenHarmonySDK的18\ets\kits路径下。

将IDE的sdk\default\hms\ets\api\@hms.collaboration.rcp.d.ts文件和sdk\default\hms\ets\api\@hms.collaboration.urpc.d.ts文件复制到OpenHarmonySDK的18\ets\api路径下。

将IDE的sdk\default\hms\ets\build-tools\ets-loader\kit_configs\@kit.RemoteCommunicationKit.json文件复制到OpenHarmonySDK的18\ets\build-tools\ets-loader\kit_configs路径下。

依次点击菜单栏File -> Sync and Refresh Project,待成功sync后可以看到IDE不会再报错。

主hap调用HSP

entry\src\main\ets\pages\Index.ets里新增代码

import { httpRequest } from 'hoshsp'

···
struct Index {
    ···
    aboutToAppear(): void {
        httpRequest()
    }
}

default产品运行在hos设备上,待应用启动后能正常打印日志

httpRequest enter
httpRequest createSession end

ohos产品运行在ohos设备上,待应用启动后会直接闪退,并生成jscrash文件。关键堆栈如下:

Reason:ReferenceError
Error name:ReferenceError
Error message:Cannot find module '@hms:collaboration.rcp' imported from '&hoshsp/src/main/ets/utils/Calc&'.
Stacktrace:
Cannot get SourceMap info, dump raw stack:
#01 pc 00000000005efddd /system/lib/platformsdk/libark_jsruntime.so(1f14b2924add8117bba6c127485f7916)

上述报错因为在ohos设备上是没有hms独有api的实现导致,因此我们需要动态加载hsp。

动态加载hsp

entry模块的oh-package.json5里的配置修改为

{
    ···
    {
        "dynamicDependencies": {
            "hoshsp": "file:../HosHSP"
        }
    }
}

HosHSP\src\main\ets\utils\Calc.ets里的代码修改为

import { rcp } from '@kit.RemoteCommunicationKit';
import { BusinessError } from '@kit.BasicServicesKit';

export function httpRequest() {
  try {
    let session: rcp.Session = rcp.createSession();
    session.get("https://www.baidu.com").then((response) => {
      console.info(`httpRequest success, response: ${response}`);
    }).catch((err: BusinessError) => {
      console.info(`httpRequest failed, err: ${err}`);
    });
  }catch (error) {
    console.info(`httpRequest failed, error: ${error}`);
  }
}

entry\src\main\ets\pages\Index.ets里的代码修改为

import { BusinessError } from '@kit.BasicServicesKit';

···
struct Index {
    ···
    aboutToAppear(): void {
        import('hoshsp').then((ns: ESObject) => {
            ns.httpRequest()
        }).catch((error: BusinessError) => {
            console.info(`aboutToAppear import, error: ${error}`);
        })
    }
}

default产品运行到hos设备上,待应用启动后可以看到如下日志

httpRequest success, response: <!DOCTYPE html>
···

表示应用能正确通过rcp的api访问网页。

ohos产品运行在ohos设备上,待应用启动后可以看到如下日志

aboutToAppear import, error: ReferenceError: Cannot find module '@hms:collaboration.rcp' imported from '&hoshsp/src/main/ets/utils/Calc&'.

表示系统已识别到hoshsp无法正常加载,主动进行了拦截。

FAQ

SDK声明文件该如何复制

参考复制SDK声明文件,如果遇到需要调用的hms或者定制化api,可以根据IDE报错信息把声明文件复制到OpenHarmonySDK的对应路径下。

参考文献

https://blog.csdn.net/weixin_40307746/article/details/145624523?spm=1001.2014.3001.5501

https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/ide-oh-package-json5

https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-dynamic-import

Logo

社区规范:仅讨论OpenHarmony相关问题。

更多推荐