Repository navigation
fix(browser): explain instead of crashing when the system WebView cannot start - #414
Merged
Merged
Conversation
…not start Crashlytics (v1.2.25-v1.2.27, 2 events / 2 users) has a fatal AndroidRuntimeException caused by Resources$NotFoundException "failed to redirect ResourcesImpl" on a vivo phone (Android 16). It is thrown by WebView.setWebContentsDebuggingEnabled in GuestBrowserScreen's AndroidView factory: the first WebView call starts the system WebView provider, whose resource registration failed. The exception escaped the Compose view factory and took the whole app down. Nothing in the app can repair a provider that will not start, so creation is now guarded. viewOrNull builds the WebView (including the debugging flag and its configuration) and returns null on any Exception; the screen then shows a short explanation, translated into all eight locales, instead of the page. Only Exception is caught, so an OutOfMemoryError is not reported as a missing WebView. PlatformViewTest covers the three outcomes; it did not compile before the change. The Compose wiring and the fallback text are not unit-testable here, and the provider failure cannot be reproduced without the affected device. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Pre-PR review of the previous commit raised four points, all addressed here: - The guard wrapped the whole WebView setup, so an exception thrown by configure() or loadUrl() after the view existed returned null and left a half-built WebView that was never attached or destroyed. Only starting the system provider (the debugging flag and the WebView constructor) can fail for reasons the app cannot fix, so only that is guarded now; the rest runs on a WebView that exists, where a failure is a real bug. - A provider whose classes or native libraries fail to load surfaces as a LinkageError rather than an Exception. viewOrNull now treats it as a missing view as well. Other Errors, an OutOfMemoryError above all, still propagate. - With no WebView the address bar, Open and history buttons did nothing, so the address bar is hidden while the explanation is shown. - The explanation called the screen the "built-in browser" while its title is "Guest Browser" in each locale; it now uses the screen's own name. PlatformViewTest gains the LinkageError case, which failed before this change. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes a fatal crash that Crashlytics reports for v1.2.25–v1.2.27 (2 events / 2 users): opening the Guest Browser on a vivo V2538 (Android 16) killed the app with
setWebContentsDebuggingEnabledis the first WebView call, so it is what starts the system WebView provider, and on that device the provider's resource registration threw. The exception escaped the Compose view factory and took the whole app down. Nothing in the app can repair a provider that will not start.What changed
viewOrNull(new,feature/browser/PlatformView.kt) builds a view the platform may refuse to build and returns null instead of throwing. It catchesExceptionandLinkageError(a provider whose classes or native libraries fail to load), and nothing else: anOutOfMemoryErroris not a missing provider.GuestBrowserScreenguards only starting the provider (the debugging flag and theWebViewconstructor). Everything after it (configure,loadUrl, …) still runs on a WebView that exists, so a failure there stays a real bug and no half-built view is left behind.What this does not claim
Not in this PR (known follow-ups)
From the review, none blocking:
reloadEnabledargument would match hiding the address bar.PlatformViewTest's link-error case asserts only the count of reported throwables;assertSame(error, reported.single())would match the other test.configure()orloadUrl()throws on a half-broken provider the app still crashes; destroying the view and falling back would cover it, once a crash report shows it happens.GuestBrowserScreenInstrumentedTestcover the fallback.Test plan
./gradlew spotlessCheck detekt :app:testGithubDebugUnitTest— 1620 tests, 0 failures. New:PlatformViewTest(4). It did not compile before the change, and the link-error case failed before the second commit.i18n-check.yml) andxmllinton all eightstrings.xml./gradlew :app:assembleGithubDebug :app:lintGithubDebug— 0 errorsPre-PR review
The
repo-reviewersubagent is defined for OpenCode (.opencode/agents/repo-reviewer.md) and is not available in the Claude Code session that produced this change. The reviews below were done by a read-only Claude Code subagent given that definition as its role and output format. It is a stand-in, not therepo-reviewersubagent itself.The first commit (
2024b17b) was approved with non-blocking suggestions; the second commit (eec26bf0) applies four of them (narrower guard,LinkageError, hidden address bar, wording). The report below is the re-review ofHEAD(eec26bf0).Pre-PR review
判定: APPROVE
対象: コミット2件(2024b17b、eec26bf0)。
origin/mainの先頭を祖先に持つ。今回の差分はgit show HEAD。ブロッカー
なし
提案(非ブロッキング)
onReload = { webView?.reload() })— フォールバック表示中も、更新ボタンは有効なままで、押しても何も起きない(履歴ボタンはcanGoBack/canGoForwardが false のため無効)。GuestBrowserTopBarにreloadEnabled = !webViewUnavailableのような引数を足して無効にすると、URL バーを隠した判断と揃う。if (!webViewUnavailable)で URL バーを隠す)— 失敗する factory は、AndroidView が挿入される composition の apply フェーズでwebViewUnavailable = trueを書く。そのため最初の1フレームは URL バー付きで描画され、次のフレームで消える可能性がある。実害はなく、許容範囲。a provider whose native code fails to load ...)—assertEquals(1, reported.size)だけでは、報告されたThrowableがUnsatisfiedLinkErrorそのものかを確認できない。assertSame(error, reported.single())にすると、既存のRuntimeException側のテストと揃う。configure()やloadUrl()が投げた場合はクラッシュに戻る(生成後の失敗は実バグという割り切り)。プロバイダが半壊状態の端末では、WebView は作れてもsettingsやloadUrlが投げる場合が理論上ありうる。その場合はdestroy()してフォールバックに回す選択肢もあるが、クラッシュ報告で実例が出てから検討すれば十分。チェック済み項目
created.apply { ... }の中の順序は、layoutParamsの設定、configure(...)、webView = this、loadUrl(initialUrlが空でないとき)で、createの中の順序(setWebContentsDebuggingEnabled→WebView(context))も元のとおり。applyのthisはWebView、webView = thisのwebViewは外側のremember状態で、書き込み先と書き込み時点(AndroidView の factory、composition の apply フェーズ)も元のコードと同じ。created == nullの分岐ではwebViewに何も代入されないので、作りかけの WebView が状態に残らない(前回の指摘の解消を確認)。val created = viewOrNull(...)はWebView?。else 枝ではvalなので smart cast が効く。factory の最後の式が if-else で、枝の型はView(View(context))とWebViewなので共通上位型はView、AndroidView<View>に推論される。updateやonReleaseを使っていないので影響はない。webViewUnavailable = trueは枝の先頭で書いてからView(context)を返す。factory は apply フェーズで走り、前例のwebView = thisと同じ位置から状態を書くだけなので安全。urlInput(rememberSaveable)や BackHandler(webViewが null ならonBack())には影響しない。setWebContentsDebuggingEnabledとWebView(context)の2呼び出しだけに縮小されているので、過剰ではない。UnsatisfiedLinkError、NoClassDefFoundError、NoSuchMethodErrorはこの範囲ではプロバイダの不整合の表れで、アプリ側では直せないため、フォールバックが適切。OutOfMemoryErrorやStackOverflowErrorはLinkageErrorではないので伝播し、テストもそれを確認している。ごくまれにExceptionInInitializerError(LinkageErrorの一種)が OOM を原因に持つ場合は、握られる可能性があるが、実害の見込みは極めて低い。guest_browser_webview_unavailableが8ファイル(values、ar、es、fr、ja、pt-rBR、ru、zh-rCN)に各1つあり、xmllint --nooutでエラーなし。guest_browser_titleに揃っている(Guest Browser、ゲストブラウザ、Navegador invitado、Navigateur invité、Navegador convidado、Гостевой браузер、访客浏览器、المتصفح الضيف)。WebViewとAndroid System WebViewは文中にあり、文末はアラビア語(المشكلة.)なので RTL の表示は崩れない。n\'aは、同ファイル既存のl\'autorisationと同じエスケープ形式。他の特殊文字はない。🤖 Generated with Claude Code