JCEF 和 Java 之间的通信方法
JCEF (Java Chromium Embedded Framework) 是一个将 Chromium 浏览器嵌入 Java 应用程序的框架。以下是 JCEF 中实现 Java 和 JavaScript 双向通信的几种主要方法。
1. 基本通信架构
JCEF 中的通信主要通过以下方式实现:
- Java → JavaScript:通过
executeJavaScript
方法 - JavaScript → Java:通过注册 Java 处理器供 JavaScript 调用
2. Java 调用 JavaScript
基本调用方式
// 获取浏览器对象
CefBrowser browser = ...;
// 执行JavaScript代码
browser.executeJavaScript("alert('Hello from Java!');", browser.getURL(), 0);
// 调用函数并获取返回值
browser.executeJavaScript(
"document.getElementById('myElement').value;",
browser.getURL(),
0
);
带回调的调用
browser.executeJavaScript(
"function getData() { return {name: 'John', age: 30}; } getData();",
browser.getURL(),
0,
new CefV8Value.CefV8ValueHandler() {
@Override
public boolean execute(String name, CefV8Value object, CefV8Value[] arguments) {
// 处理返回值
return true;
}
}
);
3. JavaScript 调用 Java
3.1 使用 MessageRouter
这是推荐的方式,支持异步通信。
Java 端设置:
// 创建消息路由器
CefMessageRouter msgRouter = CefMessageRouter.create();
// 添加处理器
msgRouter.addHandler(new CefMessageRouter.CefMessageRouterHandler() {
@Override
public boolean onQuery(CefBrowser browser, long queryId, String request,
boolean persistent, CefQueryCallback callback) {
if (request.equals("getUserData")) {
// 处理请求
callback.success("{\"name\":\"John\", \"age\":30}");
return true;
}
return false;
}
@Override
public void onQueryCanceled(CefBrowser browser, long queryId) {
// 查询被取消
}
}, true);
// 将路由器添加到客户端处理器
clientHandler.addMessageRouter(msgRouter);
JavaScript 端调用:
window.cefQuery({
request: "getUserData",
onSuccess: function(response) {
console.log("Received data:", JSON.parse(response));
},
onFailure: function(error_code, error_message) {
console.error("Error:", error_code, error_message);
}
});
3.2 注册 Java 对象到 JavaScript
Java 端注册:
CefApp.getInstance().registerJsExtension(
"java_handler",
"var myJavaHandler = {};" +
"myJavaHandler.callMethod = function(name, args) {" +
" native function callJavaMethod();" +
" return callJavaMethod(name, args);" +
"};"
);
// 在客户端处理器中
clientHandler.addHandler(new CefClientHandler() {
@Override
public boolean onProcessMessageReceived(
CefBrowser browser, CefProcessId sourceProcess, CefProcessMessage message) {
if (message.getName().equals("callJavaMethod")) {
// 处理来自JS的调用
return true;
}
return false;
}
});
JavaScript 端调用:
myJavaHandler.callMethod("methodName", {param1: "value"});
4. 双向通信完整示例
Java 部分
public class MyCefApp {
private CefBrowser browser;
private CefMessageRouter msgRouter;
public void initBrowser(JFrame frame) {
CefApp cefApp = CefApp.getInstance();
CefClient client = cefApp.createClient();
// 创建消息路由器
msgRouter = CefMessageRouter.create();
msgRouter.addHandler(new MessageRouterHandler(), true);
client.addMessageRouter(msgRouter);
// 创建浏览器
browser = client.createBrowser("http://localhost:8080", false, false);
frame.add(browser.getUIComponent(), BorderLayout.CENTER);
// 注入Java对象
injectJavaObject();
}
private void injectJavaObject() {
String jsCode = "window.javaBridge = {" +
"invokeJava: function(request, callback) {" +
" window.cefQuery({" +
" request: JSON.stringify(request)," +
" onSuccess: function(response) { callback(null, JSON.parse(response)); }," +
" onFailure: function(code, msg) { callback(new Error(msg), null); }" +
" });" +
"}" +
"};";
browser.executeJavaScript(jsCode, browser.getURL(), 0);
}
private class MessageRouterHandler implements CefMessageRouter.CefMessageRouterHandler {
@Override
public boolean onQuery(CefBrowser browser, long queryId, String request,
boolean persistent, CefQueryCallback callback) {
try {
JSONObject req = new JSONObject(request);
String method = req.getString("method");
JSONObject params = req.getJSONObject("params");
// 处理方法调用
if ("addNumbers".equals(method)) {
int result = params.getInt("a") + params.getInt("b");
callback.success("{\"result\": " + result + "}");
} else {
callback.failure(404, "Method not found");
}
} catch (Exception e) {
callback.failure(500, e.getMessage());
}
return true;
}
@Override
public void onQueryCanceled(CefBrowser browser, long queryId) {
System.out.println("Query cancelled: " + queryId);
}
}
public void callJavaScript(String functionName, JSONObject data) {
String js = functionName + "(" + data.toString() + ");";
browser.executeJavaScript(js, browser.getURL(), 0);
}
}
JavaScript 部分
// 调用Java方法
function addNumbers(a, b, callback) {
javaBridge.invokeJava({
method: "addNumbers",
params: {a: a, b: b}
}, callback);
}
// 使用示例
addNumbers(5, 7, (err, result) => {
if (err) {
console.error("Error:", err);
} else {
console.log("Result:", result.result);
}
});
// 接收Java调用
window.onJavaMessage = function(data) {
console.log("Received from Java:", data);
return "Ack";
};
5. 安全注意事项
- 输入验证:始终验证来自 JavaScript 的输入
- 异常处理:妥善处理通信中的异常
- 性能考虑:大量数据传输可能影响性能
- 资源清理:在窗口关闭时移除消息处理器
6. 调试技巧
-
启用远程调试:
CefApp.getInstance().setRemoteDebuggingPort(8088);
然后访问
http://localhost:8088
-
日志记录:实现
CefRenderHandler
来捕获控制台输出 -
错误处理:重写
CefLoadHandler
来捕获页面加载错误