App与Web的通讯方式

12147

App和Web的通讯通道建立 (javascriptChannels)

在Flutter的WebView中,javascriptChannels也是一个重要的控制器。它是一个类型为Set的变量,允许Flutter应用和WebView之间进行javascript通信。该控制器用于定义自定义javascript通道,允许WebView调用Flutter代码中的指定方法并传递数据。Flutter代码可以在控制器中注册定义的通道,以便在WebView调用javascript时实现回调。这可以用于实现从JavaScript到Flutter的双向通信。通过使用javascriptChannels,开发人员可以在Flutter应用和WebView之间创建灵活的通信机制。

通信通道

  • 定义 jsChannel
  1. //代码来源:jianghuAppBrowser/jianghuBrowser/lib/common/Constants.dart
  2. static String jsChannel = "JianghuBridge";
  • 定义 JavascriptChannel,并在 onMessageReceived 中对事件做出响应。
  1. //代码来源:jianghuAppBrowser/jianghuBrowser/lib/layout/WebViewHandler.dart
  2. JavascriptChannel messagePost(BuildContext context, {Function? inputFocus}) {
  3. return JavascriptChannel(
  4. name: Constants.jsChannel, //JavascriptChannel的name指定为Constants.jsChannel(JianghuBridge),所以将通过JianghuBridge使用通信通道。
  5. onMessageReceived: (JavascriptMessage message) async { //监听消息事件
  6. Map result = jsonDecode(message.message);
  7. if (result.containsKey('action')) {
  8. //根据action判断需要做什么,如果没有定义并处理,则web端发送到app的消息是无响应的
  9. switch (result['action']) {
  10. case 'appUpdate':
  11. VersionUtil.appUpdate(result, context);
  12. break;
  13. }
  14. }
  15. },
  16. );
  17. }

App发送消息到Web

有两种发送方式:

1、runJavascript,可以传递参数,无法接收回调

  1. // web端定义一个全局方法,注册在window或直接使用function定义
  2. function onAppResumed() {
  3. //do something...
  4. }
  5. //App端调用
  6. RenderKey.controller.value?.runJavascript("onAppResumed()");

2、runJavascriptReturningResult ,可以传递参数,可以接收回调

  1. //web端定义方法
  2. getReadyState: function () {
  3. return 'complete';
  4. },
  5. //App端调用
  6. String state = await RenderKey.controller.value!.runJavascriptReturningResult("getReadyState()");

以上两种方式,传递的参数只能是字符串,如果不是字符串,也会默认转换为字符串。回调里的返回结果也是字符串。

Web发送消息到App

jianghuJsBridge.js

jianghuJsBridge.js [文件目录:/jianghuAppBrowser/jianghuBrowser/assets/jianghuJsBridge.js] 定义了一些web端可使用的方法和变量。需要先注册jianghuJsBridge.js ,注册之后这些方法和变量将挂载在web端window上,web端可以调用和使用jianghuJsBridge.js里的方法和变量。

  1. //注册jianghuJsBridge.js,
  2. //代码来源:jianghuAppBrowser/jianghuBrowser/lib/layout/WebViewHandler.dart
  3. Future<void> registerJianghuJsBridge() async {
  4. print("注册方法 registerJianghuJsBridge");
  5. String jsStr = await rootBundle.loadString("assets/jianghuJsBridge.js");
  6. RenderKey.controller.value!.runJavascript(jsStr);
  7. RenderKey.controller.value!.runJavascript("checkReadyState()");
  8. }

使用通信通道发送消息

有两种发送方式:

  1. 通过JianghuBridge.postMessage,postMessage是JavascriptChannel原生方法。postMessage只能发送字符串,如果是其他格式,需要转换一下。
  1. //JianghuBridge.postMessage 发送消息
  2. JianghuBridge.postMessage(JSON.stringify({ action: 'downloadFile', downloadPath, filename }));
  1. 通过jianghuBridgePostMessage,jianghuBridgePostMessage是在jianghuJsBridge.js中封装的一个方法,通过JianghuBridge.postMessage发送消息,对数据格式做了转换,并且注册了回调。
  1. //代码来源:/jianghuAppBrowser/jianghuBrowser/assets/jianghuJsBridge.js
  2. function jianghuBridgePostMessage(params, callback) {
  3. try {
  4. const actionTime = new Date().getTime();
  5. params.actionTime = actionTime;
  6. JianghuBridge.postMessage(`${JSON.stringify(params)}`);
  7. if (callback != null) {
  8. //注册回调,回调的具体实现见下文
  9. window.jianghuBridgeCallbackMap[`${actionTime}`] = callback;
  10. }
  11. } catch (e) {
  12. }
  13. }

回调

App端接收到web端的消息,并处理完成之后,可以调用回调方法,将web端需要的数据返回给web端。

  • 注册回调
  1. //代码来源:/jianghuAppBrowser/jianghuBrowser/assets/jianghuJsBridge.js
  2. window.jianghuBridgeCallbackMap[`${actionTime}`] = callback;
  • 执行回调
  1. //代码来源:/jianghuAppBrowser/jianghuBrowser/assets/jianghuJsBridge.js
  2. function jianghuBridgeCallback(
  3. actionTime,
  4. data,
  5. keepCallback = false
  6. ) {
  7. console.log(`[jianghuBridgeCallbackMap]${actionTime} ${data}`)
  8. if (window.jianghuBridgeCallbackMap.hasOwnProperty(`${actionTime}`)) {
  9. try {
  10. if(typeof data == 'string') {
  11. console.log(`[jianghuBridgeCallbackMap]${actionTime} ${data}`)
  12. window.jianghuBridgeCallbackMap[`${actionTime}`](JSON.parse(data));
  13. } else {
  14. console.log(`[jianghuBridgeCallbackMap]${actionTime} ${JSON.stringify(data)}`)
  15. window.jianghuBridgeCallbackMap[`${actionTime}`](data);
  16. }
  17. } catch (err) {
  18. window.jianghuBridgeCallbackMap[`${actionTime}`]();
  19. }
  20. if (keepCallback != true && keepCallback != 'true') {
  21. delete window.jianghuBridgeCallbackMap[`${actionTime}`];
  22. }
  23. }
  24. }
  • 触发回调
  1. //回调方法封装
  2. void runJsCallback(methodName, {String? message}) {
  3. if (_callBackId != null) {
  4. String method = "jianghuBridgeCallback('$_callBackId', ${jsonEncode({'action': methodName, 'message': message})}, true)";
  5. RenderKey.controller.value?.runJavascript(method);
  6. }
  7. }
  8. //回调
  9. runJsCallback('disconnected', message: "iceConnectionState disconnected");

示例

  1. //jianghuJsBridge.js 中定义变量和方法
  2. window.isBrowserApp = true;
  3. window.jianghuBridge = {
  4. forwardToChatSessionDone: (params, callBack) => {
  5. jianghuBridgePostMessage({ action: 'forwardToChatSessionDone', ...params }, callBack)
  6. },
  7. };
  8. //web端调用
  9. if(window.isBrowserApp) {
  10. window.jianghuBridge.forwardToChatSession({forwardUserId: '123'});
  11. }