# 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