Skip to content

MainPageController 与相关 API(鸿蒙)

本文详细介绍与 JSViewMainPage 配套的公开控制与扩展能力:

  • MainPageController
  • AppLauncher / DefaultAppLauncher
  • JSBridge
  • 加载状态监听回调中的 JSViewItem,以及控制用的 JSViewItemController

均从 @shijiu/jsview-app 引入。页面级放置方式见 鸿蒙集成指南

1. 关系总览

text
宿主页面
  └── JSViewMainPage
        ├── controller: MainPageController   ← 宿主持有并操控
        └── appLauncher?: AppLauncher        ← JS 侧拉起应用时回调宿主

MainPageController
  ├── loadParams / addJavascriptInterface / addListener ...
  └── getTopJSViewItem() → JSViewItemController   ← 运行时控制当前小程序

JSViewRunTimeListener
  ├── onStartLoad(item: JSViewItem)
  ├── onLoadSuccess(item: JSViewItem)
  └── onLoadFail(item?: JSViewItem)
        └── item.getParams() → 当前 MiniAppParams

注意两类对象不要混用:

类型常见来源用途
JSViewItem加载状态监听回调参数组件实例,主要查 getParams()
JSViewItemControllercontroller.getTopJSViewItem(),以及 JSBridge.onJsViewBind控制向 JS 发事件、按键、evalJavaScript

2. MainPageController

MainPageController 继承 JSViewContainController,是宿主操作 JSViewMainPage 的入口。

typescript
import { MainPageController } from '@shijiu/jsview-app';

const controller = new MainPageController();

须在 JSViewMainPage({ controller }) 中传入后,再调用下列能力(组件 aboutToAppear 会把内部实现挂到该实例上)。

2.1 API 一览

方法说明
loadParams(params: string | MiniAppParams)解析并加载 / 切换小程序
addJavascriptInterface(bridge: JSBridge)注册公开 Bridge(按 bridgeName 去重)
getBridges(): JSBridge[]已注册 Bridge 列表
addListener(listener: JSViewRunTimeListener)增加加载状态监听
removeListener(listener: JSViewRunTimeListener)移除监听
getTopJSViewItem(): JSViewItemController | undefined当前顶层小程序控制器
sendKeyCodeToJs(keyEvent: KeyEvent): boolean将按键转给 JS
onAbilityForeground()Ability 回到前台时调用
onAbilityBackground()Ability 退到后台时调用
openMiniApp(params: MiniAppParams)内部打开(由容器挂接;一般宿主用 loadParams 即可)

2.2 loadParams

typescript
// URL(可带 query)
controller.loadParams('https://example.com/js/main.jsv.mjs?startImg=https://cdn/.../s.png');

// MiniAppParams
import { MiniAppParams } from '@shijiu/jsview-app';
const p = new MiniAppParams();
p.url = 'https://example.com/js/main.jsv.mjs';
p.appName = 'demo.app';
p.startUpImg = 'https://example.com/startup.png';
controller.loadParams(p);

要点:

  • 传入 string 时按默认 Parser 解析;传入 MiniAppParams 时会在解析结果上 appendParams
  • 与上次相同的 url:跳过加载。
  • launchMode === 1appName 与当前相同:不重载页面,向当前实例 emitEvent("onNewIntent", { url })
  • 须在 JSViewMainPage 已挂载(aboutToAppear 之后)调用;首屏也可改用组件的 defaultParams

string 形态(含 jsvconfig / jsvappid 等)与 Want 转 MiniAppParams 详见: MiniAppParams 入口与解析

2.3 前后台

typescript
// Ability.onForeground
controller.onAbilityForeground();

// Ability.onBackground
controller.onAbilityBackground();

内部转发到当前 JSViewItemController,用于 Surface / 引擎暂停与恢复。

2.4 其它控制

typescript
// 顶层控制器(加载成功后再取更稳妥)
const itemCtrl = controller.getTopJSViewItem();
itemCtrl?.evalJavaScript('console.log("from native")');
itemCtrl?.emitEvent('customEvent', { foo: 'bar' });
itemCtrl?.sendKeyCodeToJs(keyEvent);

3. AppLauncher

小程序通过内置运行时 Bridge(如 openMiniApp / openMiniAppInNewTab)请求拉起其它应用时,会回调宿主提供的 AppLauncher

接口定义(源码):

typescript
export interface AppLauncher {
  openMiniApp(params: MiniAppParams): boolean
  openMiniAppInNewTab(params: MiniAppParams): Promise<boolean>
  openNativeApp(want: Want): boolean
}

包导出为 DefaultAppLauncher。业务侧请 继承 DefaultAppLauncher 覆盖需要的方法,再传给 JSViewMainPage

typescript
import { DefaultAppLauncher, MiniAppParams, MainPageController } from '@shijiu/jsview-app';
import { Want, common } from '@kit.AbilityKit';

class MyAppLauncher extends DefaultAppLauncher {
  constructor(controller?: MainPageController) {
    super(controller);
  }

  // 当前页内打开(Default 实现会转调 openMiniAppInNewTab)
  openMiniApp(params: MiniAppParams): boolean {
    // 例如:本页复用 controller.loadParams
    // this.mainPageController?.loadParams(params);
    return true;
  }

  // 新 Ability / 新窗口打开
  async openMiniAppInNewTab(params: MiniAppParams): Promise<boolean> {
    const want: Want = {
      bundleName: 'com.example.app',
      abilityName: 'SubAbility',
      parameters: {
        URL: params.url ?? '',
      },
    };
    if (params.startUpImg) {
      want.parameters!['STARTIMG'] = params.startUpImg;
    }
    const context = getContext(this) as common.UIAbilityContext;
    await context.startAbility(want);
    return true;
  }

  openNativeApp(want: Want): boolean {
    // 打开任意原生 Ability;默认实现返回 false
    return false;
  }
}

挂到页面:

typescript
JSViewMainPage({
  controller: this.controller,
  defaultParams: '...',
  appLauncher: new MyAppLauncher(this.controller),
})

不传时,JSViewMainPage 会创建 DefaultAppLauncher

方法默认行为
openMiniApp转调 openMiniAppInNewTab,并返回 true
openMiniAppInNewTab返回 false(不实际打开)
openNativeApp返回 false

因此若业务需要「JS 里开第二个小程序 / 原生页」,必须自行实现 AppLauncher,否则对应 JS API 会失败。

调用链简图:

text
JS: jJsvRuntimeBridge.openMiniApp / openMiniAppInNewTab / ...
  → RuntimeBridge
  → JSViewAppPlayer(callback)
  → AppLauncher.openMiniApp / openMiniAppInNewTab / openNativeApp

4. JSBridge

宿主向当前小程序注入可被 JS 调用的原生对象。公开注册入口:

typescript
controller.addJavascriptInterface(bridge: JSBridge)

4.1 接口字段

typescript
export interface JSBridge {
  bridgeName: string
  syncMethodList: string[]
  asyncMethodList?: string[]
  onJsViewBind: (jsView: JSViewItemController) => object
  onJsViewUnbind?: (jsView: JSViewItemController) => void
}
字段必填说明
bridgeNameJS 侧对象名;同名重复注册会被忽略并打日志
syncMethodList同步方法名。JS 调用会等待返回值,适合立刻能算出结果的接口
asyncMethodList异步方法名。JS 侧按异步调度,实现应为 async 并返回 Promise(或引擎可识别的异步结果),不要写成普通同步 return
onJsViewBind小程序绑定前调用,返回要挂到 JS 的对象;参数为当前 JSViewItemController
onJsViewUnbind卸载 / 重新加载前调用,用于释放监听、定时器等

方法名必须写进对应列表,且与 onJsViewBind 返回对象上的函数名一致。同步方法放 syncMethodList,异步方法放 asyncMethodList,不要混用。

4.2 注册时机

loadParams / defaultParams 触发加载 之前 注册,例如在页面 aboutToAppear

typescript
aboutToAppear() {
  this.controller.addJavascriptInterface(this.buildBridge());
}

每次真正加载时,容器会把已注册列表 setBridgesJSViewItem,再对每个 bridge 调用 onJsViewBind,把返回对象注入引擎。重新 loadParams 会先 onJsViewUnbind(若有),再重新 bind。

4.3 示例

typescript
import {
  JSBridge,
  JSViewItemController,
  MainPageController,
} from '@shijiu/jsview-app';

function buildDemoBridge(): JSBridge {
  return {
    bridgeName: 'demoBridge',
    syncMethodList: ['ping', 'getToken'],
    asyncMethodList: ['fetchData'],
    onJsViewBind: (jsView: JSViewItemController): object => {
      return {
        // syncMethodList
        ping: (): string => 'pong',
        getToken: (): string => 'native-token',

        // asyncMethodList:用 async / Promise,不要写成同步 return
        fetchData: async (): Promise<string> => {
          // 模拟异步(网络、磁盘等)
          // 需要时也可 jsView.emitEvent(...) 主动推给 JS
          return '{"ok":true}';
        },
      };
    },
    onJsViewUnbind: (_jsView: JSViewItemController): void => {
      // 清理
    },
  };
}

controller.addJavascriptInterface(buildDemoBridge());

JS 侧通过 bridgeName 访问(具体全局挂载名以运行时约定为准,一般为注入的 Bridge 名)。

4.4 与内置 RuntimeBridge 的区别

引擎已内置 jJsvRuntimeBridge(设备信息、开窗、关页等),宿主无需也无法用本文 JSBridge 去替换它。 JSBridge 只用于业务自定义扩展。拉起其它应用请走上一节 AppLauncher,不要在自定义 Bridge 里重复造未公开的底层接口。

5. 加载状态监听与 JSViewItem

5.1 JSViewRunTimeListener

typescript
export interface JSViewRunTimeListener {
  onStartLoad: (item: JSViewItem) => void
  onLoadSuccess: (item: JSViewItem) => void
  onLoadFail: (item?: JSViewItem) => void
}
回调时机item
onStartLoad开始 setSrc / start有,当前 JSViewItem
onLoadSuccess小程序加载成功(含 JS 通知 page loaded)
onLoadFail加载超时等失败;解析失败时也可能无 item可能为 undefined
typescript
import {
  JSViewItem,
  JSViewRunTimeListener,
  MainPageController,
} from '@shijiu/jsview-app';

const listener: JSViewRunTimeListener = {
  onStartLoad: (item: JSViewItem): void => {
    const params = item.getParams();
    console.info('start', params?.url, params?.appName);
  },
  onLoadSuccess: (item: JSViewItem): void => {
    const params = item.getParams();
    console.info('success', params?.url);
  },
  onLoadFail: (item?: JSViewItem): void => {
    console.error('fail', item?.getParams()?.url);
  },
};

controller.addListener(listener);
// 页面销毁:
controller.removeListener(listener);

JSViewMainPage 内部也会注册自己的 listener(启动图、失败弹窗)。业务 listener 与之并存,互不替代。

5.2 回调里的 JSViewItem

监听拿到的是 组件实例 JSViewItem,公开能力主要是:

方法说明
getParams(): MiniAppParams | undefined当前加载使用的参数(含 url、启动图、appName 等)

不要把它当成页面控制器去调 loadParams / evalJavaScript。需要控制运行中的小程序时,用:

typescript
const itemCtrl = controller.getTopJSViewItem(); // JSViewItemController

5.3 JSViewItemController(控制面)

getTopJSViewItem()JSBridge.onJsViewBind(jsView) 获得。

方法说明
getParams()同组件侧,取当前 MiniAppParams
emitEvent(key, event)向 JS 派发事件
evalJavaScript(script)执行一段 JS
sendKeyCodeToJs(keyEvent)转发按键
loadParams(params)底层直接加载(通常走 MainPageController.loadParams,带解析与去重)

示例:加载成功后再 eval:

typescript
onLoadSuccess: (item: JSViewItem): void => {
  const url = item.getParams()?.url;
  controller.getTopJSViewItem()?.evalJavaScript(
    `console.log("native saw load success: ${url}")`
  );
},

6. 推荐接入顺序

  1. const controller = new MainPageController()
  2. controller.addJavascriptInterface(...)(如有 Bridge)
  3. controller.addListener(...)(如需状态)
  4. 构建页面:JSViewMainPage({ controller, appLauncher?, defaultParams? })
  5. 需要动态切换时:controller.loadParams(...)
  6. Ability 前后台:onAbilityForeground / onAbilityBackground
  7. 页面销毁:removeListener

7. 相关文档

文档内容
鸿蒙集成指南HAR 依赖、权限、JSViewMainPage 放置与参数
MiniAppParams 入口与解析loadParams(string) 的 scheme、Want → MiniAppParams
本文MainPageControllerAppLauncherJSBridgeJSViewItem / JSViewItemController 细节