Bridge vs Cordova exec()

Where Capacitor's Bridge Differs from Cordova's exec()

Both bridges carry JSON between a WebView and native plugins, and Capacitor 243,123 still carries Cordova 129 's traffic: MessageHandler.postMessage hands any message of type cordova to a real org.apache.cordova.PluginManager (Migrating from Cordova). The mechanics differ. In cordova-android 15.1.0 (read with npm 2,036 pack), androidExec() in cordova.js builds a callback id such as "Camera" + counter, JSON-encodes the argument array and calls exec(bridgeSecret, service, action, callbackId, argsJson), a @JavascriptInterface method. The page's JavaScript thread waits while CordovaBridge.jsExec runs the plugin's execute(), which is why PluginManager.java still warns "THREAD WARNING: exec() call to ... blocked the main thread" and points plugin authors to getThreadPool().

The two bridges on Android, read from their 2026 source
Aspect Cordova (exec) Capacitor 8
JS API exec(success, fail, service, action, args) Promise from a typed proxy
Arguments JSON array JSON object (named options)
JS to native @JavascriptInterface, blocking addWebMessageListener, async
Security bridge secret from prompt() origin rules, main frame only
Plugin thread the caller's, unless moved "CapacitorPlugins" thread
Plugin lookup config.xml feature names annotations and reflection

In practice a slow Capacitor plugin cannot freeze your JavaScript, and a try/await replaces the success and failure callback pair. When a migrated Cordova plugin misbehaves inside Capacitor, look for "To native (Cordova plugin)" in logcat -s Capacitor/Plugin: it proves the call crossed the bridge, so the problem is in the plugin.