Topics
Recent articles

Android & Mobile

Fix Android WebView onShowFileChooser Overlapping Callback

Learn how to correctly manage Android WebView onShowFileChooser callbacks, handle overlapping chooser requests, complete cancellations, and process URI results safely.

Table of Contents5 sections
Blurred laptop screen displaying code with a white cap in focus, depicting a tech workspace.
Blurred laptop screen displaying code with a white cap in focus, depicting a tech workspace.

Rapid user actions can crash Android WebViews when file selection prompts overlap.

Quick solution:

filePathCallback?.onReceiveValue(null)
filePathCallback = newCallback

Prerequisites require setting up an ActivityResultLauncher registered via the Android Activity Result APIs before launching intents. Developers must ensure that any active callback is explicitly resolved before accepting a new one, as detailed in the official Android WebChromeClient reference.

What happens when a second chooser replaces the first?

Each invocation of onShowFileChooser delivers a fresh ValueCallback reference. If a user taps an upload button, switches away, and taps another upload button without resolving the first, the framework expects the host app to finish the pending request. Failing to do so leaves the underlying WebView thread waiting indefinitely for a response, causing frozen states, unresponsive UI threads, and subtle memory leaks that degrade overall application performance.

To prevent hanging threads, always invoke onReceiveValue(null) on the existing callback reference before assigning the new one. This signals to the WebView engine that the previous operation has been canceled and frees the blocked resources. For further details on modern result handling patterns, consult the Android Activity Result APIs documentation.

When multiple WebView instances run concurrently or when a single WebView instance reloads content rapidly during deep user interaction, tracking the active callback becomes even more critical. If you are auditing system behavior, ensuring proper cache disposal is also important as discussed when you audit macos system data before deleting developer caches in separate environments, though mobile runtimes require distinct callback lifecycles. Maintaining a single source of truth for the active file chooser prevents memory leaks from accumulating silently across configuration changes and rapid view recycling. Furthermore, managing the callback reference lifecycle correctly avoids unexpected state corruption inside the native bridge layers.

In high-frequency mobile applications, users often tap file upload controls repeatedly before the system file picker sheet finishes presenting itself on the screen. Each successive tap triggers another lifecycle event on the host WebView container. If your implementation simply overwrites the previous callback reference without notifying the rendering engine, the thread allocated for the first dialog stays blocked. Over time, these stranded references consume native heap memory and leave the WebView event loop waiting for a response that will never arrive. Implementing an explicit null termination step guarantees that stale operations clean up their allocated memory structures immediately upon interruption.

When scaling this architecture across multiple fragments or dynamic view hierarchies within a single hosting activity, developers should centralize the callback holder inside a dedicated singleton, view model, or controller class. Storing the reference loosely inside anonymous inner classes or direct fragment fields often leads to window leaks when the underlying fragment view is destroyed while an intent is still active in the system background. By routing all file chooser actions through a single managed interface, your application maintains total control over outstanding file selection requests, preventing concurrency bugs and UI locks.

Option When to use it Trade-off or Failure Mode Recommendation
Immediate null-resolution When replacing an active filePathCallback May discard an ongoing user selection Always invoke onReceiveValue(null) to free the pending thread.
Queueing requests When multi-step uploads are required Adds state complexity and potential memory leaks Avoid queueing; retain only the most recent user action.

How are cancellation and launch failure completed?

When a user cancels the system file picker dialog, the result code returns Activity.RESULT_CANCELED. The host application must catch this result and immediately fulfill the pending callback with a null array to release the waiting thread inside the WebView runtime. Neglecting this step leaves the browser engine waiting indefinitely for file URIs that will never arrive. Developers must handle these edge cases gracefully to maintain robust application flows.

val fileChooserLauncher = registerForActivityResult(
    ActivityResultContracts.StartActivityForResult()
) { result ->
    if (result.resultCode == Activity.RESULT_OK) {
        val uris = WebSafeUtils.parseResultUris(result.resultCode, result.data)
        filePathCallback?.onReceiveValue(uris)
    } else {
        filePathCallback?.onReceiveValue(null)
    }
    filePathCallback = null
}

If the device lacks an application capable of handling the file selection intent, an ActivityNotFoundException is thrown. The launcher block must catch this exception, invoke onReceiveValue(null) to clean up the waiting WebView thread, and safely return false to the system so the web page does not remain locked. Proper exception handling ensures that legacy or stripped-down custom ROMs do not crash your entire application flow when users trigger file uploads. When designing robust architectures, preventing UI thread blockages is essential for overall application responsiveness. Developers should always wrap intent creation inside try-catch blocks whenever custom file manager extensions or exotic intent filters are declared within the manifest file of the application package.

Failing to catch chooser launch errors can result in unhandled runtime exceptions that propagate up to the Android system runtime, abruptly terminating your application process. Custom file managers installed by users may sometimes declare malformed intent filters or throw security exceptions when queried by external applications. Defensive programming around the launcher execution block guarantees that even if a third-party file manager fails catastrophically, your WebView recovers cleanly, resets its internal state, and displays an appropriate error message or toast to the user.

Which state survives activity recreation?

Complex configurations such as screen rotations, eliminating dark mode fouc and building interfaces correctly, or low-memory process kills cause the hosting Activity to recreate entirely. Complex runtime objects like ValueCallback and raw file streams cannot be reliably serialized into saved instance state bundles.

Developers must not attempt to serialize callbacks into saved state bundles. Instead, let the callback clear itself upon destruction, and require the web view to trigger a fresh selection if the user interacts with the page post-recreation. Managing state across configuration changes requires careful separation between transient callback references and persistent application data. When views are destroyed and recreated, any dangling reference to an old WebView or its associated callbacks can lead to subtle memory retention issues if not nullified explicitly in the lifecycle hooks.

During an orientation change, the Android operating system destroys the current activity instance and instantiates a completely fresh one. Any active file chooser intent launched via startActivityForResult continues running in a separate system process or operating system window, but the callback reference pointing back to your activity’s WebView instance becomes instantly orphaned. Attempting to restore this callback across configuration changes using Parcelable or Serializable mechanisms will fail because ValueCallback contains non-serializable native references and UI bindings. Consequently, developers must gracefully drop any pending file selection operation during configuration changes, ensuring that the recreated activity initializes with a clean slate and avoids interacting with dead view references.

Verification checklist

Continue Exploring

You Might Also Like

View all articles