Repository navigation
[Android] App is killed when the WebView renderer goes away while an InAppWebView is being disposed #2945
Description
Is there an existing issue for this?
- I have searched the existing issues
Current Behavior
On Android, if the WebView renderer process goes away while an InAppWebView is being disposed, Android WebView terminates the whole app. This happens even when the app sets useOnRenderProcessGone: true and handles onRenderProcessGone for every WebView it still uses.
InAppWebView.dispose() does not destroy the WebView right away. It swaps in an anonymous WebViewClient, calls loadUrl("about:blank"), and calls destroy() only from that client's onPageFinished:
InAppWebView.java#L2190-L2194:setWebViewClient(new WebViewClient() { onPageFinished → destroy(); })InAppWebView.java#L2241:loadUrl("about:blank");
Until that onPageFinished runs, the WebView is still alive and attached to the renderer. If the renderer goes away in that window, Android WebView asks this WebView through WebViewClient.onRenderProcessGone. The anonymous client does not override it, so the default returns false. The API docs describe what happens then: "otherwise, application will crash if render process crashed, or be killed if render process was killed by the system."
The plugin's regular clients cannot help here either. dispose() sets channelDelegate to null first, and InAppWebViewClient(Compat).onRenderProcessGone then falls through to super (false) anyway (L793-L803).
A common way to hit this: the app recovers from a renderer crash by recreating the InAppWebView (new key), which disposes the old one. If the renderer dies again shortly afterwards, for example from another low-memory kill, the old WebView is still waiting for about:blank and takes the app down. Popping a route that contains an InAppWebView opens the same window. We first saw this in production.
How long the window lasts:
- Kill:
about:blankloads in a new renderer, anddestroy()ran 13–149 ms afterdispose()started. - Crash: the renderer stays frozen while its crash is being reported (~2.5 s here), so
about:blankcannot finish. The window lasts until the crash is reported.
Expected Behavior
The app survives. It handles onRenderProcessGone for every WebView it still uses, and the WebView being disposed has nothing left to recover.
Proposed fix: handle onRenderProcessGone in the dispose client by destroying right away and reporting it as handled. I'll open a PR.
setWebViewClient(new WebViewClient() {
public void onPageFinished(WebView view, String url) {
destroy();
}
@RequiresApi(api = Build.VERSION_CODES.O)
@Override
public boolean onRenderProcessGone(WebView view, RenderProcessGoneDetail detail) {
destroy();
return true;
}
});Steps with code example to reproduce
Minimal repro app: https://github.com/BottlePumpkin/flutter_inappwebview-render-gone-repro
It has one InAppWebView and no other WebViews. The renderer is terminated from inside the app with WebViewRenderProcess.terminate() (didCrash=false) or chrome://crash (didCrash=true). The README has the steps and every scenario.
- Show an
InAppWebViewwithInAppWebViewSettings(useOnRenderProcessGone: true). InonRenderProcessGone, recreate it with a newKey. - Terminate the renderer (
WebViewCompat.getWebViewRenderProcess(webView).terminate()). The app handles it and the old WebView is disposed. - Terminate the respawned renderer within ~50–150 ms, before the old WebView's
about:blankhas finished.
InAppWebView(
key: ValueKey('home-$_gen'),
initialData: InAppWebViewInitialData(data: '<h1>gen $_gen</h1>'),
initialSettings: InAppWebViewSettings(useOnRenderProcessGone: true),
onRenderProcessGone: (controller, detail) => setState(() => _gen++), // recreate -> old one is disposed
)App killed out of 10 cold starts:
| scenario | before | with the fix |
|---|---|---|
| kill → recreate → kill the respawned renderer | 10/10 | 0/10 |
renderer terminated right after dispose() issues about:blank |
10/10 | 0/10 |
| pop a page with a WebView, kill right after | 3/10 | 0/10 |
| crash the renderer, pop a page with a WebView while the crash is being reported | 9/10 | 0/10 |
Stacktrace/Logs
Logs
The only extra logging is in the dispose client (IAWV-dispose tag), and its return value is unchanged.
didCrash=false (terminate / low-memory kill):
E chromium: [ERROR:android_webview/browser/aw_browser_terminator.cc:176] Renderer process (31109) crash detected (code -1).
W IAWV-dispose: dispose client onRenderProcessGone id=259602995 url=about:blank didCrash=false returns=false
E chromium: [ERROR:android_webview/browser/aw_browser_terminator.cc:124] Render process (31109) kill (OOM or update) wasn't handed by all associated webviews, killing application.
I ActivityManager: Process dev.repro.repro_app (pid 31075) has died: fg TOP
didCrash=true (renderer crash):
E chromium: [ERROR:android_webview/browser/aw_browser_terminator.cc:176] Renderer process (9113) crash detected (code 11).
W IAWV-dispose: dispose client onRenderProcessGone id=85388500 url=about:blank didCrash=true returns=false
F chromium: [FATAL:third_party/crashpad/crashpad/client/crashpad_client_linux.cc:763] Render process (9113)'s crash wasn't handled by all associated webviews, triggering application crash.
F libc : Fatal signal 5 (SIGTRAP), code 1 (TRAP_BRKPT) ... (repro.repro_app)
In the same runs, the app's live InAppWebView received onRenderProcessGone through the plugin's normal client and returned true. Out of 40 cold starts, all 32 app deaths were preceded (within 0–5 ms) by returns=false from the dispose client, and none of the 8 surviving runs had it.
Flutter version
3.44.4 (Dart 3.12.2)
Operating System, Device-specific and/or Tool
Android 16 (API 36) emulator (sdk_gphone64_arm64), Android System WebView 155.0.8059.43
Plugin version
6.2.0-beta.3 (master 17527cae), flutter_inappwebview_android 1.2.0-beta.3. The same code is in 6.2.0-beta.2.
Additional information
- Related: Render process (11786)'s crash wasn't handled by all associated webviews, triggering application crash. #1325 (same FATAL message; a commenter still got it after handling
onRenderProcessGoneand recreating the WebView with a new key, which matches this path). InAppWebView dispose issue. #2478 and Page blinking with black background on Impeller #2460 are other symptoms of the same deferred-destroydispose(). - Most other WebView wrappers, such as webview_flutter and react-native-webview, call
destroy()directly in their dispose path, so they don't have this window. - Separate, smaller observation: when the renderer crashes (as opposed to being killed), a WebView disposed during the crash report sometimes never reaches
destroy(). NoonRenderProcessGonearrives for it, so the fix above does not cover that leak. It did not affect app survival in our runs.
Self grab
- I'm ready to work on this issue!
Activity