The Android half of #252, which is iOS-only and fixed in #253.
MainActivity.kt.template, onReceivedError:
if (request?.isForMainFrame == true && hasBundledFallback && !loadedBundledFallback) {
loadedBundledFallback = true
webView.loadUrl(BUNDLED_APP_URL)
}Same shape as the iOS bug, and both halves of it:
- It does not look at the error. Any main-frame error falls back, whatever the cause.
- It is one-way.
loadedBundledFallbackis set and never cleared, so after one failure the app is on the bundled copy until the process dies. The bundle cannot reach a frontend's API by relative path, so the app loses its API for the rest of the session.
What #253 did on iOS, for reference
- Fall back only on connectivity errors: not connected, cannot find or connect to the host, timed out, connection lost, DNS. TLS and bad-response errors stay on the remote, since the server was reached and the bundle would hide the fault.
- Come back when connectivity returns and when the app returns to the foreground.
- The network trigger fires on a false → true transition only. Retrying whenever the network is merely up loops against a server that is down on a network that is up.
What is different on Android
- The error codes.
WebResourceError.getErrorCode()usesWebViewClient.ERROR_*. The connectivity ones are roughlyERROR_HOST_LOOKUP,ERROR_CONNECT,ERROR_TIMEOUTandERROR_IO.ERROR_FAILED_SSL_HANDSHAKEand the auth errors should stay on the remote. Worth checking whether Android reports a cancelled navigation throughonReceivedErrorat all, since that was the main trigger on iOS. It may not, and the latch would then matter mostly for real failures. - The recovery hooks.
ConnectivityManager.NetworkCallback.onAvailablefor the network, andonResumefor the foreground. The same transition-only rule applies to the network callback. - Testing.
index.test.tscan pin the wiring. The classifier can be a pure function over the error code and tested directly in the Kotlin compile thatandroid-builderalready runs, or its decision table checked from TypeScript.