Of course! This is a fundamental concept in PhoneGap (now known as Apache Cordova). Understanding the communication between your app's JavaScript (the "web view") and its native Java code (for Android) is key to building powerful hybrid apps.

Let's break it down into the core concepts, a practical example, and best practices.
The Core Concept: The Bridge
Imagine your PhoneGap app is a web page running inside a native "container" (the WebView). This container provides special abilities that a normal web page in a browser doesn't have, like accessing the camera, GPS, or contacts.
The communication between the JavaScript in your web page and the native Java code of the Android container happens through a bridge. This bridge is managed by Cordova.
There are two main directions for this communication:

| Direction | JavaScript to Native Java | Native Java to JavaScript |
|---|---|---|
| Purpose | Calling a native feature. Your JS code says, "Hey Android, I need to use the camera." | Sending data back to the web view. The Android code says, "Here is the photo from the camera." |
| Mechanism | Cordova Plugin. You call a predefined JavaScript function, which Cordova routes to the corresponding Java code. | Cordova WebView. The Java code uses a reference to the WebView to execute JavaScript. |
JavaScript to Native Java (The Most Common Case)
This is when your web page needs to perform an action that only the native OS can handle. You do this using Cordova Plugins.
How it Works:
-
You Call a JS Function: In your JavaScript code, you call a function provided by a plugin. For example, to get the current GPS coordinates, you'd use:
navigator.geolocation.getCurrentPosition(onSuccess, onError);
-
Cordova Intercepts the Call: The
navigator.geolocationobject is not a standard browser object. It's provided by the Cordova Geolocation plugin. When you callgetCurrentPosition, Cordova's JavaScript engine intercepts this call. -
Cordova Routes to Native Code: Cordova looks at the plugin definition (
plugin.xml) and sees thatnavigator.geolocation.getCurrentPositionshould be handled by the native Java code in the plugin.
(图片来源网络,侵删) -
Java Code Executes: The corresponding Java method in the plugin is executed on the Android UI thread. This is where the actual native Android API calls are made (e.g.,
LocationManager). -
Result is Passed Back: The Java code doesn't return the result directly to the JS call. Instead, it passes the result back to the JavaScript engine, which then triggers the success or error callback you provided (
onSuccessoronDelete).
Anatomy of a Simple Plugin (Example: Custom Toast)
Let's create a super simple plugin that shows a native Android "Toast" message from JavaScript.
Step 1: The JavaScript Interface (www/toast.js)
This file defines the function that your app will call.
// toast.js
var exec = require('cordova/exec');
// The function name (show) is what you'll call in JS.
// The first string ('Toast') is the service name that maps to the Java class.
// The second string ('show') is the action name that maps to the Java method.
exports.show = function(message, success, error) {
cordova.exec(success, error, 'Toast', 'show', [message]);
};
Step 2: The Java Plugin Class (src/org/apache/cordova/plugin/Toast.java)
This is the native code that actually does the work.
// Toast.java
package org.apache.cordova.plugin;
import org.apache.cordova.CordovaPlugin;
import org.apache.cordova.CallbackContext;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.widget.Toast; // Import the Android Toast class
public class Toast extends CordovaPlugin {
// This method is called from JavaScript when you call cordova.exec()
@Override
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
// Check if the action we're trying to execute is "show"
if ("show".equals(action)) {
String message = args.getString(0); // Get the first argument from the JS call
// Create and show the native Android Toast
android.widget.Toast.makeText(this.cordova.getActivity(), message, Toast.LENGTH_SHORT).show();
// Signal that the action was successful
callbackContext.success("Toast shown successfully");
return true; // Indicates that the action was handled
}
return false; // If the action is not recognized
}
}
Step 3: Register the Plugin (plugin.xml)
This file tells Cordova about your plugin's structure.
<!-- plugin.xml -->
<plugin xmlns="http://www.phonegap.com/ns/plugins/1.0"
id="cordova-plugin-toast"
version="0.0.1">
<js-module src="www/toast.js" name="Toast">
<clobbers target="window.toast" /> // Makes the 'toast' object available globally
</js-module>
<platform name="android">
<config-file parent="/*" target="res/xml/config.xml">
<feature name="Toast">
<param name="android-package" value="org.apache.cordova.plugin.Toast" />
</feature>
</config-file>
<source-file src="src/org/apache/cordova/plugin/Toast.java" target-dir="src/org/apache/cordova/plugin" />
</platform>
</plugin>
How to Use It in Your App:
After adding this plugin to your project, you can call it from any JavaScript file:
// In your index.js or any other JS file
document.addEventListener('deviceready', onDeviceReady, false);
function onDeviceReady() {
// Now you can use the plugin
window.toast.show("Hello from Native Java!", function() {
console.log("Toast was shown!");
}, function(err) {
console.error("Error showing toast: " + err);
});
}
Native Java to JavaScript
This is less common but useful for scenarios where a native event needs to update the UI of your web view. The classic example is a background service.
How it Works:
-
Native Event Occurs: Something happens in Java-land that your web view needs to know about. For example, a background
Servicedetects that the device has moved 100 meters. -
Java Calls JavaScript: The Java code gets a reference to the main
CordovaWebViewand uses itsloadUrl()method to execute a snippet of JavaScript. -
JavaScript Executes: The JavaScript code runs in the context of your web page. This code is typically an event dispatcher.
Example: Broadcasting from a Service
Let's say you have a LocationService that runs in the background.
Java Code (LocationService.java)
// LocationService.java
// ... imports ...
public class LocationService extends Service {
private CordovaInterface cordova;
private CordovaWebView webView;
// ... onCreate, onStartCommand etc. ...
public void updateLocation(double lat, double lng) {
if (webView != null) {
// This is the key part!
// We create a JavaScript string that calls a global function.
String js = "cordova.fireDocumentEvent('locationUpdate', {lat: " + lat + ", lng: " + lng + "});";
// Execute the JavaScript on the UI thread
webView.loadUrl("javascript:" + js);
}
}
}
JavaScript Code (www/js/main.js)
Your web page needs to listen for this custom event.
// main.js
document.addEventListener('deviceready', onDeviceReady, false);
function onDeviceReady() {
// Listen for the custom event from the native service
document.addEventListener('locationUpdate', onLocationUpdate, false);
}
function onLocationUpdate(data) {
console.log("Received location update from native service!");
console.log("Latitude: " + data.lat);
console.log("Longitude: " + data.lng);
// Update your map or UI here
updateMap(data.lat, data.lng);
}
Key Takeaways & Best Practices
-
Use Plugins for Everything: Don't try to write your own JS-to-Java bridge from scratch for common features. Use the Cordova Plugin Registry. If a plugin doesn't exist, then create one following the official guides.
-
Asynchronous is King: All communication between JS and Java is asynchronous. You must use callbacks (Promises are a modern way to handle this) to get results back. You cannot do
var result = nativeFunction();. -
Keep it Simple: The bridge has some overhead. Don't use it for high-frequency data transfer. For example, don't send every single GPS coordinate via the bridge. Instead, have the native service accumulate data and send it in batches.
-
Threading: Java code executed from
cordova.exec()runs on the UI thread. If your native code is going to take a long time (e.g., a network request), you must create a new background thread (AsyncTask,Thread,ExecutorService) to avoid blocking the UI and making your app freeze. -
Error Handling: Always provide both a success and an error callback in your JavaScript calls and handle them appropriately in your Java code. Use
callbackContext.error("message")to report failures back to JavaScript.
