第 77 期 - JSBridge 在前端 H5 与客户端 Native 交互中的原理与应用
logoFRONTALK AI/1月9日 16:31/阅读原文

摘要

文章讲述混合应用开发中混合开发模式的意义,介绍 JSBridge 作为 H5 与 Native 双向通信机制的概念、作用、重要性、实现原理,还阐述了 Native 与 Web 双向调用的方式以及 H5 端的具体实现。

一、混合开发概述

混合开发是一种使用多种开发模型(原生 Native 和 Web H5)开发 App 的模式。原生开发效率低,重新打包依赖用户更新,但性能高功能覆盖率高;Web H5 发布更新方便,跨平台性好但性能低特性受限。随着手机硬件和系统对 Web 特性支持变好,混合开发的意义在于结合两者优点。

二、JSBridge 的概念和作用

(一)通信桥梁

JSBridge 充当 Web 和原生应用间的通信桥梁,可双向通信,互相调用和传递数据。

(二)原生功能调用

在 JavaScript 中能调用原生应用功能,触发原生操作如打开相机等。

(三)数据传递

JavaScript 和原生代码间可传递复杂数据结构,如对象、数组等。

(四)回调机制

支持回调机制,原生操作完成后可通知 JavaScript 并传递结果。

三、JSBridge 的重要性

(一)跨平台开发

允许一套代码运行在不同平台,用 Web 技术开发核心逻辑,必要时调用原生功能,提高开发效率。

(二)原生功能扩展

充分利用原生平台功能,如访问硬件设备、调用系统 API,丰富应用功能提升用户体验。

(三)灵活性和扩展性

灵活可扩展地实现 Web 和原生代码通信,开发人员可按需添加原生功能并在 JavaScript 中调用。

四、JSBridge 的实现原理

将 Web 和 Native 端通信类比为 Client/Server 模式,JSBridge 类似 HTTP 协议。Native 端把原生接口封装成 JavaScript 接口并注册到全局对象;Web 端把 JavaScript 接口封装成原生接口暴露给原生代码。

五、Native 与 Web 的双向调用

(一)Native -> Web

JavaScript 是解释性语言,Native 端调用 Web 端时,可将拼接的 JavaScript 代码字符串传入 JS 解析器(webView)执行。

1. Android

Android 提供 evaluateJavascript 来执行 JS 代码并获取返回值执行回调,如:

String jsCode = String.format("window.showWebDialog('%s')", text);
webView.evaluateJavascript(jsCode, new ValueCallback<String>() {
    @Override
    public void onReceiveValue(String value) {
    }
});

2. IOS

IOS 的 WKWebView 使用 evaluateJavaScript,如:

[webView evaluateJavaScript:@"执行的 JS 代码" 
completionHandler:^(id _Nullable response, NSError * _Nullable error) {
    // 
}];

(二)Web -> Native

1. URL Schema

是类 URL 的请求格式,可自定义用于 JSBridge 通信。Native 可重写 WebView 方法拦截 Web 请求,符合自定义格式则解析调用原生方法,否则转发。如:

get existOrderRedirect() {
    let url: string;
    if (this.env.isHelloBikeApp) {
        url = 'hellobike://hellobike.com/xxxxx_xxx?from_type=xxxx & selected_tab=xxxxx';
    } else if (this.env.isSFCApp) {
        url = 'hellohitch://hellohitch.com/xxx/xxxx?bottomTab=xxxx';
    }
    return url;
}

但这种方式基于 URL,有长度、直观性、数据格式限制,建立请求耗时。

2. 在 Webview 中注入 JS API

App 将 Native 接口注入到 JS 的 Context(window)对象中,Web 端可调用原生方法。

Android

4.2 前用 addJavascriptInterface 接口有漏洞,4.2 后用@JavascriptInterface 解决兼容性问题。

IOS

UIWebView 的 JavaSciptCore 支持 iOS 7.0 及以上,WKWebView 的 WKScriptMessageHandler 支持 iOS 8.0 及以上。例如:

// 注入全局 JS 对象
webView.addJavascriptInterface(new NativeBridge(this), "NativeBridge");
class NativeBridge {
    private Context ctx;
    NativeBridge(Context ctx) {
        this.ctx = ctx;
    }
    // 绑定方法
    @JavascriptInterface
    public void showNativeDialog(String text) {
        new AlertDialog.Builder(ctx).setMessage(text).create().show();
    }
}
// 调用 nativeBridge 的方法
window.NativeBridge.showNativeDialog('hello');

六、H5 具体实现

将功能抽象为 AppBridge 类,封装交互和回调方法。

(一)调用原生方法

定义 callNative 方法在 JavaScript 中调用原生方法,根据平台调用相应原生方法。

callNative<P, R>(classMap: string, method: string, params: P): Promise<R> {
    return new Promise<R>((resolve, reject) => {
        const id = v4();
        this.__callbacks[id] = { resolve, reject, method: `${classMap} - ${method}` };
        const data = {
            classMap,
            method,
            params: params === null? '' : JSON.stringify(params),
            callbackId: id,
        };
        const dataStr = JSON.stringify(data);
        if (this.env.isIOS && isFunction(window?.webkit?.messageHandlers?.callNative?.postMessage)) {
            window.webkit.messageHandlers.callNative.postMessage(dataStr);
        } else if (this.env.isAndroid && isFunction(window?.AppFunctions?.callNative)) {
            window.AppFunctions.callNative(dataStr);
        }
    });
}

(二)回调处理

初始化桥接回调函数,重新定义 window.callBack 方法处理原生回调数据。

private initBridgeCallback() {
    const oldCallback = window.callBack;
    window.callBack = (data) => {
        if (isFunction(oldCallback)) {
            oldCallback(data);
        }
        console.info('native callback', data, data.callbackId);
        const { callbackId } = data;
        const callback = this.__callbacks[callbackId];
        if (callback) {
            if (data.code === 0) {
                callback.resolve(data.data);
            } else {
                const error = new Error(data.msg) as Error & {response:unknown};
                error.response = data;
                callback.reject(error);
            }
            delete this.__callbacks[callbackId];
        }
    };
}

(三)使用示例

包括调用原生方法、打开 webview、获取驾驶证 OCR 信息等示例。

// 调用原生方法的封装函数
callNative<P, R>(classMap: string, method: string, params: P) {
    const bridge = container.resolve<AppBridge>(AppBridge);
    const func = bridge.callNative.bind(bridge);
    return func<P, R>(classMap, method, params);
}
// 打开 webview
openWebView(url: string): Promise<void> {
    return this.callNative<{url:string}, void>('xxxxx/hitch', 'openWebview', { url });
}
// 获取驾驶证 OCR 信息
getDriverLicenseOcrInfo(
    params: HBNative.getDriverLicenseOcrInfo.Params,
): Promise<HBNative.getDriverLicenseOcrInfo.Result> {
    return this.callNative<
        HBNative.getDriverLicenseOcrInfo.Params,
        HBNative.getDriverLicenseOcrInfo.Result>(
            'xxxxx/hitch', 'getOcrInfo', params,
        );
}
 

扩展阅读

Made by 捣鼓键盘的小麦 / © 2025 Front Talk 版权所有