Quickstart
Learn how to integrate the H5 container component into a HarmonyOS NEXT client. Add the H5 container SDK to your project using ohpmrc.
Prerequisites
Before adding the H5 container SDK, ensure you have integrated your project with mPaaS. For details, see the developer guide Integrate mPaaS capabilities.
Ensure the mppm plugin is installed. For details, see Install the mppm tool.
Dependencies
In your project's .ohpmrc file, add the following repository:
@mpaas:registry=https://mpaas-ohpm.oss-cn-hangzhou.aliyuncs.com/metaAdd the SDK
Use the mppm tool to install the H5 container component.

Permissions
Configure the required permissions in module.json5.
"requestPermissions":[
{
"name" : "ohos.permission.GET_NETWORK_INFO",
},
{
"name" : "ohos.permission.INTERNET",
}
]Using the SDK
Initialization
Once the mPaaS framework is initialized, initialize HRiver with the following code:
HRiver.init();Before using the SDK, you must initialize the mPaaS framework and set the userId and appsecret. The appsecret can be obtained from the AppCenter console. For details, see Get the HarmonyOS NEXT configuration file.
The code example follows:
import AbilityStage from '@ohos.app.ability.AbilityStage';
import { MPFramework } from '@mpaas/framework';
export default class ModuleEntry extends AbilityStage {
async onCreate() {
MPFramework.create(this.context);
MPFramework.instance.userId = 'MPTestCase'
MPFramework.instance.appSecret = "12a711d78980f661aca5401788fdf09a";
}
}Open an offline package
After initializing HRiver, call startApp to open the offline package.
import { HRiver } from '@mpaas/hriver'
/**
* Opens an offline package.
* @param appId The offline package ID.
* @param startParams Optional. Startup parameters that control elements such as the title bar and the target page.
*/
HRiver.startApp(appId: string, startParams?: Map<string, Object>)Here is a code sample:
import { HRiver } from '@mpaas/hriver'
// Example 1:
HRiver.startApp('20190517') // Start the offline package directly.
// Example 2:
let startParams: Map<string, Object> = new Map();
startParams.set('defaultTitle', 'Default Title') // Sets the default title for the loading phase.
HRiver.startApp('20190517', startParams)Online page
Initialize HRiver, then call startUrl to open a web page.
import { HRiver } from '@mpaas/hriver'
/**
* Opens a web page.
* @param url The URL of the web page.
* @param startParams Optional. Start parameters to control elements such as the title bar and the target page.
*/
HRiver.startUrl(url: string, startParams: Map<string, Object>)Custom JSAPI
To register a custom JSAPI, initialize HRiver and then call registerPlugin.
import { HRiver } from '@mpaas/hriver'
/**
* Registers a custom JSApi implementation.
* @param pluginClass An array of plugin classes.
* @param scope Optional. The plugin's lifecycle scope. Use `HRiver.SCOPE_APP` for the offline package's app-level lifecycle or `HRiver.SCOPE_PAGE` for the page-level lifecycle. Defaults to `HRiver.SCOPE_PAGE`.
*/
HRiver.registerPlugin(pluginClass: ESObject, scope: string)Code example:
HRiver.registerPlugin({H5CustomPlugin, H5Custom1Plugin})The following example shows the plugin implementation:
import { HRiver, H5SimplePlugin, H5EventFilter, H5Event, H5BridgeContext } from '@mpaas/hriver'
class H5CustomPlugin extends H5SimplePlugin {
onPrepare(filter: H5EventFilter): void {
filter.addAction('myapi1')
}
handleEvent(event: H5Event, context: H5BridgeContext): Boolean {
if ('myapi1' == event.action) {
context.sendBridgeResult({
success: true,
data: 'myapi1 called successfully'
})
return true
}
return super.handleEvent(event, context);
}
}
class H5Custom1Plugin extends H5SimplePlugin {
onPrepare(filter: H5EventFilter): void {
filter.addAction('myapi2')
}
handleEvent(event: H5Event, context: H5BridgeContext): Boolean {
if ('myapi2' == event.action) {
context.sendBridgeResult({
success: true,
data: 'myapi2 called successfully'
})
return true
}
return super.handleEvent(event, context);
}
}Native calls H5
There are two ways to call H5 from native code.
In a custom JSAPI, you can send a message to an H5 page using the
H5BridgeContext.sendToWebmethod.import { H5BridgeContext, H5Event, H5EventFilter, H5SimplePlugin, HRiver } from '@mpaas/hriver'; class H5CustomPlugin extends H5SimplePlugin { handleEvent(event: H5Event, context: H5BridgeContext): Boolean { // Send a message to the H5 page. context.sendToWeb('customCallWeb', { data: 'abc' }) ... // Other code } }From your native code, use
AppManagerto get the current top page and call itssendToWebmethod to send a message to the H5 page. How you retrieve the page depends on how it was opened.Method 1
When using
HRiver.startAppto open an offline package, get the top app withfindTopAppand the current active page withgetActivePage.import { HRiver, XRiverProxy, getProxy, AppManager, AppNode, Page } from '@mpaas/hriver'; let appManager = getProxy(XRiverProxy.AppManager) as AppManager let appNode: AppNode | null = appManager.findTopApp() if (appNode != null) { let page: Page | null = appNode.getActivePage() if (page != null) { page.sendToWeb('testAction', { data: '' }) } }Method 2
When using
HRiver.startUrlto open an online page, get the topH5SessionwithfindTopSessionand the current page withgetTopPage.import { HRiver, XRiverProxy, getProxy, AppManager, AppNode, H5Session, Page } from '@mpaas/hriver'; let appManager = getProxy(XRiverProxy.AppManager) as AppManager let topSession: H5Session = appManager.findTopSession() as H5Session if (topSession != null) { let page: Page | null = topSession.getTopPage() if (page != null) { page.sendToWeb('testAction', { data: '' }) } }
Built-in offline package
Loads all built-in offline packages, including the public ones.
After initializing HRiver, call loadOfflineResource to load the built-in offline package.
import { HRiver } from '@mpaas/hriver'
/**
* Loads a built-in offline package.
* @param jsonFileName The filename of the `h5_json.json` file for the built-in offline package. Place this file in the rawfile directory. For example: `h5_json.json`.
* @param callback A callback function that runs after the built-in offline package loads. The signature is `(result: string) => {}`.
*/
HRiver.loadOfflineResource(jsonFileName: string, callback: Function)Here is a code example:
In the
entry/src/main/resources/rawfiledirectory, add theh5_json.jsonfile, which can be downloaded from the Appcenter console.{ "config":{ "updateReqRate":16400, "limitReqRate":13600, "appPoolLimit":3, "versionRefreshRate":86400 }, "data":[ { "app_desc":"Offline package 1", "app_id":"20180910", "auto_install":1, "fallback_base_url":"https://mcube-prod.oss-cn-hangzhou.aliyuncs.com/570DA89281533-default/20180910/1.0.0.3_all/nebula/fallback/", "global_pack_url":"", "icon_url":"", "installType":1, "main_url":"/www/index.html", "name":"Offline package 1", "online":1, "package_url":"https://mcube-prod.oss-cn-hangzhou.aliyuncs.com/570DA89281533-default/20180910/1.0.0.3_all/nebula/20180910_1.0.0.3.amr", "patch":"", "sub_url":"", "version":"1.0.0.3", "vhost":"https://20180910.h5app.com" }, { "app_desc":"Offline package 2", "app_id":"20190517", "auto_install":1, "fallback_base_url":"https://mcube-prod.oss-cn-hangzhou.aliyuncs.com/570DA89281533-default/20190517/1.0.0.0_all/nebula/fallback/", "global_pack_url":"", "icon_url":"", "installType":1, "main_url":"/www/index.html", "name":"Offline package 2", "online":1, "package_url":"https://mcube-prod.oss-cn-hangzhou.aliyuncs.com/570DA89281533-default/20190517/1.0.0.0_all/nebula/20190517_1.0.0.0.amr", "patch":"", "sub_url":"", "version":"1.0.0.0", "vhost":"https://20190517.h5app.com" } ], "resultCode":100, "resultMsg":"Operation successful", "state":"success" }Download the .amr file and rename it to
${appid}_${version}.amr.
Call the API.
import { HRiver } from '@mpaas/hriver' HRiver.loadOfflineResource('h5_json.json', (result: string) => { this.resultText = this.resultText + `\n${result} preloaded successfully` })
Update an offline package
import { HRiver } from '@mpaas/hriver'
/**
* Updates offline packages in a batch.
* @param appIds The IDs of the offline packages to update.
* @param updateCallback A callback that is executed after the update completes. The function receives a `result` (boolean) and a `code` (number).
*/
HRiver.updateApp(appIds?: Array<string>, updateCallback?: Function)
/**
* Updates offline packages in a batch to specific versions.
* @param appIds A map of `appId`s to their target `version`s.
* @param updateCallback A callback that is executed after the update completes. The function receives a `result` (boolean) and a `code` (number).
*/
HRiver.updateAppWithVersion(appIds?: Map<string, string>, updateCallback?: Function)
The code example follows:
import { HRiver } from '@mpaas/hriver'
HRiver.updateApp(['90000002'], (result: boolean, code: number) => {
this.resultText = `Update for 90000002: ${result}`
})Public offline package
Configure the
H5CommonAppProvider.import { HRiver, H5CommonAppProvider } from '@mpaas/hriver' /** * Configures the common offline package provider. Must be called before HRiver.init(). */ HRiver.setProvider(H5CommonAppProvider.name, new H5AppCommonProviderImpl())NotesetProvidermust be called beforeHRiver.init().Implement the
getCommonResourceAppListmethod inH5AppCommonProviderImpl.ets.import { HRiver, H5CommonAppProvider } from '@mpaas/hriver' export class H5AppCommonProviderImpl extends H5CommonAppProvider { getCommonResourceAppList(): Array<string> { return ['20220719'] // Returns a list of common offline package IDs. } }
Fallback logic
On iOS and Android, if an offline package fails to download, the application falls back to the online address.
This feature is disabled by default. To enable it, use the following switch:
In H5CommonAppProvider, implement the configJSON method and add the enableFallback parameter.
export class H5AppCommonProviderImpl extends H5CommonAppProvider {
... // Other configurations
// Specifies configuration settings, such as the update frequency.
configJSON(): string {
return JSON.stringify({
enableFallback: 'YES',
xxx // Other configurations
})
}
}User-Agent
Calling setUserAgent after HRiver.init() appends the specified useragent to the default user agent.
Offline package default update frequency
Set the
H5CommonAppProvider.import { HRiver, H5CommonAppProvider } from '@mpaas/hriver' /** * Configures the common offline package provider, including its update frequency and other settings. This method must be called before HRiver.init(). */ HRiver.setProvider(H5CommonAppProvider.name, new H5AppCommonProviderImpl())NoteYou must call
setProviderbeforeHRiver.init().Implement the
configJSONmethod ofH5CommonAppProvideras shown in the following code example.import { HRiver, H5CommonAppProvider } from '@mpaas/hriver' export class H5AppCommonProviderImpl extends H5CommonAppProvider { ... // Other configuration // configJSON: Configure the update frequency and other settings. configJSON(): string { return JSON.stringify({ h5_nbmngconfig: "{\"config\":{\"al\":\"3\",\"pr\":{\"4\":\"86400\",\"common\":\"864000\"},\"ur\":\"1800\",\"fpr\":{\"common\":\"3888000\"}},\"switch\":\"yes\"}" }) } }The h5_nbmngconfig parameter contains the configuration string:
h5_nbmngconfig: "{\"config\":{\"al\":\"3\",\"pr\":{\"4\":\"86400\",\"common\":\"864000\"},\"ur\":\"1800\",\"fpr\":{\"common\":\"3888000\"}},\"switch\":\"yes\"}"ur: 1800indicates an update frequency of 1800 seconds; change the value ofurto adjust this frequency.
Offline package signature verification
Set the
H5CommonAppProvider.import { HRiver, H5CommonAppProvider } from '@mpaas/hriver' /** * Configures features such as the common offline package provider, offline package update frequency, and signature verification. * Call this method before HRiver.init(). */ HRiver.setProvider(H5CommonAppProvider.name, new H5AppCommonProviderImpl())NoteCall
setProviderbeforeHRiver.init().Implement the
H5CommonAppProvidermethods:shouldVerifyandpubKey. The following is a code example:import { HRiver, H5CommonAppProvider } from '@mpaas/hriver' export class H5AppCommonProviderImpl extends H5CommonAppProvider { ... // Other configurations /** * Specifies whether to enable signature verification for offline packages. Disabled by default. * @returns {boolean} `true` to enable verification, or `false` to disable it. */ shouldVerify(): boolean { return false } // The public key for offline package signature verification. // This key is required only if shouldVerify returns true. pubKey(): string { return '' } }
Enable debug mode
Monitor page lifecycle
After initialization, set the provider.
import {H5PageLifeCycleProvider} from '@mpaas/hriver';
HRiver.setProvider(H5PageLifeCycleProvider.name, new H5PageLifeCycle())H5PageLifeCycle
import { H5PageLifeCycleProvider, Page } from '@mpaas/hriver';
import { hilog } from '@kit.PerformanceAnalysisKit';
export class H5PageLifeCycle extends H5PageLifeCycleProvider {
onPageShow(routerName: string, page?: Page | undefined): void {
super.onPageShow(routerName, page);
hilog.debug(1, 'H5PageLifeCycle', "pageshow: " + page?.pageUrl)
}
onPageHide(routerName: string, page?: Page | undefined): void {
super.onPageHide(routerName, page);
hilog.debug(1, 'H5PageLifeCycle', "onPageHide: " + page?.pageUrl)
}
onPageCreate(page?: Page | undefined): void {
super.onPageCreate(page);
hilog.debug(1, 'H5PageLifeCycle', "onPageCreate: " + page?.pageUrl)
}
onPageExit(page?: Page | undefined): void {
super.onPageExit(page);
hilog.debug(1, 'H5PageLifeCycle', "onPageExit: " + page?.pageUrl)
}
onBackPress(page?: Page | undefined): boolean {
hilog.debug(1, 'H5PageLifeCycle', "onBackPress: " + page?.pageUrl)
return super.onBackPress(page);
}
}Plugin deregistration
Implement log out for the Page component in Page.ets.
// Unregisters a plugin by its action.
unregisterPluginByAction(action: string);
// Unregisters a plugin by its name.
unregisterPluginByPluginName(name: string);Here is an example:

Embedded mode
The page embedding mode is based on navigation.
Create the
NavPathStackrequired by the offline package.pageInfos: NavPathStack = new NavPathStack()Add a blank component where you want to embed the page.
ImportantThe mode must be
NavigationMode.Stack. Otherwise, you may encounter issues when switching between portrait and landscape orientations or on foldable screens.Navigation(this.pageInfos) { Column() { // Blank component to embed the offline package page. No content is required. }.width('100%') // Set the width as needed. .backgroundColor(Color.Red) // For example only. .height(500) // Set the height as needed. }.navDestination(this.PagesMap) .mode(NavigationMode.Stack) // pagesMap implementation: import {HRBuilder, RouterUtils, HRiver, H5RouterNavStackProvider} from '@mpaas/hriver' let mPaaSHRiverBuilder: WrappedBuilder<[string, ESObject]> = wrapBuilder(HRBuilder); @Builder PagesMap(name: string, params: ESObject) { if (RouterUtils.isMPHRiverPage(name)) { mPaaSHRiverBuilder.builder(name, params) } else if (name == 'xxx') { // Pages for other services. } }When starting an offline package or an online page, pass the NavPathStack created in Step 1 as the third parameter. Add the embedPage parameter to specify that the page is embedded.
let param: Map<string, Object> = new Map<string, Object>() param.set('embedPage', 'YES') HRiver.startUrl('https://www.baidu.com', param, this.pageInfos) // HRiver.startApp('90000000', param, this.pageInfos)Intercept the back press event.
HRiver.setProvider(H5PageLifeCycleProvider.name, new H5PageLifeCycle())
import { H5PageLifeCycleProvider, Page } from '@mpaas/hriver'; import { hilog } from '@kit.PerformanceAnalysisKit'; import { router } from '@kit.ArkUI'; export class H5PageLifeCycle extends H5PageLifeCycleProvider { onPageShow(routerName: string, page?: Page | undefined): void { super.onPageShow(routerName, page); hilog.debug(1, 'H5PageLifeCycle', "onPageShow: " + page?.pageUrl) } onPageHide(routerName: string, page?: Page | undefined): void { super.onPageHide(routerName, page); hilog.debug(1, 'H5PageLifeCycle', "onPageHide: " + page?.pageUrl) } onPageCreate(page?: Page | undefined): void { super.onPageCreate(page); hilog.debug(1, 'H5PageLifeCycle', "onPageCreate: " + page?.pageUrl) } onPageExit(page?: Page | undefined): void { super.onPageExit(page); hilog.debug(1, 'H5PageLifeCycle', "onPageExit: " + page?.pageUrl) } onBackPress(page?: Page | undefined): boolean { hilog.debug(1, 'H5PageLifeCycle', "onBackPress: " + page?.pageUrl) let navPathStack: NavPathStack|undefined = page?.getSession()?.getRouter()?.getNavPathStack() if (navPathStack && page && page.embedPage && navPathStack.size() == 1) { router.back() return true; } return super.onBackPress(page); } }
UI customization
Custom navigation bar
After initialization, use a
providerto set a custom navigation bar.import {HRBuilder, RouterUtils, HRiver, H5CommonAppProvider, H5RouterNavStackProvider, CustomUIBuilderProvider, H5CacheProvider } from '@mpaas/hriver' HRiver.setProvider(CustomUIBuilderProvider.name, new CustomUIBuilderProviderImpl())Implement
CustomUIBuilderProviderImpl.import { CustomUIBuilderProvider, Page } from '@mpaas/hriver'; import { CustomUIBuilder } from '../pages/CustomTitleBarComponent'; export class CustomUIBuilderProviderImpl extends CustomUIBuilderProvider { getCustomUIBuilder(): WrappedBuilder<[string, Page]> { return wrapBuilder(CustomUIBuilder); } }CustomUIBuilderis the global builder for a customtitle barcomponent./** * name: The name of the custom component. * page: The page that corresponds to the custom title bar. */ @Builder export function CustomUIBuilder(name: string, p: Page) { if (name === 'titleBar') { CustomTitleBarComponent({page: p, titleBarData: p.titleBarData}) } }CustomTitleBarComponentis the concrete title bar component.titleBarDatacontains the data for the title bar. The UI updates in real time as this data changes.import { H5NavMenuItemData, HRiverUtil, Page, TitleBarData } from '@mpaas/hriver' const TAG: string = "CustomTitleBarComponent" const MENU_MARGIN: number = 5 const DEFAULT_MARGIN: number = 12 @Builder export function CustomUIBuilder(name: string, p: Page) { if (name === 'titleBar') { CustomTitleBarComponent({page: p, titleBarData: p.titleBarData}) } } @Component export struct CustomTitleBarComponent { page: Page | null = null @Prop titleBarData: TitleBarData aboutToAppear(): void { } build() { RelativeContainer() { Button() { Image(this.getBackIconImage()) .id('titlebar_back_img') .width(12) .height(20) } .width(48) .height('100%') .borderRadius(0) .backgroundColor(Color.Transparent) .align(Alignment.Center) .alignRules({ top: { anchor: '__container__', align: VerticalAlign.Top } }) .id("h5_nav_close") .onClick((event) => { if (this.page != null) { this.page.backClickEvent() } }) Flex({ justifyContent: FlexAlign.Center, direction: FlexDirection.Column }) { // If an image title is set, the title bar displays the image instead of text. if (this.titleBarData.titleImage) { Image(this.titleBarData.titleImage) .id('titlebar_title_image') .height(36).onClick(() => { this.onTitleClick() }) } else { Flex({ justifyContent: FlexAlign.Start,direction: FlexDirection.Row }){ Text(this.titleBarData.title) .fontSize(18) .textAlign(TextAlign.Start) .textOverflow({ overflow: TextOverflow.Ellipsis }) .maxLines(1) .fontColor(this.titleBarData.titleColor) .onClick(() => { this.onTitleClick() }) if(this.titleBarData.showTitleLoading ){ Image(this.getTitleBarLoadingIcon()).width(18).height(18) .id('titlebar_progress_img') .margin({left:5,top:1}) .rotate({ angle: this.titleBarData.loadingRotateAngle }) .animation({ duration:3000, curve: Curve.Linear, delay: 0, iterations: -1, playMode: PlayMode.Normal, }).onAppear(()=>{ this.titleBarData.loadingRotateAngle = 360 }) } } if (this.titleBarData.subtitle) { Text(this.titleBarData.subtitle) .textAlign(TextAlign.Start) .fontColor(this.titleBarData.titleColor) .textOverflow({ overflow: TextOverflow.Ellipsis }) .maxLines(1) .fontSize(18) .onClick(() => { this.onTitleSubtitleClick() }) } } }.id("h5_tv_title") .height("100%") .alignRules({ top: { anchor: '__container__', align: VerticalAlign.Top }, left: { anchor: 'h5_nav_close', align: HorizontalAlign.End }, right: { anchor: "h5_nav_options", align: HorizontalAlign.Start } }) if (this.titleBarData.optionMenuState) { if (this.titleBarData.menuType == TitleBarData.MENU_TYPE_TITLE) { Text(this.titleBarData.menuTitle) .fontSize(16) .align(Alignment.Center) .textAlign(TextAlign.Center) .fontColor(this.titleBarData.menuColor) .height('100%') .id("h5_nav_options") .alignRules({ top: { anchor: '__container__', align: VerticalAlign.Top }, right: { anchor: '__container__', align: HorizontalAlign.End } }) .onClick(() => { this.onMoreClick(false, 0) }) .margin({ right: DEFAULT_MARGIN }) } else if (this.titleBarData.menuType == TitleBarData.MENU_TYPE_ICON) { Button() { Image(HRiverUtil.getIconImage(this.titleBarData.menuIcon)) .id('titlebar_right_icon') .width(22) .height(22) .objectFit(ImageFit.Contain) } .width(30) .borderRadius(0) .backgroundColor(Color.Transparent) .align(Alignment.Center) .id("h5_nav_options") .height("100%") .onClick(() => { this.onMoreClick(false, 0) }) .alignRules({ top: { anchor: '__container__', align: VerticalAlign.Top }, right: { anchor: '__container__', align: HorizontalAlign.End } }) .margin({ right: DEFAULT_MARGIN }) } else if (this.titleBarData.menuType == TitleBarData.MENU_TYPE_MORE) { Button() { Image(HRiverUtil.getIconImage('more')) .id('titlebar_right_more') .width(22) .height(22) .objectFit(ImageFit.Contain) } .width(48) .borderRadius(0) .backgroundColor(Color.Transparent) .align(Alignment.Center) .id("h5_nav_options") .height("100%") .margin({ right: DEFAULT_MARGIN }) .alignRules({ top: { anchor: '__container__', align: VerticalAlign.Top }, right: { anchor: '__container__', align: HorizontalAlign.End } }).onClick(() => { if (!this.titleBarData.preventDefault) { this.titleBarData.customPopup = !this.titleBarData.customPopup } this.onMoreClick(true, 0) }) .bindPopup(this.titleBarData.customPopup , { builder: this.MenuBuilder, placement:Placement.BottomLeft, popupColor:"#fff", onStateChange: (e) => { console.info(JSON.stringify(e.isVisible)) if (!e.isVisible) { this.titleBarData.customPopup = false } } }) } if (this.titleBarData.menuType1 == TitleBarData.MENU_TYPE_TITLE) { Text(this.titleBarData.menuTitle1) .fontSize(16) .align(Alignment.Center) .textAlign(TextAlign.Center) .fontColor(this.titleBarData.menuColor1) .height('100%') .id("h5_nav_options1") .alignRules({ top: { anchor: '__container__', align: VerticalAlign.Top }, right: { anchor: 'h5_nav_options', align: HorizontalAlign.Start } }) .margin({ right: MENU_MARGIN }) .onClick(()=> { this.onMoreClick(false, 1) }) } else if (this.titleBarData.menuType1 == TitleBarData.MENU_TYPE_ICON) { Button() { Image(HRiverUtil.getIconImage(this.titleBarData.menuIcon1)) .id('titlebar_right_icon1') .width(22) .height(22) .objectFit(ImageFit.Contain) } .width(30) .borderRadius(0) .backgroundColor(Color.Transparent) .align(Alignment.Center) .id("h5_nav_options1") .height("100%") .onClick(()=> { this.onMoreClick(false, 1) }) .alignRules({ top: { anchor: '__container__', align: VerticalAlign.Top }, right: { anchor: 'h5_nav_options', align: HorizontalAlign.Start } }) .margin({ right: MENU_MARGIN }) } else if (this.titleBarData.menuType1 == TitleBarData.MENU_TYPE_MORE) { Button() { Image(HRiverUtil.getIconImage('more')) .id('titlebar_right_more1') .width(22) .height(22) .objectFit(ImageFit.Contain) } .width(48) .borderRadius(0) .backgroundColor(Color.Transparent) .align(Alignment.Center) .id("h5_nav_options1") .height("100%") .margin({ right: MENU_MARGIN }) .alignRules({ top: { anchor: '__container__', align: VerticalAlign.Top }, right: { anchor: 'h5_nav_options', align: HorizontalAlign.Start } }).onClick(() => { if (!this.titleBarData.preventDefault) { this.titleBarData.customPopup1 = !this.titleBarData.customPopup1 } this.onMoreClick(true, 1) }) .bindPopup(this.titleBarData.customPopup1 , { builder: this.MenuBuilder, placement:Placement.BottomLeft, popupColor:"#fff", onStateChange: (e) => { console.info(JSON.stringify(e.isVisible)) if (!e.isVisible) { this.titleBarData.customPopup1 = false } } }) } } }.height(this.titleBarData.showTitleBar ? 48 : 0) } getTitleBarLoadingIcon(): Resource | string { return $rawfile(`icon/h5_title_bar_progress_bg.webp`) } getIconImage(icon: string): Resource | string { return HRiverUtil.getIconImage(icon) } getBackIconImage(): Resource | string { return $rawfile(`icon/hriverback.webp`) } onTitleClick() { if (this.page != null) { this.page.titleBarClickEvent() } } onTitleSubtitleClick() { if (this.page != null) { this.page.subTitleBarClickEvent() } } onMoreClick(fromMenu:boolean, index: number) { if (this.page != null) { this.page.onMoreClick(fromMenu, index) } } onMoreItemClick(tag: string, name: string,isShowPopMenu:boolean) { if (this.page != null) { this.page.onMoreItemClick(tag, name, isShowPopMenu) } } @Builder MenuBuilder() { Flex({ direction: FlexDirection.Column, justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) { ForEach(this.titleBarData.h5NavMenuItemList, (item: H5NavMenuItemData, index) => { Column() { Row() { Image(item.icon).width(20).height(20).margin({ right: 5 }) Text(item.name).fontSize(16) } .width('100%') .height(30) .padding({ left: 10 }) .justifyContent(FlexAlign.Start) .align(Alignment.Center) .onClick(() => { if (item) { this.onMoreItemClick(item.tag || '', item.name || '',false) this.titleBarData.customPopup = false this.titleBarData.customPopup1 = false } }) if (index != this.titleBarData.h5NavMenuItemList.length - 1) { Divider().height(10).width('90%').color('#ccc') } }.padding(5).height(40) }) }.width(150).height(165).backgroundColor("#fff") } }
Custom loading and error pages
Set the provider after initialization.
import { CustomLoadingBuilderProvider} from '@mpaas/hriver'; HRiver.setProvider(CustomLoadingBuilderProvider.name, new CustomLoadingBuilderProviderImpl())Implement the
CustomLoadingBuilderProviderImplclass.import { CustomLoadingBuilderProvider, H5Router, HRLoadingData } from '@mpaas/hriver'; import { CustomUIBuilder } from './CustomLoadingComponent'; export class CustomLoadingBuilderProviderImpl extends CustomLoadingBuilderProvider { getCustomUIBuilder(): WrappedBuilder<[string, HRLoadingData, H5Router]> { return wrapBuilder(CustomUIBuilder); } }Implement the
CustomLoadingComponent.etsclass. UseloadingStatusto control the loading UI.import { H5Router, HRLoadingData, LoadingStatus } from '@mpaas/hriver' const TAG: string = "CustomLoadingComponent" export const Loading_STATE_Init = 0 export const Loading_STATE_Start = 1 export const Loading_STATE_End = 2 export const Loading_STATE_Err = 3 @Builder export function CustomUIBuilder(name: string, loadingData: HRLoadingData, h5Router: H5Router) { if (name === 'loading') { CustomLoadingComponent({loadingStatus: loadingData.loadingStatus, h5Router: h5Router}) } } @Component export struct CustomLoadingComponent { @ObjectLink loadingStatus: LoadingStatus h5Router?: H5Router aboutToAppear(): void { } build() { Row() { Column() { Flex({ direction: FlexDirection.Row }) { // back button Button() { Image($rawfile("icon/hriverback.webp")) .width(12) .height(20) } .width(48) .height('100%') .borderRadius(0) .backgroundColor(Color.Transparent) .align(Alignment.Center) .onClick((event) => { this.h5Router?.routerBack() }) }.width('100%') .height(48) Image(this.loadingStatus.icon ? this.loadingStatus.icon : $rawfile("icon/hriverloading.webp")) .width(40) .height(40) .margin({top: 38}) if (this.loadingStatus.state == Loading_STATE_Err || this.loadingStatus.state == Loading_STATE_Start){ Text(this.loadingStatus.state == Loading_STATE_Err ? `The network is unstable. Please try again later. \n(${this.loadingStatus.code} ${this.loadingStatus.msg})` : this.loadingStatus.title) .fontSize(18) .margin({top: 15}) .textAlign(TextAlign.Center) } } .width('100%') .margin({ top: 48 }) } } }
Custom URL error page
Set a provider to intercept the web page error callback.
HRiver.setProvider(H5WebClientProvider.name, new H5WebClientProviderImpl());In the
H5WebClientProviderImplclass, implement theonErrorReceivemethod.import { MPFramework } from '@mpaas/framework'; import { H5WebClientProvider, Page } from '@mpaas/hriver'; import { util } from '@kit.ArkTS'; export class H5WebClientProviderImpl extends H5WebClientProvider{ onErrorReceive(page: Page | undefined, request: WebResourceRequest | undefined, err: WebResourceError | undefined): boolean { let errorUrl = request?.getRequestUrl() let errorCode = err?.getErrorCode() if (errorCode == 403 || errorCode == 404) { // For consistency with the iOS implementation, do not show the error page for 404 and 403 errors. // log(TAG, "ignoreErrorPage 404 or 403, return "); return true; } let lastUrl = page?.webcontroller?.getUrl() if (errorUrl == page?.pageUrl || errorUrl == `${page?.pageUrl}/`) { // Read the custom error page demo_custom_err.html from the rawfile directory. let dataBytes = MPFramework.instance.context.resourceManager.getRawFileContentSync('demo_custom_err.html') let textDecoder = new util.TextDecoder("utf-8", { fatal: false, ignoreBOM: false }) let html = textDecoder.decodeWithStream(new Uint8Array(dataBytes), { stream: true }) page?.webcontroller?.loadData(html, "text/html", "utf-8", lastUrl) return true; } return false } }In the
demo_custom_err.htmlfile, write the error page code.<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta name="apple-mobile-web-app-capable" content="yes"/> <meta name="apple-mobile-web-app-status-bar-style" content="black"/> <meta name="format-detection" content="telephone=no"/> <meta name="format-detection" content="email=no"/> <meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,minimum-scale=1,user-scalable=0"/> <title>!!!!</title> <style type="text/css"> body { background-color: #FFF; } .am-page-result { text-align: center; } .am-page-result .am-page-result-pic { width: 135px; height: 135px; margin: 40px auto; } .am-page-result .am-page-result-pic img { width: 100%; height: 100%; } .am-page-result p { margin: 0; font-size: 16px; color: #999; } .am-page-result-button { margin-top: 25px; display: -webkit-box; display: -webkit-flex; display: flex; } .am-button { display: block; margin: 0 10px; padding: 0 10px; height: 42px; text-align: center; font-size: 18px; line-height: 42px; border-radius: 4px; outline: 0; -webkit-appearance: none; -webkit-box-flex: 1; -webkit-flex: 1; flex: 1; width: 50%; } .am-button[am-mode~=white] { border: 1px solid #DDD; color: #666; background-color: #FFF; } .am-button[am-mode~=white]:active { border-color: #D8D8D8; background-color: #F8F8F8; } .am-button[am-mode~=blue] { border: 1px solid #28F; color: #FFF; background-color: #39F; } .am-button[am-mode~=blue]:active { border-color: #17F; background-color: #28F; } .am-button[am-mode~=light], .am-button[am-mode~=light]:active { border: none; color: #39F; background-color: #FFF; } @media screen and (min-device-width: 375px) { .am-page-result .am-page-result-pic { width: 160px; height: 160px; margin: 45px auto; } .am-page-result-button { margin-top: 30px; } .am-button { margin: 0 20px; height: 44px; line-height: 44px; } } @media screen and (min-device-width: 414px) { .am-page-result .am-page-result-pic { width: 180px; height: 180px; margin: 50px auto; } .am-button { margin: 0 24px; height: 50px; line-height: 50px; } } </style> <body> <div class="am-page-result"> <div class="am-page-result-pic"> <img src="data:image/png;base64,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"/> </div> <p>This is a custom error page.</p> </div> </body> <script> </script> </html>
Synchronized scrolling with native view
// Set startup parameters.
// scrollForward: 0 = SELF_ONLY, 1 = SELF_FIRST, 2 = PARENT_FIRST, 3 = PARALLEL
// scrollBackward: 0 = SELF_ONLY, 1 = SELF_FIRST, 2 = PARENT_FIRST, 3 = PARALLEL
param.set("scrollForward", 2)
param.set("scrollBackward", 1)Limiting web component width
Set the width with a startup parameter.
params.set('webWidth', xxx); // Accepts a string for a percentage (e.g., '100%') or a number for pixels (e.g., 800).Update the width dynamically.
page.setWebWidth(webWidth: string | number)
HarmonyOS web behavior customization
H5WebClientProvider
Implement H5WebClientProvider to customize the default web behavior of the HarmonyOS H5 container.
HRiver.setProvider(H5WebClientProvider.name, new H5WebClientProviderImpl())The customizable APIs are:
export class H5WebClientProvider {
// HTTP error callback
onHttpErrorReceive(page: Page | undefined, request: WebResourceRequest | undefined, response: WebResourceResponse | undefined) {
}
// Download callback
onDownloadStart(page: Page | undefined, url: string | undefined, userAgent: string | undefined, contentDisposition: string | undefined,
mimetype: string | undefined, contentLength: number | undefined) {
}
// SSL error callback
onSslErrorEventReceive(page: Page | undefined, handler: SslErrorHandler, error: SslError) {
}
// Page error callback
onErrorReceive(page: Page | undefined, request: WebResourceRequest | undefined, error: WebResourceError | undefined): boolean {
return false
}
// Full-screen enter callback
onFullScreenEnter(page: Page | undefined, handler: FullScreenExitHandler) {
}
// Full-screen exit callback
onFullScreenExit(page: Page | undefined) {
}
// Permission request callback
onPermissionRequest(page: Page | undefined, request: PermissionRequest | undefined) {
}
// Screen capture request callback
onScreenCaptureRequest(page: Page | undefined, handler: ScreenCaptureHandler | undefined) {
}
onPageBegin(page: Page | undefined, url: string | undefined) {
}
onAppear(page: Page | undefined) {
}
// Scroll callback
onScroll(x: number, y: number, page: Page | undefined) {
}
getWindow(context: Context, page: Page | undefined): Promise<window.Window> | undefined {
return undefined
}
// Context menu callback
onContextMenuShow(param: WebContextMenuParam | undefined, result: WebContextMenuResult | undefined) {
return false
}
// File selector callback
onShowFileSelector(fileSelector: FileSelectorParam, result: FileSelectorResult, page: Page | undefined) {
return false
}
// Geolocation show callback
onGeolocationShow(origin: string | undefined, geoLocation: JsGeolocation | undefined, page: Page | undefined) {
}
// Geolocation hide callback
onGeolocationHide(page: Page | undefined) {
}
}H5MixModeSettingProvider
Use H5MixModeSettingProvider to set the MixedMode for handling mixed HTTP and HTTPS web content:
HRiver.setProvider(H5MixModeSettingProvider.name, new H5MixModeSettingProviderImpl()) // Business implementation.export class H5MixModeSettingProviderImpl extends H5MixModeSettingProvider {
mixMode(page: Page | undefined): MixedMode {
// Return the appropriate MixedMode for the given page.
return MixedMode.Compatible
}
}HarmonyOS does not support loading content from an IP address via HTTP on an HTTPS page. Instead, use an HTTPS link to the IP address or an HTTP link to a domain name.
Page navigation
Page routing defaults to router mode. In HarmonyOS, this mode does not support closing a specific page in the stack; you can only return to the previous page. Alternatively, offline packages support navigation mode:
Support for closing pages in the stack.
Support for split-view mode.
Global navigation mode
Set
H5RouterNavStackProviderafter HRiver is initialized and before launching an offline package.HRiver.setProvider(H5RouterNavStackProvider.name, new NavStackProvider(this.pageInfos))import { H5RouterNavStackProvider } from '@mpaas/hriver'; export class NavStackProvider extends H5RouterNavStackProvider { navStack: NavPathStack // Global NavPathStack. constructor(navStack: NavPathStack) { super(); this.navStack = navStack; } getNavPathStack(): NavPathStack { return this.navStack } }In
navDestination, configure thebuilder, and in thebuilder, load the mPaaS global builder.import {HRBuilder, RouterUtils, HRiver, H5RouterNavStackProvider} from '@mpaas/hriver' let mPaaSHRiverBuilder: WrappedBuilder<[string, ESObject]> = wrapBuilder(HRBuilder); @Builder PagesMap(name: string, params: ESObject) { if (RouterUtils.isMPHRiverPage(name)) { mPaaSHRiverBuilder.builder(name, params) } else if (name == 'xxx') { // Pages for other services. } } build() { Navigation(this.pageInfos) { Row() { Column() { // Application page. MainPage() } .width('100%') } }.navDestination(this.PagesMap) } }
Standalone navigation mode with offline package
Simply pass NavPathStack as the third parameter to
HRiver.startApporHRiver.startUrl.See Step 2 in Global navigation mode.