# ohos_cordova **Repository Path**: closeJs/ohos_cordova ## Basic Information - **Project Name**: ohos_cordova - **Description**: 开发和维护跨平台适配能力,以插件的形式为Web提供原生接口,让Web应用适配OpenHarmony的能力。 - **Primary Language**: Unknown - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: https://gitee.com/openharmony-sig/ohos_cordova - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 23 - **Created**: 2025-05-19 - **Last Updated**: 2025-05-19 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # cordova-harmony ## 介绍 Cordova Harmony是以[Apache Cordova Android](https://github.com/apache/cordova-android)项目为基础,翻译并对等在Harmony系统上实现Cordova跨平台适配能力,使原运行于Android或iOS系统的Cordova H5项目可以无缝运行于Harmony系统。 - 插件化,支持以插件的形式为Web提供原生接口,让Web应用适配OpenHarmony的能力。 - 新增别名映射,支持映射一个插件方法到另一个插件方法 - 新增插件白名单,支持配置域名白名单,对于处于白名单中的域名,所有的插件方法权限全部放开 ## 配置要求 - Harmony SDK - Node.js ## 下载安装 ```shell ohpm install @ohos/cordova ``` ## 原生端用法 ### 初始化 | 属性名 | 类型 | 是否必填 | 默认值 | 备注 | | -------------- | ------- | -------- | ------ | ------------------------------------------------------------ | | debug | boolean | 否 | false | 是否调试模式,默认为生产发布模式
Debug模式下遇到错误将以异常形式抛出,Release模式遇到错误将尽力兜底,避免抛出异常 | | canLog | boolean | 否 | false | 是否可输出日志
注意:仅在debug为true调试模式时生效,生产发布时禁止输出日志 | | inspect | boolean | 否 | false | 是否开启WebView Inspect调试
注意:仅在debug为true调试模式时生效,生产发布时禁止Inspect调试 | | ignoreSslError | boolean | 否 | false | 是否忽略Web加载过程中的SSL握手错误
注意:仅在调试模式下生效,生产发布模式强制完成SSL握手以及校验 | | userAgent | string | 否 | false | 设置自定义的UA | ***示例*** ```typescript import { CordovaConfig, CordovaManager } from '@ohos/cordova/Index' import * as BuildProfile from 'BuildProfile' export default class EntryAbility extends UIAbility { onWindowStageCreate(windowStage: window.WindowStage): void { // Main window is created, set main page for this ability hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate'); // 初始化Cordova const cordovaConfig = new CordovaConfig() .setDebug(BuildProfile.DEBUG) .setCanLog(BuildProfile.DEBUG) .setInspect(BuildProfile.DEBUG) .setIgnoreSslError(BuildProfile.DEBUG) .setCustomUserAgent('testCordova/1.0.0') CordovaManager.init(cordovaConfig) // ........ } } ``` > 注意 > > 1. 请务必在使用CordovaWeb之前完成初始化 > 2. 初始化仅可执行一次,不可重复初始化 ### 插件 #### 开发插件 继承**CordovaPlugin**类,覆写*execute*方法(如有必要亦可覆写*onPageShow*、*onPageHide*、*onAboutToDisappear*等方法) ***示例*** ```typescript import { CallbackContext, CordovaPlugin } from '@ohos/cordova/Index' import { CordovaErrorType, getGeneralCordovaError } from '../utils/ErrorUtils' enum Actions { SAY = 'say' } export class HelloBridgePlugin extends CordovaPlugin { async execute(action: string, args: ESObject[], callbackContext: CallbackContext): Promise { if (action === Actions.SAY) { this.say(args[0], callbackContext) } else { return false } return true } private say(params: object, callbackContext: CallbackContext) { try { const name = params['name'] as string if (name === null || name === undefined || name.length === 0) { throw new Error() } callbackContext.successByString(`Hi ${name}, I'm Harmony`) } catch (e) { callbackContext.errorByJson(getGeneralCordovaError(CordovaErrorType.PARAM_ILLEGAL)) } } } ``` #### 配置插件 使用**CordovaManager**内的插件相关方法配置插件,即*setBridgeEntries*、*appendBridgeEntry*两个方法 - setBridgeEntries 设置全部的Cordova桥,重复设置会覆盖前一次的设置 - appendBridgeEntry 增加一个Cordova桥,不得重复添加相同的桥 ***示例*** ```typescript CordovaManager.appendBridgeEntry({ service: 'HelloBridge', factory: () => new HelloBridgePlugin() }) ``` ### 插件别名 别名可用于将原生的Cordova桥方法以另外一个名字暴露给H5,一方面可以针对H5屏蔽不同端的实现差异,一方面避免暴露底层桥模块实现 使用**CordovaManager**内的别名相关方法配置别名,即*setBridgeAliases*、*appendBridgeAlias*两个方法 - setBridgeAliases 设置全部的别名配置,重复设置会覆盖前一次的设置 - appendBridgeAlias 增加一个别名配置,不得重复添加相同的别名 > 注意:别名优先级高于实际的插件名,即Cordova调用查找实际桥之前先进行一次别名替换,使用替换后的桥名进行调用 ***示例*** ```typescript CordovaManager.appendBridgeAlias({ aliasService: 'TestBridge', aliasAction: 'print', service: 'HelloBridge', action: 'say' }) ``` > 按照示例所述配置,H5使用 TestBridge.print 同样可以调用到HelloBridge下的say方法 ### 权限 本项目权限支持两级配置,分别是域名白名单和CordovaWeb的*bridges*属性 两种方法差异点如下所示 | 方式 | 域名白名单 | CordovaWeb.bridges属性 | | ------ | -------------------------- | ---------------------- | | 作用域 | 全局,所有的CordovaWeb实例 | 仅限当前CordovaWeb实例 | | 优先级 | 高 | 低 | **域名白名单** 使用**CordovaManager**内的域名白名单相关方法配置白名单域名,即*setWhiteDomains*、*appendWhiteDomain*两个方法 - setWhiteDomains 设置全部的白名单域名配置,重复设置会覆盖前一次的设置 - appendWhiteDomain 增加一个白名单域名配置 ***示例*** ```typescript CordovaManager.appendWhiteDomain('www.baidu.com') ``` ### CordovaWeb接入 #### 属性列表 | 属性 | 含义 | 类型 | 是否必填 | 默认值 | 备注 | | -------------------- | -------------------------------------------- | ------------------------------------------------------------ | -------- | --------- | --------------------------------------------------- | | page | 所关联的Page页面CustomCompoent对象 | () => object | 是 | - | 定义为函数便于后期使用预绘制Web技术进行升级 | | parent | 所关联的Page页面(限定IPageContainer接口类型) | IPageContainer | 是 | - | | | url | 待加载的url | string | 是 | - | | | bridges | 当前加载Web页面可用桥权限配置 | BridgePermission[] | 否 | undefined | | | mixedMode | 混合加载模式 | MixedMode | 否 | None | 默认不允许混合加载 | | domStorageEnabled | 是否开启dom存储 | boolean | 否 | false | | | supportZoom | 是否开启手势缩放 | boolean | 否 | false | | | textZoom | 字体缩放比 | number | 否 | 1 | | | isCacheStrategy | 是否启用H5缓存 | boolean | 否 | false | | | isGeolocationEnabled | 是否开启访问地理位置 | boolean | 否 | false | | | darkMode | 深色模式 | WebDarkMode | 否 | Off | | | forceDarkAccess | 是否强制设置为深色模式 | boolean | 否 | false | | | incognitoMode | 是否开启隐私模式 | boolean | 否 | false | | | onTitleReceive | Web title标签回调 | (event?: OnTitleReceiveEvent) => void | 否 | undefined | 可监听此方法获取Html中的title标签,用于设置原生标题 | | onShowFileSelector | Web 文件选择器回调 | (event: ShowFileSelectorEvent) => boolean | 否 | undefined | | | onInterceptRequest | Web资源加载拦截器 | (event?: OnInterceptRequestEvent) => WebResourceResponse \| null \| undefined | 否 | undefined | 可覆写本方法实现H5离线包 | | onLoadStateChanged | Web加载状态回调 | (loadState: PageLoadState) => void | 否 | undefined | 可监听此回调,设置Loading页面或者Error页面 | #### 接入 接入方根据自己的页面框架(Router或者Navigation)开发自己的页面,接入CordovaWeb组件,并按需传入所需的属性;其次页面需要实现**IPageContainer**接口(此接口为鸭式编程接口,无须显示继承,定义同名方法即可) 1. 实现**IPageContainer**接口中的方法,该接口由CordovaWeb创建和销毁时调用,接入方页面需要持有和管理CordovaWeb实例。 2. CordovaWeb作为一个CustomCompoent组件,无法直接拿到*onBackPress*、*onPageShow*和*onPageHide*事件,需要由调用方的页面根据页面栈技术选型自行实现并派发给CordovaWeb实例 3. build渲染函数中接入CordovaWeb使用,并配置必要的属性(必填属性必须配置,选填属性自行根据需要配置) ***示例*** ```typescript import router from '@ohos.router' import List from '@ohos.util.List' import { CordovaWeb, ICordovaWebContainer, IPageContainer } from '@ohos/cordova/Index' @Entry @Component struct Index { /** * 一个页面支持有多个CordovaWeb容器,使用集合记录 */ private readonly cordovaWebList: List = new List() onPageShow(): void { // 通知当前页面下的所有CordovaWeb容器页面显示 this.cordovaWebList.forEach(it => it.onPageShow && it.onPageShow()) } onPageHide(): void { // 通知当前页面下的所有CordovaWeb容器页面隐藏 this.cordovaWebList.forEach(it => it.onPageHide && it.onPageHide()) } onBackPress(): boolean | void { // 优先派发返回按钮点击事件给CordovaWeb容器(Cordova优先交给H5处理,其次自行退history栈) for (let index = 0; index < this.cordovaWebList.length; index++) { if (this.cordovaWebList.get(index).onBackPress && this.cordovaWebList.get(index).onBackPress!()) { // Web组件消费了返回事件 return true } } // 如果CordovaWeb组件没有消费返回事件,则页面自行处理,即页面本身退栈 router.back() return true } /** * CordovaWeb挂载的页面需要实现的 {@link IPageContainer} 的接口 * * @param cordovaWeb 新创建的CordovaWeb实例 */ onCordovaWebAppear(cordovaWeb: ICordovaWebContainer): void { // 记录CordovaWeb实例,用于通知onBackPress、onPageShow、onPageHide等事件 if (!this.cordovaWebList.has(cordovaWeb)) { this.cordovaWebList.add(cordovaWeb) } } /** * CordovaWeb挂载的页面需要实现的 {@link IPageContainer} 的接口 * * @param cordovaWeb 即将销毁的CordovaWeb实例 */ onCordovaWebDisappear(cordovaWeb: ICordovaWebContainer): void { this.cordovaWebList.remove(cordovaWeb) } build() { Column() { Row() { Image($r('app.media.ic_navigation_back')) .width(48) .height(48) .padding(10) .objectFit(ImageFit.Contain) .onClick(() => { this.onBackPress() }) .id('back_img') Row() { Text('测试Cordova') .fontSize(18) .fontColor('#F0F0F0') .maxLines(1) } .layoutWeight(1) .margin({ right: 48 }) } .width('100%') .height(48) .backgroundColor('#333') CordovaWeb({ page: () => this, url: 'https://www.baidu.com/js/test.html', parent: this as IPageContainer, bridges: [ { name: 'HelloBridge', methods: null } ] } .width('100%') .height('100%') } } ``` ### H5端用法 1. 判断当前运行端是否为Harmony系统,若为Harmony系统则引入**cordova.harmony.js**文件(cordova.harmony.js位于工程根目录/js/cordova.harmony.js) 2. 根据业务需要调用Cordova方法 1. 使用*document.addEventListener*监听**deviceready**事件(deviceready事件表征cordova异步加载完成) 2. 获取*window.Cordova.exec*对象,该对象为cordova.js文件执行完成挂载到window上的一个对象 3. 执行exec方法调用原生Cordova方法 > **exec**方法共有5个参数,分别如下所示 > > 1. 成功回调函数 > 2. 失败回调函数 > 3. 插件名 > 4. 方法名 > 5. 参数对象 注意:参数最外层必须为数组结构 ***示例*** ```js function onTestBtnClick() { document.addEventListener('deviceready', () => { var exec = window.Cordova.exec exec((result) => { alert(JSON.stringify(result)) }, () => { alert('fail') }, 'HelloBridge', 'say', [{name:'test'}]) }) } ``` #### back事件处理 back事件Cordova中已经适配并做了特殊处理,具体处理方法见示例代码 ***示例*** ```javascript function onTestInterceptBack() { document.addEventListener('deviceready', () => { alert('开始监听back按钮点击事件') document.addEventListener('backbutton', () => { alert('收到back按钮点击事件') }) }) } ``` ### 参与贡献 1. Fork 本仓库 2. 新建 Feat_xxx 分支 3. 提交代码 4. 新建 Pull Request