接入 WebView
更新时间: 2025/01/03 17:57:37
本文介绍 Android、iOS、Windows、macOS、鸿蒙客户端通过 WebView 接入互动白板的方法。
背景说明
网易云信分别提供了互动白板和录像回放的 WebView 页面。WebView 是一个用于在移动应用或网页中显示网页内容的组件。每个 WebView 又额外提供了带虚拟控制台(vconsole
)的页面。vconsole
页面在 WebView 中挂载一个虚拟的开发者控制台,方便开发者查看开发中遇到的问题。
功能原理
互动白板 WebView 和录像回放 WebView 均通过 jsBridge
和原生客户端进行通信和数据交换。这种桥梁使得 Web 页面能够访问原生应用的功能,同时也让原生应用能够控制 Web 页面的行为。消息流如下图所示。
graph LR
classDef default fill:#337EFF,stroke:#337EFF,stroke-width:0px,color:#FFFFFF;
WebView --window.jsBridge.NativeFunctio--> 原生客户端
原生客户端 --window.WebJSBridge--> WebView
WebView 向原生客户端发送消息:
- 原生客户端在 WebView 注册
window.jsBridge.NativeFunction
方法。 - WebView 通过该方法,将消息传递给原生客户端处理。
Native 客户端向 WebView 发送消息:
- WebView 在页面上注册
window.WebJSBridge
方法。(WebView 已处理,您无需处理) - 原生客户端通过该方法,将消息传递给 WebView。
注意事项
- Windows 和 macOS 客户端推荐使用 Electron 框架接入。若使用 Qt 框架接入,集成音视频播放功能可能存在问题。
- 如果使用 Flutter 客户端接入互动白板,请将 WebView 发送给客户端的消息 注册在
window.jsBridge.postMessage
方法上,不要将消息注册在window.jsBridge.NativeFunction
方法上。 - 如果客户端无法直接在
window
对象上注册NativeFunction
,可以选择注册在window.jsBridge.NativeFunction
上。
部署 WebView
-
下载 WebView,具体请参考 下载 SDK 和 Demo。
-
在业务服务器上创建 WebView 的项目文件夹,并将解压后的 WebView 脚本文件拷贝至文件夹中。
WebView 文件夹中的主要页面说明如下:
g2/webview.html
:网易云信互动白板的 WebView 地址。g2/webview_vconsole.html
:网易云信互动白板的虚拟控制台。g2/webview.record.html
:网易云信互动白板回放的 WebView 地址。g2/webview_vconsole.record.html
:网易云信互动白板回放的虚拟控制台。
安卓接入示例
-
客户端为了接收 WebView 的数据,需要在
Web
的window
对象上注册NativeFunction
。Java
/** * 安卓接入示例 */ //MainActivity.java webView.addJavascriptInterface(new WebAppInterface(this, webView), "jsBridge"); //WebAppInterface.java public class WebAppInterface { MainActivity mContext; @JavascriptInterface public void NativeFunction(String toast) throws JSONException { JSONObject obj = new JSONObject(toast); String action = obj.getString("action"); JSONObject param = obj.getJSONObject("param"); switch (action) { case "webPageLoaded": login(); break; case "webJoinWBSucceed": enableDraw(); break; case "webLoginIMFailed": case "webJoinWBFailed": case "webCreateWBFailed": case "webLeaveWB": /** * WebView 已经退出了白板房间,客户端此时应该销毁 WebView */ destroyWebViewAndExitActivity(); break; case "webGetAuth": sendAuthInfo(); break; } } }
-
WebView 页面载入后,会在
window
上挂载WebJSBridge
对象。客户端通过WebJSBridge
向 WebView 发送消息。传给 WebView 的参数为 JSON 字符串。
Java
//WebAppInterface.java public class WebAppInterface { MainActivity mContext; webview webView; /** Instantiate the interface and set the context */ WebAppInterface(Context c, webview w) { mContext = (MainActivity)c; webView = w; } private void runJs(final String param) { final String escapedParam = param.replaceAll("\"", "\\\\\""); webView.post(new Runnable() { @Override public void run() { Log.d("native function call js", "javascript:WebJSBridge(\"" + escapedParam + "\")"); webView.loadUrl("javascript:WebJSBridge(\"" + escapedParam + "\")"); } }); } private void enableDraw() throws JSONException { JSONObject jsParam = new JSONObject(); JSONObject param = new JSONObject(); jsParam.put("action", "jsDirectCall"); jsParam.put("param", param); param.put("target", "drawPlugin"); param.put("funcName", "enableDraw"); param.put("arg1", true); runJs((jsParam.toString())); } }
鸿蒙接入示例
TypeScriptimport { webview } from '@kit.ArkWeb';
import { JSON } from '@kit.ArkTS';
import fetch, { FetchResponse } from '@system.fetch';
interface NativeFunctionInterface {
action: string;
}
interface jsJoinWBParam {
uid: number;
debug: boolean;
channelName: string;
appKey: string;
platform: string;
record: boolean;
}
interface jsSendAuthParam {
code: number;
nonce: string;
curTime: string;
checksum: string;
}
interface jsDirectCallParam {
target: string;
funcName: string;
arg1: boolean
}
interface wbResponseData {
wbAppKey : string,
wbNonce: string,
wbCheckSum: string,
wbCurtime: string,
}
interface ResponseData {
code: number,
data: wbResponseData
}
@Entry
@Component
struct Index {
controller: webview.WebviewController = new webview.WebviewController();
uid = 1314; //自定义 uid
channelName = 'test111'; //自定义 channelName
appKey = ''; //白板 appkey
checkSumUrl = ''; //鉴权地址
webviewUrl = ""; //网易云信互动白板的 webview 地址
jsJoinWB() {
const param: jsJoinWBParam = {
uid: this.uid,
debug: true,
channelName: this.channelName,
appKey: this.appKey,
platform: 'android',
record: false
}
this.controller.runJavaScript(`WebJSBridge({action: "jsJoinWB", param:${JSON.stringify(param)}})`)
}
enableDraw() {
const param: jsDirectCallParam = {
target: 'drawPlugin',
funcName: 'enableDraw',
arg1: true
}
this.controller.runJavaScript(`WebJSBridge({action: "jsDirectCall", param:${JSON.stringify(param)}})`)
}
destroyWebView() {
}
sendAuthInfo() {
const wbAppKey = this.appKey;
const roomId = this.channelName;
const uid = this.uid;
const url = this.checkSumUrl;
fetch.fetch({
url,
method: 'POST',
header: {
'content-type': 'application/json'
},
data: JSON.stringify({
roomId,
uid,
wbAppKey
}),
success: (res: FetchResponse) => {
const data = JSON.parse(res.data as string) as ResponseData
const param: jsSendAuthParam = {
code: 200,
nonce: data.data.wbNonce,
curTime:data.data.wbCurtime,
checksum:data.data.wbCheckSum,
}
this.controller.runJavaScript(`WebJSBridge({action: "jsSendAuth", param:${JSON.stringify(param)}})`)
},
fail: (data: object, code: number) => {
console.error('getCheckSum fail', JSON.stringify(data), code)
}
})
}
build() {
Column() {
Web({
src: this.webviewUrl,
controller: this.controller
}).javaScriptProxy({
object: {
NativeFunction: (channelType: string) => {
const obj = JSON.parse(channelType) as NativeFunctionInterface
switch (obj.action) {
case 'webPageLoaded':
console.log('webPageLoaded, run jsJonWB()')
this.jsJoinWB()
break;
case "webJoinWBSucceed":
this.enableDraw();
break;
case "webLoginIMFailed":
case "webJoinWBFailed":
case "webCreateWBFailed":
case "webLeaveWB":
/**
* webview 已经退出了白板房间,客户端此时应该销毁 webview
*/
this.destroyWebView();
break;
case "webGetAuth":
this.sendAuthInfo();
break;
}
},
},
name: 'jsBridge',
methodList: ['NativeFunction'],
controller: this.controller,
})
.domStorageAccess(true)
.fileAccess(true)
}
}
}
此文档是否对你有帮助?