HarmonyOS鸿蒙Next中在webview异步调用方法

HarmonyOS鸿蒙Next中在webview异步调用方法 现在希望在 webview 中调用一个鸿蒙注入到 window 上的方法,按照文档说明:https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/web-in-page-app-function-invoking#promise%E5%9C%BA%E6%99%AF

应该是可以实现的,但是实际测试中总是返回一个空对象,并没有返回 promise 对象。


更多关于HarmonyOS鸿蒙Next中在webview异步调用方法的实战教程也可以访问 https://www.itying.com/category-93-b0.html

7 回复

开发者你好,

根据提供的链接,测试是可以正常返回结果的。实际测试中总是返回一个空对象,并没有返回 promise 对象具体是指在哪一侧返回的结果呢,是打印的空对象还是调试获取到的是空对象呢。是否可以提供demo呢。

本地测试代码:

// xxx.ets
import { webview } from '@kit.ArkWeb';
import { BusinessError } from '@kit.BasicServicesKit';

class TestClass {
  constructor() {
  }

  test(): Promise<string> {
    let p: Promise<string> = new Promise((resolve, reject) => {
      setTimeout(() => {
        console.info('执行完成');
        resolve('success');
      }, 5000);
    });
    return p;
  }

  toString(param: string): void {
    console.info(' ' + param);
  }
}

@Entry
@Component
struct Index {
  webviewController: webview.WebviewController = new webview.WebviewController();
  @State testObj: TestClass = new TestClass();

  build() {
    Column() {
      Web({ src: $rawfile('test4.html'), controller: this.webviewController })
        .javaScriptProxy({
          object: this.testObj,
          name: 'testObjName',
          methodList: ['test', 'toString'],
          controller: this.webviewController,
        });
    };
  }
}
<!-- index.html -->
<!DOCTYPE html>
<html>
<head>
    <meta name="viewport" content="width=device-width, initial-scale=1" />
</head>
<body>
<button type="button" onclick="callArkTS()">Click Me!</button>
<p id="demo"></p>
<script>
    function callArkTS() {
      testObjName.test().then((param)=>{testObjName.toString(param)}).catch((param)=>{testObjName.toString(param)})
    }
</script>
</body>
</html>

更多关于HarmonyOS鸿蒙Next中在webview异步调用方法的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


关注,顶帖

鸿蒙 WebView 默认可能关闭 JS,导致注入的window方法未生效,加上.javaScriptAccess(true)试试

小伙伴你好,可以通过 javaScriptProxy 注册原生方法,当原生方法返回 Promise 时,Web 页面可以使用 .then()async/await 来处理异步结果。

详细说明

方案一:原生方法返回 Promise

方案说明:在原生侧定义返回 Promise 的方法,通过 javaScriptProxy 注册给 Web 页面调用。Web 页面可以通过 Promise 的 .then().catch()async/await 语法来处理异步结果。这种方式适合需要异步操作的原生能力调用,如文件读写、网络请求、权限检查等。

实现步骤

1. 定义返回 Promise 的原生桥接类

// Index.ets
import { webview } from '@kit.ArkWeb';
import { BusinessError } from '@kit.BasicServicesKit';

/**
 * 定义供前端调用的原生对象类
 * 支持返回 Promise 的异步方法
 */
class NativeBridge {
  /**
   * 异步获取设备信息
   * @returns Promise<string> 设备信息字符串
   */
  getDeviceInfoAsync(): Promise<string> {
    return new Promise((resolve, reject) => {
      // 模拟异步操作
      setTimeout(() => {
        try {
          const deviceInfo = 'HarmonyOS Device';
          resolve(deviceInfo);
        } catch (error) {
          reject(error);
        }
      }, 1000);
    });
  }

  /**
   * 异步检查权限
   * @param permission 权限名称
   * @returns Promise<boolean> 是否有权限
   */
  checkPermissionAsync(permission: string): Promise<boolean> {
    return new Promise((resolve, reject) => {
      // 模拟权限检查异步操作
      setTimeout(() => {
        try {
          // 实际项目中应调用权限检查 API
          const hasPermission = true;
          resolve(hasPermission);
        } catch (error) {
          reject(error);
        }
      }, 500);
    });
  }

  /**
   * 异步读取文件
   * @param filePath 文件路径
   * @returns Promise<string> 文件内容
   */
  readFileAsync(filePath: string): Promise<string> {
    return new Promise((resolve, reject) => {
      // 模拟文件读取异步操作
      setTimeout(() => {
        try {
          // 实际项目中应调用文件系统 API
          const content = 'File content';
          resolve(content);
        } catch (error) {
          reject(error);
        }
      }, 2000);
    });
  }
}

@Entry
@Component
struct WebComponent {
  controller: webview.WebviewController = new webview.WebviewController();
  // 创建原生桥接对象
  nativeBridge: NativeBridge = new NativeBridge();

  aboutToAppear() {
    // 注册原生对象供前端调用
    this.controller.registerJavaScriptProxy({
      object: this.nativeBridge,
      name: 'nativeBridge',
      methodList: ['getDeviceInfoAsync', 'checkPermissionAsync', 'readFileAsync'],
      controller: this.controller
    });
  }

  build() {
    Column() {
      Web({ src: $rawfile('index.html'), controller: this.controller })
        .width('100%')
        .height('100%')
    }
  }
}

2. Web 页面调用 Promise 方法

在 Web 页面中,可以通过多种方式调用返回 Promise 的原生方法:

方式一:使用 .then().catch()

// 前端 JavaScript 代码
// 调用异步获取设备信息
nativeBridge.getDeviceInfoAsync()
  .then((deviceInfo) => {
    console.log('设备信息:', deviceInfo);
    // 处理成功结果
  })
  .catch((error) => {
    console.error('获取设备信息失败:', error);
    // 处理错误
  });

// 调用异步权限检查
nativeBridge.checkPermissionAsync('camera')
  .then((hasPermission) => {
    if (hasPermission) {
      console.log('有权限');
    } else {
      console.log('无权限');
    }
  })
  .catch((error) => {
    console.error('权限检查失败:', error);
  });

方式二:使用 async/await(推荐)

// 前端 JavaScript 代码
async function handleDeviceInfo() {
  try {
    const deviceInfo = await nativeBridge.getDeviceInfoAsync();
    console.log('设备信息:', deviceInfo);
    // 处理成功结果
  } catch (error) {
    console.error('获取设备信息失败:', error);
    // 处理错误
  }
}

async function handlePermissionCheck() {
  try {
    const hasPermission = await nativeBridge.checkPermissionAsync('camera');
    if (hasPermission) {
      console.log('有权限');
    } else {
      console.log('无权限');
    }
  } catch (error) {
    console.error('权限检查失败:', error);
  }
}

// 调用异步函数
handleDeviceInfo();
handlePermissionCheck();

方式三:链式调用多个 Promise

// 前端 JavaScript 代码
// 顺序执行多个异步操作
nativeBridge.checkPermissionAsync('camera')
  .then((hasPermission) => {
    if (hasPermission) {
      return nativeBridge.getDeviceInfoAsync();
    } else {
      throw new Error('无权限');
    }
  })
  .then((deviceInfo) => {
    console.log('设备信息:', deviceInfo);
    return nativeBridge.readFileAsync('/path/to/file');
  })
  .then((fileContent) => {
    console.log('文件内容:', fileContent);
  })
  .catch((error) => {
    console.error('操作失败:', error);
  });

方式四:并行执行多个 Promise

// 前端 JavaScript 代码
// 使用 Promise.all 并行执行多个异步操作
Promise.all([
  nativeBridge.getDeviceInfoAsync(),
  nativeBridge.checkPermissionAsync('camera'),
  nativeBridge.readFileAsync('/path/to/file')
])
  .then((results) => {
    const [deviceInfo, hasPermission, fileContent] = results;
    console.log('设备信息:', deviceInfo);
    console.log('权限状态:', hasPermission);
    console.log('文件内容:', fileContent);
  })
  .catch((error) => {
    console.error('操作失败:', error);
  });

关键 API 说明


方案二:使用回调函数处理异步结果

方案说明:如果不想使用 Promise,也可以通过回调函数的方式处理异步结果。原生方法接收回调函数作为参数,在异步操作完成后调用回调函数返回结果。

代码示例

// Index.ets
import { webview } from '@kit.ArkWeb';

/**
 * 使用回调函数处理异步结果的原生桥接类
 */
class NativeBridgeWithCallback {
  /**
   * 异步获取设备信息(回调方式)
   * @param callback 回调函数,接收设备信息字符串
   */
  getDeviceInfoWithCallback(callback: (deviceInfo: string) => void): void {
    // 模拟异步操作
    setTimeout(() => {
      const deviceInfo = 'HarmonyOS Device';
      callback(deviceInfo);
    }, 1000);
  }

  /**
   * 异步操作(支持成功和失败回调)
   * @param onSuccess 成功回调
   * @param onError 失败回调
   */
  asyncOperationWithCallbacks(
    onSuccess: (result: string) => void,
    onError: (error: string) => void
  ): void {
    setTimeout(() => {
      try {
        const result = '操作成功';
        onSuccess(result);
      } catch (error) {
        onError('操作失败');
      }
    }, 1000);
  }
}

@Entry
@Component
struct WebComponent {
  controller: webview.WebviewController = new webview.WebviewController();
  nativeBridge: NativeBridgeWithCallback = new NativeBridgeWithCallback();

  aboutToAppear() {
    this.controller.registerJavaScriptProxy({
      object: this.nativeBridge,
      name: 'nativeBridge',
      methodList: ['getDeviceInfoWithCallback', 'asyncOperationWithCallbacks'],
      controller: this.controller
    });
  }

  build() {
    Column() {
      Web({ src: $rawfile('index.html'), controller: this.controller })
        .width('100%')
        .height('100%')
    }
  }
}

前端调用示例

// 前端 JavaScript 代码
// 使用回调函数
nativeBridge.getDeviceInfoWithCallback((deviceInfo) => {
  console.log('设备信息:', deviceInfo);
});

// 使用成功和失败回调
nativeBridge.asyncOperationWithCallbacks(
  (result) => {
    console.log('操作成功:', result);
  },
  (error) => {
    console.error('操作失败:', error);
  }
);

参考文档

我看你找的这个官方文档写的很详细,还有Demo可以参考,应该没问题的。

要不你多贴点代码,帮你分析分析

在HarmonyOS Next中,Web组件通过postMessage与前端页面进行异步通信。前端通过window.chrome.webview.postMessage发送消息,鸿蒙应用侧在Web组件上监听onMessageEvent事件接收并处理。应用侧也可通过postMessage向Web页面发送数据,前端通过window.onmessage监听。这是一种基于消息的事件驱动异步交互机制。

在HarmonyOS Next的WebView中异步调用注入方法时,如果返回空对象而非Promise,通常是由于注入方法未正确返回Promise或通信机制存在配置问题。

请按以下步骤排查:

  1. 确认注入方法正确性:确保ArkTS侧通过registerJavaScriptProxy()注入的方法返回一个Promise对象。例如:

    // ArkTS侧
    webview.registerJavaScriptProxy({
      myAsyncMethod: () => {
        return new Promise((resolve) => {
          setTimeout(() => resolve("data"), 1000);
        });
      }
    }, "namespace");
    
  2. 检查Web侧调用方式:在Web页面中通过window.namespace.myAsyncMethod()调用后,需使用.then()await处理Promise:

    // Web侧
    window.namespace.myAsyncMethod().then(data => console.log(data));
    
  3. 验证通信开关:确保WebView已启用JavaScript调用ArkTS的能力:

    webviewController.enableJavaScriptProxy(true);
    
  4. 排查Promise链断裂:若注入方法涉及异步操作(如网络请求),需确保Promise链完整,无未捕获的异常。

若问题仍存在,可检查WebView初始化时机,确保注入在页面加载前完成(如onPageBegin回调中注入),并确认HarmonyOS SDK版本已支持Promise类型返回。

回到顶部