Usage Guide: Persistent Callbacks

By default, a callback function passed between Java and JavaScript is designed for a single use. After it's invoked once, it is removed from memory to prevent leaks. However, some scenarios require a long-lasting communication channel where one side can send multiple messages to the other over time.

Persistent callbacks solve this problem. They are not discarded after the first use and can be invoked multiple times.

This is useful for:

  • Real-time data updates (e.g., streaming sensor data to the UI).
  • Event notifications (e.g., native download progress updates).
  • Maintaining a long-term communication link without re-establishing it.

Java Calling JavaScript with a Persistent Callback

When you want JavaScript to be able to call back to your Java code multiple times, use callHandlerPersistent.

Java Side

// Use callHandlerPersistent to create a callback that won't be deleted.
webView.callHandlerPersistent("startListeningToEvents", "some-event-id", new OnBridgeCallback() {
    @Override
    public void onCallBack(String data) {
        // This callback can be invoked multiple times from JavaScript.
        Log.d(TAG, "Persistent callback in Java called with: " + data);
        // Update UI or perform action based on the event data.
    }
});

JavaScript Side

In JavaScript, the handler receives the responseCallback as usual. The difference is that this callback can now be stored and reused.

var eventCallback = null; // Variable to store the persistent callback

bridge.registerHandler("startListeningToEvents", function(data, responseCallback) {
    console.log("Java wants to listen to events with ID: " + data);

    // Store the callback for later use
    eventCallback = responseCallback;

    // Send an initial confirmation
    eventCallback("Listening has started.");
});

// Later, when an event occurs, you can reuse the stored callback
document.getElementById('some-button').onclick = function() {
    if (eventCallback) {
        eventCallback("An event just happened!");
    }
};

JavaScript Calling Java with a Persistent Callback

The pattern is similar when the communication originates from JavaScript.

JavaScript Side

// Use callHandlerPersistent to keep the JS callback active.
WebViewJavascriptBridge.callHandlerPersistent("javaProgressHandler", { 'taskId': 123 }, function(response) {
    // This callback can be invoked multiple times from Java.
    console.log("Progress update from Java: " + response);
});

Java Side

Your Java handler will receive an OnBridgeCallback function. You must store this function's unique callbackId to send responses back later.

This is typically managed by adding a JavascriptInterface, as seen in the example app's MainJavascriptInterface.java.

// A simplified conceptual example
public class MyHandlers {
    private OnBridgeCallback progressCallback;

    public void registerProgressHandler() {
        webView.registerHandler("javaProgressHandler", new BridgeHandler() {
            @Override
            public void handler(String data, OnBridgeCallback function) {
                // Store the callback for later use
                progressCallback = function;
                // Send initial confirmation
                progressCallback.onCallBack("Progress tracking started.");
            }
        });
    }

    // When progress happens elsewhere in your app...
    public void updateProgress(int progress) {
        if (progressCallback != null) {
            progressCallback.onCallBack("Progress is now " + progress + "%");
        }
    }
}

Manual Management (JavaScript)

JavaScript also provides functions to manually register and remove persistent callbacks if you need finer control over their lifecycle.

// Manually register a persistent callback with a custom ID
var callbackId = "my_persistent_callback";
WebViewJavascriptBridge.registerPersistentCallback(callbackId, function(data) {
    console.log("Persistent callback '" + callbackId + "' called with: ", data);
});

// When you are done with it, remove it to prevent memory leaks
WebViewJavascriptBridge.removePersistentCallback(callbackId);