Implementing a communication channel between a Vue.js application hosted in an Android WebView and native networking components requires a properly configured JavaScript bridge. The native layer handles HTTP operations on background threads and routes the results back to the JavaScript context.
Native Bridge Implementation
Create a dedicated class to expose network capabilities to the WebView. Use an ExecutorService to offload I/O operations and ensure the JavaScript callback executes on the main thread.
public class NativeApiBridge {
private final WeakReference<WebView> webViewRef;
private final ExecutorService ioDispatcher = Executors.newSingleThreadExecutor();
public NativeApiBridge(WebView webView) {
this.webViewRef = new WeakReference<>(webView);
}
@JavascriptInterface
public void executeRemoteCall(String endpoint, String jsCallbackName) {
ioDispatcher.execute(() -> {
String responsePayload = fetchFromNetwork(endpoint);
WebView targetView = webViewRef.get();
if (targetView != null) {
targetView.post(() -> {
String escapedData = responsePayload.replace("'", "\\'");
String invocation = String.format("%s('%s');", jsCallbackName, escapedData);
targetView.evaluateJavascript(invocation, null);
});
}
});
}
private String fetchFromNetwork(String url) {
// Replace with OkHttp, Retrofit, or HttpURLConnection
try {
Thread.sleep(300); // Simulate network latency
return "{\"code\": 200, \"payload\": \"Native request successful\"}";
} catch (InterruptedException e) {
return "{\"code\": 500, \"payload\": \"Thread interrupted\"}";
}
}
}
WebView Configuration
Inject the bridge object into the WebView before loading the Vue application. JavaScript execution must be explicitly enabled, and DOM storage should be activated for modern framework compatibility.
WebView container = findViewById(R.id.web_view_host);
WebSettings config = container.getSettings();
config.setJavaScriptEnabled(true);
config.setDomStorageEnabled(true);
config.setAllowFileAccess(false);
container.addJavascriptInterface(new NativeApiBridge(container), "NativeBridge");
container.loadUrl("file:///android_asset/dist/index.html");
Vue.js Integration
Within the Vue component, attach a global callback function to the window object. The native layer will invoke this function by name once the network operation completes. Clean up the reference when the component unmounts to prevent memory leaks.
<template>
<section>
<button @click="requestNativeData" :disabled="isProcessing">
{{ isProcessing ? 'Processing...' : 'Fetch via Android' }}
</button>
<div v-if="apiResult" class="output-panel">
{{ apiResult }}
</div>
</section>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue';
const apiResult = ref(null);
const isProcessing = ref(false);
const bridgeCallbackKey = 'onNativeDataReceived';
const requestNativeData = () => {
if (window.NativeBridge) {
isProcessing.value = true;
window.NativeBridge.executeRemoteCall('https://api.internal/data', bridgeCallbackKey);
}
};
const handleBridgeResponse = (rawString) => {
isProcessing.value = false;
try {
apiResult.value = JSON.parse(rawString);
} catch (parseError) {
apiResult.value = { error: 'Malformed response' };
}
};
onMounted(() => {
window[bridgeCallbackKey] = handleBridgeResponse;
});
onUnmounted(() => {
delete window[bridgeCallbackKey];
});
</script>
Ensure the Android manifest includes the INTERNET permission. When targeting API level 17 or higher, the @JavascriptInterface annotation is mandatory for any method exposed to JavaScript. Network operations must never block the main thread, and all evaluateJavascript calls require execution on the UI thread to prevent runtime exceptions.