桥接机制 #

一、桥接概述 #

1.1 什么是桥接 #

桥接(Bridge)是Capacitor的核心机制,它负责在JavaScript代码和原生平台代码之间建立通信通道。通过桥接,Web应用可以调用原生功能,原生代码也可以向JavaScript发送事件。

text
┌─────────────────────────────────────────────────────────────┐
│                         Bridge                               │
│                                                              │
│    JavaScript  ◀──────────────────────▶  Native Code        │
│                                                              │
│    - 调用原生方法                            - 执行原生功能   │
│    - 接收原生事件                            - 返回结果       │
│    - 传递参数                                - 发送事件       │
│                                                              │
└─────────────────────────────────────────────────────────────┘

1.2 桥接类型 #

类型 方向 用途
方法调用 JS → Native 调用原生功能
结果返回 Native → JS 返回调用结果
事件通知 Native → JS 原生事件通知
回调触发 Native → JS 异步回调

二、通信协议 #

2.1 消息格式 #

typescript
// 调用请求格式
interface BridgeCall {
    pluginId: string;      // 插件标识
    methodName: string;    // 方法名称
    options: any;          // 调用参数
    callbackId?: string;   // 回调标识
}

// 响应格式
interface BridgeResponse {
    callbackId?: string;   // 回调标识
    success: boolean;      // 是否成功
    data?: any;            // 返回数据
    error?: {              // 错误信息
        message: string;
        code?: string;
    };
}

// 事件格式
interface BridgeEvent {
    pluginId: string;      // 插件标识
    eventName: string;     // 事件名称
    data: any;             // 事件数据
}

2.2 序列化 #

typescript
// 参数序列化
function serialize(options: any): string {
    // 处理特殊类型
    const processed = JSON.stringify(options, (key, value) => {
        // 处理Date对象
        if (value instanceof Date) {
            return { __type: 'Date', value: value.toISOString() };
        }
        // 处理ArrayBuffer
        if (value instanceof ArrayBuffer) {
            return { __type: 'ArrayBuffer', value: arrayBufferToBase64(value) };
        }
        return value;
    });
    
    return processed;
}

// 反序列化
function deserialize(json: string): any {
    return JSON.parse(json, (key, value) => {
        if (value?.__type === 'Date') {
            return new Date(value.value);
        }
        if (value?.__type === 'ArrayBuffer') {
            return base64ToArrayBuffer(value.value);
        }
        return value;
    });
}

三、iOS桥接实现 #

3.1 WKWebView消息处理 #

swift
// iOS桥接核心
class CapacitorBridge: NSObject, WKScriptMessageHandler {
    private var webView: WKWebView!
    private var messageHandlerName = "capacitorBridge"
    
    func setupWebView() {
        // 注册消息处理器
        let contentController = webView.configuration.userContentController
        contentController.add(self, name: messageHandlerName)
        
        // 注入Capacitor运行时
        let capacitorJS = """
        window.Capacitor = {
            nativeCallback: function(pluginName, methodName, options, callback) {
                window.webkit.messageHandlers.capacitorBridge.postMessage({
                    pluginName: pluginName,
                    methodName: methodName,
                    options: options,
                    callbackId: callback ? this.generateCallbackId() : null
                });
            }
        };
        """
        let script = WKUserScript(source: capacitorJS, 
                                  injectionTime: .atDocumentStart, 
                                  forMainFrameOnly: true)
        contentController.addUserScript(script)
    }
    
    // 接收来自JavaScript的消息
    func userContentController(_ userContentController: WKUserContentController, 
                               didReceive message: WKScriptMessage) {
        guard let body = message.body as? [String: Any] else { return }
        
        let pluginName = body["pluginName"] as? String ?? ""
        let methodName = body["methodName"] as? String ?? ""
        let options = body["options"] as? [String: Any] ?? [:]
        let callbackId = body["callbackId"] as? String
        
        // 调用插件方法
        handlePluginCall(pluginName: pluginName, 
                        methodName: methodName, 
                        options: options, 
                        callbackId: callbackId)
    }
}

3.2 插件调用处理 #

swift
extension CapacitorBridge {
    func handlePluginCall(pluginName: String, 
                         methodName: String, 
                         options: [String: Any], 
                         callbackId: String?) {
        
        // 获取插件实例
        guard let plugin = plugins[pluginName] else {
            sendError(callbackId: callbackId, 
                     error: "Plugin not found: \(pluginName)")
            return
        }
        
        // 执行方法
        do {
            let result = try plugin.execute(methodName: methodName, options: options)
            
            // 返回结果
            if let callbackId = callbackId {
                sendSuccess(callbackId: callbackId, data: result)
            }
        } catch {
            sendError(callbackId: callbackId, error: error.localizedDescription)
        }
    }
    
    // 发送成功结果到JavaScript
    func sendSuccess(callbackId: String, data: Any?) {
        let js = """
        window.Capacitor.handleCallback('\(callbackId)', true, \(data ?? "null"));
        """
        webView.evaluateJavaScript(js)
    }
    
    // 发送错误到JavaScript
    func sendError(callbackId: String, error: String) {
        let js = """
        window.Capacitor.handleCallback('\(callbackId)', false, { message: '\(error)' });
        """
        webView.evaluateJavaScript(js)
    }
}

3.3 事件发送 #

swift
extension CapacitorBridge {
    // 从原生向JavaScript发送事件
    func sendEvent(pluginName: String, eventName: String, data: [String: Any]) {
        let jsonData = try? JSONSerialization.data(withJSONObject: data)
        let jsonString = jsonData.flatMap { String(data: $0, encoding: .utf8) } ?? "{}"
        
        let js = """
        window.Capacitor.triggerEvent('\(pluginName)', '\(eventName)', \(jsonString));
        """
        
        DispatchQueue.main.async {
            self.webView.evaluateJavaScript(js)
        }
    }
}

// 使用示例
class NetworkPlugin: CAPPlugin {
    func notifyNetworkChange(connected: Bool) {
        bridge?.sendEvent(
            pluginName: "Network",
            eventName: "networkStatusChange",
            data: ["connected": connected]
        )
    }
}

四、Android桥接实现 #

4.1 WebView JavaScript接口 #

java
// Android桥接核心
public class CapacitorBridge {
    private WebView webView;
    private static final String BRIDGE_NAME = "CapacitorAndroidBridge";
    
    public void setupWebView(WebView webView) {
        this.webView = webView;
        
        // 启用JavaScript
        webView.getSettings().setJavaScriptEnabled(true);
        
        // 添加JavaScript接口
        webView.addJavascriptInterface(new BridgeInterface(), BRIDGE_NAME);
        
        // 注入Capacitor运行时
        String js = "window.Capacitor = {" +
            "nativeCallback: function(pluginName, methodName, options, callback) {" +
            "  var result = window." + BRIDGE_NAME + ".callPlugin(" +
            "    pluginName, methodName, JSON.stringify(options));" +
            "  return JSON.parse(result);" +
            "}" +
            "};";
        webView.evaluateJavascript(js, null);
    }
    
    // JavaScript接口类
    private class BridgeInterface {
        @JavascriptInterface
        public String callPlugin(String pluginName, String methodName, String optionsJson) {
            try {
                JSONObject options = new JSONObject(optionsJson);
                Plugin plugin = plugins.get(pluginName);
                
                if (plugin == null) {
                    return errorResponse("Plugin not found: " + pluginName);
                }
                
                JSONObject result = plugin.execute(methodName, options);
                return successResponse(result);
                
            } catch (Exception e) {
                return errorResponse(e.getMessage());
            }
        }
    }
}

4.2 异步调用处理 #

java
public class CapacitorBridge {
    private Map<String, PluginCall> savedCalls = new HashMap<>();
    
    // 保存异步调用
    public void saveCall(PluginCall call) {
        savedCalls.put(call.getCallbackId(), call);
    }
    
    // 获取保存的调用
    public PluginCall getSavedCall(String callbackId) {
        return savedCalls.get(callbackId);
    }
    
    // 释放保存的调用
    public void releaseCall(PluginCall call) {
        savedCalls.remove(call.getCallbackId());
    }
    
    // 异步返回结果
    public void resolveCall(PluginCall call, JSObject data) {
        String js = String.format(
            "window.Capacitor.handleCallback('%s', true, %s);",
            call.getCallbackId(),
            data.toString()
        );
        
        webView.post(() -> webView.evaluateJavascript(js, null));
        releaseCall(call);
    }
}

// 插件异步调用示例
public class CameraPlugin extends Plugin {
    @PluginMethod
    public void getPhoto(PluginCall call) {
        // 保存调用以便后续返回结果
        bridge.saveCall(call);
        
        // 启动相机
        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        startActivityForResult(intent, CAMERA_REQUEST_CODE);
    }
    
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == CAMERA_REQUEST_CODE) {
            PluginCall call = bridge.getSavedCall(callId);
            
            JSObject result = new JSObject();
            result.put("dataUrl", imageDataUrl);
            
            bridge.resolveCall(call, result);
        }
    }
}

4.3 事件发送 #

java
public class CapacitorBridge {
    // 发送事件到JavaScript
    public void sendEvent(String pluginName, String eventName, JSObject data) {
        String js = String.format(
            "window.Capacitor.triggerEvent('%s', '%s', %s);",
            pluginName, eventName, data.toString()
        );
        
        // 确保在UI线程执行
        webView.post(() -> webView.evaluateJavascript(js, null));
    }
}

// 使用示例
public class NetworkPlugin extends Plugin {
    private BroadcastReceiver networkReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            boolean connected = isNetworkConnected();
            
            JSObject data = new JSObject();
            data.put("connected", connected);
            
            bridge.sendEvent("Network", "networkStatusChange", data);
        }
    };
}

五、JavaScript桥接接口 #

5.1 核心接口 #

typescript
// Capacitor核心对象
interface CapacitorGlobal {
    // 平台标识
    platform: 'ios' | 'android' | 'web';
    isNative: boolean;
    
    // 插件注册
    registerPlugin<T>(pluginName: string, options?: PluginRegistrationOptions): T;
    
    // 原生调用
    nativeCallback(
        pluginName: string,
        methodName: string,
        options?: any,
        callback?: (result: any) => void
    ): void;
    
    // 原生Promise调用
    nativePromise<T>(
        pluginName: string,
        methodName: string,
        options?: any
    ): Promise<T>;
    
    // 事件触发
    triggerEvent(pluginName: string, eventName: string, data: any): void;
    
    // 回调处理
    handleCallback(callbackId: string, success: boolean, data: any): void;
    
    // 事件监听
    addEventListener(eventName: string, listener: Function): void;
    removeEventListener(eventName: string, listener: Function): void;
    
    // 已注册插件
    Plugins: { [pluginName: string]: any };
}

declare global {
    interface Window {
        Capacitor: CapacitorGlobal;
    }
}

5.2 插件代理实现 #

typescript
// 插件代理
class PluginProxy implements ProxyHandler<any> {
    private pluginName: string;
    
    constructor(pluginName: string) {
        this.pluginName = pluginName;
    }
    
    get(target: any, property: string) {
        // 返回方法调用函数
        return async (...args: any[]) => {
            const options = args[0] || {};
            
            return new Promise((resolve, reject) => {
                // 调用原生方法
                window.Capacitor.nativeCallback(
                    this.pluginName,
                    property,
                    options,
                    (result: any) => {
                        if (result.success) {
                            resolve(result.data);
                        } else {
                            reject(new Error(result.error?.message || 'Unknown error'));
                        }
                    }
                );
            });
        };
    }
}

// 创建插件实例
function createPlugin<T>(pluginName: string): T {
    return new Proxy({}, new PluginProxy(pluginName)) as T;
}

5.3 事件处理 #

typescript
// 事件管理器
class EventManager {
    private listeners = new Map<string, Set<Function>>();
    
    // 添加监听器
    addListener(eventName: string, callback: Function): () => void {
        if (!this.listeners.has(eventName)) {
            this.listeners.set(eventName, new Set());
        }
        
        this.listeners.get(eventName)!.add(callback);
        
        // 返回移除函数
        return () => {
            this.listeners.get(eventName)?.delete(callback);
        };
    }
    
    // 触发事件
    trigger(eventName: string, data: any): void {
        const callbacks = this.listeners.get(eventName);
        callbacks?.forEach(callback => {
            try {
                callback(data);
            } catch (error) {
                console.error(`Event callback error: ${error}`);
            }
        });
    }
}

// 全局事件处理
window.Capacitor.triggerEvent = (pluginName: string, eventName: string, data: any) => {
    const fullEventName = `${pluginName}:${eventName}`;
    eventManager.trigger(fullEventName, data);
};

六、数据传输 #

6.1 基本类型映射 #

JavaScript iOS (Swift) Android (Java)
string String String
number Double/Int Double/Integer
boolean Bool Boolean
null nil null
undefined nil null
Array [Any] JSONArray
Object [String: Any] JSONObject

6.2 特殊类型处理 #

typescript
// Base64数据传输
function toBase64(data: ArrayBuffer): string {
    const bytes = new Uint8Array(data);
    let binary = '';
    for (let i = 0; i < bytes.byteLength; i++) {
        binary += String.fromCharCode(bytes[i]);
    }
    return btoa(binary);
}

function fromBase64(base64: string): ArrayBuffer {
    const binary = atob(base64);
    const bytes = new Uint8Array(binary.length);
    for (let i = 0; i < binary.length; i++) {
        bytes[i] = binary.charCodeAt(i);
    }
    return bytes.buffer;
}

// 使用示例
const imageData = await Camera.getPhoto({ resultType: 'base64' });
const arrayBuffer = fromBase64(imageData.base64String);

6.3 大数据传输 #

typescript
// 分块传输大数据
class ChunkedTransfer {
    private chunkSize = 1024 * 1024; // 1MB
    
    async sendLargeData(pluginName: string, methodName: string, data: ArrayBuffer) {
        const totalChunks = Math.ceil(data.byteLength / this.chunkSize);
        const transferId = generateUUID();
        
        // 发送分块
        for (let i = 0; i < totalChunks; i++) {
            const start = i * this.chunkSize;
            const end = Math.min(start + this.chunkSize, data.byteLength);
            const chunk = data.slice(start, end);
            
            await window.Capacitor.nativePromise(pluginName, methodName, {
                transferId,
                chunkIndex: i,
                totalChunks,
                data: toBase64(chunk)
            });
        }
    }
}

七、错误处理 #

7.1 错误类型 #

typescript
// Capacitor错误类
class CapacitorException extends Error {
    constructor(
        message: string,
        public code?: string,
        public data?: any
    ) {
        super(message);
        this.name = 'CapacitorException';
    }
}

// 常见错误码
enum CapacitorErrorCode {
    // 插件错误
    PLUGIN_NOT_FOUND = 'PLUGIN_NOT_FOUND',
    METHOD_NOT_FOUND = 'METHOD_NOT_FOUND',
    
    // 权限错误
    PERMISSION_DENIED = 'PERMISSION_DENIED',
    
    // 参数错误
    INVALID_ARGUMENT = 'INVALID_ARGUMENT',
    
    // 系统错误
    UNAVAILABLE = 'UNAVAILABLE',
    TIMEOUT = 'TIMEOUT'
}

7.2 错误传递 #

typescript
// JavaScript错误处理
async function safeNativeCall<T>(
    pluginName: string,
    methodName: string,
    options: any
): Promise<T> {
    try {
        const result = await window.Capacitor.nativePromise(
            pluginName,
            methodName,
            options
        );
        
        if (!result.success) {
            throw new CapacitorException(
                result.error.message,
                result.error.code,
                result.error.data
            );
        }
        
        return result.data;
    } catch (error) {
        // 转换原生错误
        if (error instanceof CapacitorException) {
            throw error;
        }
        throw new CapacitorException(
            error.message || 'Unknown error',
            CapacitorErrorCode.UNAVAILABLE
        );
    }
}

7.3 原生错误处理 #

swift
// iOS错误处理
class CapacitorError: Error {
    let code: String
    let message: String
    let data: [String: Any]?
    
    init(code: String, message: String, data: [String: Any]? = nil) {
        self.code = code
        self.message = message
        self.data = data
    }
    
    func toJSON() -> [String: Any] {
        var json: [String: Any] = [
            "code": code,
            "message": message
        ]
        if let data = data {
            json["data"] = data
        }
        return json
    }
}

// 使用示例
func execute(methodName: String, options: [String: Any]) throws -> [String: Any] {
    switch methodName {
    case "getPhoto":
        guard options["quality"] != nil else {
            throw CapacitorError(
                code: "INVALID_ARGUMENT",
                message: "quality is required"
            )
        }
        return try getPhoto(options)
    default:
        throw CapacitorError(
            code: "METHOD_NOT_FOUND",
            message: "Method \(methodName) not found"
        )
    }
}

八、性能优化 #

8.1 减少桥接调用 #

typescript
// 批量操作
class BatchOperation {
    private operations: Array<() => Promise<any>> = [];
    
    add(operation: () => Promise<any>) {
        this.operations.push(operation);
    }
    
    async execute(): Promise<any[]> {
        return Promise.all(this.operations.map(op => op()));
    }
}

// 使用示例
const batch = new BatchOperation();
batch.add(() => Storage.set({ key: 'name', value: 'John' }));
batch.add(() => Storage.set({ key: 'age', value: '30' }));
batch.add(() => Storage.set({ key: 'city', value: 'New York' }));
await batch.execute();

8.2 缓存机制 #

typescript
// 桥接调用缓存
class BridgeCache {
    private cache = new Map<string, { data: any; timestamp: number }>();
    private ttl = 60000; // 1分钟
    
    async get<T>(
        pluginName: string,
        methodName: string,
        options: any,
        fetcher: () => Promise<T>
    ): Promise<T> {
        const key = `${pluginName}:${methodName}:${JSON.stringify(options)}`;
        const cached = this.cache.get(key);
        
        if (cached && Date.now() - cached.timestamp < this.ttl) {
            return cached.data;
        }
        
        const data = await fetcher();
        this.cache.set(key, { data, timestamp: Date.now() });
        return data;
    }
}

九、调试技巧 #

9.1 桥接日志 #

typescript
// 启用桥接调试
if (import.meta.env.DEV) {
    const originalNativeCallback = window.Capacitor.nativeCallback;
    
    window.Capacitor.nativeCallback = function(...args) {
        console.log('[Bridge] Call:', args);
        return originalNativeCallback.apply(this, args);
    };
    
    const originalTriggerEvent = window.Capacitor.triggerEvent;
    
    window.Capacitor.triggerEvent = function(...args) {
        console.log('[Bridge] Event:', args);
        return originalTriggerEvent.apply(this, args);
    };
}

9.2 性能监控 #

typescript
// 桥接性能监控
class BridgeProfiler {
    private metrics = new Map<string, number[]>();
    
    async profile<T>(
        pluginName: string,
        methodName: string,
        call: () => Promise<T>
    ): Promise<T> {
        const key = `${pluginName}.${methodName}`;
        const start = performance.now();
        
        try {
            return await call();
        } finally {
            const duration = performance.now() - start;
            
            if (!this.metrics.has(key)) {
                this.metrics.set(key, []);
            }
            this.metrics.get(key)!.push(duration);
            
            console.log(`[Bridge] ${key} took ${duration.toFixed(2)}ms`);
        }
    }
    
    getStats(pluginName: string, methodName: string) {
        const key = `${pluginName}.${methodName}`;
        const durations = this.metrics.get(key) || [];
        
        return {
            count: durations.length,
            avg: durations.reduce((a, b) => a + b, 0) / durations.length,
            max: Math.max(...durations),
            min: Math.min(...durations)
        };
    }
}

十、总结 #

10.1 桥接核心 #

要点 说明
通信方向 双向:JS ↔ Native
消息格式 JSON序列化
调用方式 Promise异步
事件机制 观察者模式

10.2 最佳实践 #

  • 减少不必要的桥接调用
  • 使用批量操作合并调用
  • 合理使用缓存
  • 做好错误处理

10.3 下一步 #

了解桥接机制后,让我们学习 配置系统

最后更新:2026-03-28