H5 与 WebView 的双向通信实现详解

背景介绍

在混合开发中,H5 页面与原生应用的通信是一个常见需求。本文将详细介绍如何实现 H5 与 React Native WebView 之间的双向通信机制。

核心实现

首先,我们需要在 H5 端实现一个回调管理器,用于处理与原生端的通信:

1. H5 端完整实现

// 回调管理器
window.NativeCallbacks = {
  callbacks: {},
  
  register: function(type, successCallback, errorCallback) {
    const callbackId = type + Date.now().toString();
    
    // Promise方式
    if (!successCallback && !errorCallback) {
      const promise = new Promise((resolve, reject) => {
        this.callbacks[callbackId] = {
          success: resolve,
          error: reject,
        };
      });
      return {
        promise,
        callbackId
      };
    }
    
    // 回调方式
    this.callbacks[callbackId] = {
      success: successCallback, 
      error: errorCallback,
    };
    return callbackId;
  },

  execute: function(callbackId, type, data) {
    const callback = this.callbacks[callbackId];
    if (callback) {
      if (type === 'success') {
        callback.success && callback.success(data);
      } else {
        callback.error && callback.error(data);
      }
      delete this.callbacks[callbackId]; // 执行后清理回调
    }
  }
};

// 原生通信接口封装
window.Native = {
  // 回调方式
  getUserInfo: function(successCallback, errorCallback) {
    const callbackId = ntalkCallbacks.register('getUserInfo', successCallback, errorCallback);
    window.ReactNativeWebView.postMessage(JSON.stringify({
      type: 'getUserInfo',
      callbackId,
      data: null
    }));
  },

  // Promise方式
  getUserInfoAsync: async function() {
    const { promise, callbackId } = ntalkCallbacks.register('getUserInfo');
    window.ReactNativeWebView.postMessage(JSON.stringify({
      type: 'getUserInfo',
      callbackId,
      data: null
    }));
    return promise;
  }
};

2. React Native 端完整实现

interface MessageData {
  type: string;
  callbackId: string;
  data: any;
}

class WebViewManager {
  private webview: WebView;

  constructor(webview: WebView) {
    this.webview = webview;
  }

  handleMessage(message: string) {
    try {
      const data: MessageData = JSON.parse(message);
      const { type, callbackId, data: payload } = data;
      
      switch(type) {
        case 'getUserInfo':
          this.handleGetUserInfo(callbackId, payload);
          break;
        case 'setAppCacheValue':
          this.handleSetAppCache(callbackId, payload);
          break;
        default:
          this.sendCallback(callbackId, 'error', '未知的消息类型');
      }
    } catch (error) {
      console.error('消息处理错误:', error);
    }
  }

  sendCallback(callbackId: string, type: 'success' | 'error', data: any) {
    const script = `
      window.ntalkCallbacks.execute(
        '${callbackId}',
        '${type}',
        ${JSON.stringify(data)}
      );
    `;
    this.webview.injectJavaScript(script);
  }

  private handleGetUserInfo(callbackId: string, payload: any) {
    try {
      // 实际项目中从本地存储或状态管理中获取
      const userInfo = {
        userId: '123',
        name: '张三',
        avatar: 'https://example.com/avatar.png'
      };
      this.sendCallback(callbackId, 'success', userInfo);
    } catch (error) {
      this.sendCallback(callbackId, 'error', error.message);
    }
  }

  private handleSetAppCache(callbackId: string, payload: any) {
    try {
      const { key, value } = payload;
      // 实际项目中进行缓存操作
      this.sendCallback(callbackId, 'success', true);
    } catch (error) {
      this.sendCallback(callbackId, 'error', error.message);
    }
  }
}

// WebView 组件使用示例
function MyWebView() {
  const webViewRef = useRef<WebView>(null);
  const webViewManager = useRef<WebViewManager>();

  useEffect(() => {
    if (webViewRef.current) {
      webViewManager.current = new WebViewManager(webViewRef.current);
    }
  }, []);

  return (
    <WebView
      ref={webViewRef}
      source={{ uri: 'https://example.com' }}
      onMessage={(event) => {
        webViewManager.current?.handleMessage(event.nativeEvent.data);
      }}
    />
  );
}

3. 使用示例

// 示例1:回调方式
Native.getUserInfo(
  (userInfo) => {
    console.log('获取用户信息成功:', userInfo);
  },
  (error) => {
    console.error('获取用户信息失败:', error);
  }
);

// 示例2:Promise方式
async function getUserData() {
  try {
    const userInfo = await ntalkNative.getUserInfoAsync();
    console.log('获取用户信息成功:', userInfo);
  } catch (error) {
    console.error('获取用户信息失败:', error);
  }
}

通信流程图解

H5原生1. 调用 Native 方法2. 注册回调函数3. 发送 postMessage4. 处理业务逻辑5. 注入回调脚本6. 执行回调并清理H5原生
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值