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().
| 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.