Capacitor Version
@capacitor/cli: 8.5.0
@capacitor/core: 8.5.0
@capacitor/android: 8.5.0
@capacitor/app: 8.1.1
Other API Details
Android 14 (API 34), ASUS AI2201, System WebView chromium-SystemWebViewGoogle6432.aab-stable-787118103.
Platforms Affected
Android
Current Behavior
When the activity is recreated (any configuration change not listed in android:configChanges), AppPlugin fires pause and appStateChange immediately before the WebView is destroyed. Those events go through MockCordovaWebViewImpl.eval(), which posts to the main looper without checking whether the WebView is still alive:
// android/capacitor/src/main/java/com/getcapacitor/cordova/MockCordovaWebViewImpl.java#L187-L190
public void eval(final String js, final ValueCallback<String> callback) {
Handler mainHandler = new Handler(context.getMainLooper());
mainHandler.post(() -> webView.evaluateJavascript(js, callback));
}
The posted runnable is executed after the WebView has been destroyed, so evaluateJavascript runs on a dead instance. Timestamps from a single recreate, showing the runnable outliving its WebView by 190 ms:
14:56:49.324 V Capacitor/AppPlugin: Notifying listeners for event pause
14:56:49.339 V Capacitor/AppPlugin: Notifying listeners for event appStateChange
14:56:49.398 D Capacitor: App destroyed
14:56:49.497 D Capacitor: Starting BridgeActivity
14:56:49.529 D Capacitor: App started
14:56:49.588 W cr_AwContents: Application attempted to call on a destroyed WebView
Stack trace:
W cr_AwContents: Application attempted to call on a destroyed WebView
W cr_AwContents: java.lang.Throwable
W cr_AwContents: at org.chromium.android_webview.AwContents.p(...)
W cr_AwContents: at com.android.webview.chromium.WebViewChromium.evaluateJavaScript(...)
W cr_AwContents: at android.webkit.WebView.evaluateJavascript(WebView.java:893)
W cr_AwContents: at com.getcapacitor.cordova.MockCordovaWebViewImpl.lambda$eval$0(MockCordovaWebViewImpl.java:189)
W cr_AwContents: at com.getcapacitor.cordova.MockCordovaWebViewImpl$$ExternalSyntheticLambda1.run(...)
W cr_AwContents: at android.os.Handler.handleCallback(Handler.java:958)
W cr_AwContents: at android.os.Looper.loop(Looper.java:364)
W cr_AwContents: at android.app.ActivityThread.main(ActivityThread.java:8439)
In this particular case the consequence is benign — the dropped events target the JS context that is being torn down anyway, and the fresh context receives its own appStateChange right after App resumed. The reason for reporting it is the pattern rather than this instance: any plugin that calls notifyListeners around teardown hits the same window, and there the lost event may matter. The warning also adds noise that makes real WebView lifecycle problems harder to spot in logcat.
Expected Behavior
eval() should not call into a WebView that has been destroyed. Either skip the call when the bridge/activity is no longer alive, or have BridgeActivity clear pending evals during teardown, so that events queued before destruction do not execute afterwards.
Project Reproduction
I do not have a standalone reproduction repository, but the issue reproduces on a stock npx cap add android project with no plugins beyond @capacitor/app, because the events involved are fired by Capacitor itself.
Steps:
- Create a default Capacitor Android project and run it on a device with Android 14.
- Keep the default
android:configChanges generated by Capacitor — note that it does not include fontScale, which makes a font size change a convenient trigger for a genuine activity recreate.
- Start
adb logcat filtered on Capacitor and cr_AwContents.
- With the app in the foreground, change the system font size (Settings → Display → Font size), or run
adb shell settings put system font_scale 1.15.
- Observe
App destroyed followed by Starting BridgeActivity, and then the Application attempted to call on a destroyed WebView warning.
The same happens on any other recreate path (for example a configuration change outside configChanges, or the activity being destroyed in the background under memory pressure and then restored).
Additional Information
The code path is unchanged on main at the time of writing, so this is not specific to the 8.5.0 release.
The trigger used above is not artificial for our use case: the app is an accessibility-first podcast player for blind and partially sighted users, and changing the system font size is one of the first things our users do. Playback runs in a foreground media service that survives the recreate, so the WebView restarts while audio keeps playing — which is how we noticed the warning in the first place.
Capacitor Version
Other API Details
Android 14 (API 34), ASUS AI2201, System WebView
chromium-SystemWebViewGoogle6432.aab-stable-787118103.Platforms Affected
Android
Current Behavior
When the activity is recreated (any configuration change not listed in
android:configChanges),AppPluginfirespauseandappStateChangeimmediately before the WebView is destroyed. Those events go throughMockCordovaWebViewImpl.eval(), which posts to the main looper without checking whether the WebView is still alive:The posted runnable is executed after the WebView has been destroyed, so
evaluateJavascriptruns on a dead instance. Timestamps from a single recreate, showing the runnable outliving its WebView by 190 ms:Stack trace:
In this particular case the consequence is benign — the dropped events target the JS context that is being torn down anyway, and the fresh context receives its own
appStateChangeright afterApp resumed. The reason for reporting it is the pattern rather than this instance: any plugin that callsnotifyListenersaround teardown hits the same window, and there the lost event may matter. The warning also adds noise that makes real WebView lifecycle problems harder to spot in logcat.Expected Behavior
eval()should not call into a WebView that has been destroyed. Either skip the call when the bridge/activity is no longer alive, or haveBridgeActivityclear pending evals during teardown, so that events queued before destruction do not execute afterwards.Project Reproduction
I do not have a standalone reproduction repository, but the issue reproduces on a stock
npx cap add androidproject with no plugins beyond@capacitor/app, because the events involved are fired by Capacitor itself.Steps:
android:configChangesgenerated by Capacitor — note that it does not includefontScale, which makes a font size change a convenient trigger for a genuine activity recreate.adb logcatfiltered onCapacitorandcr_AwContents.adb shell settings put system font_scale 1.15.App destroyedfollowed byStarting BridgeActivity, and then theApplication attempted to call on a destroyed WebViewwarning.The same happens on any other recreate path (for example a configuration change outside
configChanges, or the activity being destroyed in the background under memory pressure and then restored).Additional Information
The code path is unchanged on
mainat the time of writing, so this is not specific to the 8.5.0 release.The trigger used above is not artificial for our use case: the app is an accessibility-first podcast player for blind and partially sighted users, and changing the system font size is one of the first things our users do. Playback runs in a foreground media service that survives the recreate, so the WebView restarts while audio keeps playing — which is how we noticed the warning in the first place.