这可能是一个微不足道的问题,但我对 Swing 和 JavaFX 很陌生。
我正在尝试为几乎完全批处理的应用程序构建 FB OAuth 登录表单。我知道我必须打开一个 WebView 来获取“代码”,然后与“ token ”进行交换。为此,我想使用 JFXPanel 作为一个使用 WebEngine 的简单内部 Web 浏览器。
我的是一个简单的数据处理批处理作业,仅在主线程中完成艰苦的工作。该代码必须嵌入到另一代码中,在该代码中我无法控制除主线程之外的线程。为了简单起见,假设我必须使用这个简单的 main 方法调用 WebView:
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
ComponentBrowser browser = new ComponentBrowser();
browser.setVisible(true);
browser.loadURL("https://www.facebook.com/connect/login_success.html");
}
});
}
ComponentBrowser 已定义 here .
我需要 WebView 同步,这是最佳流程:
- 主线程进入休眠状态
- 用户导航 WebView,直到获得确认码
- 代码以某种方式被推回到主线程
- 主线程恢复运行
我缺少的是如何向调用者返回响应的内容(正文中只有“成功”一词)和重定向后的网址(具有“code”查询字符串参数) 。然后,调用者应该优雅地关闭窗口。
问题是,除了 hibernate/恢复主线程之外,我无法控制主线程。对于具有 main() 代码的类也是如此:该代码是由我必须将代码嵌入其中的数据处理平台自动生成的。因此,我无法修改 main() 本身之外的那部分代码(向类添加方法或监听器是不可能的,但我可以向 main() 代码本身添加语句)
这就是为什么 JavaFX 和批处理代码之间的通信必须尽可能简单,甚至微不足道。
最佳答案
您可以使用 Web 引擎的负载工作线程的状态属性注册监听器:
engine.getLoadWorker().stateProperty().addListener((obs, oldState, newState) -> {
if (newState == Worker.State.SUCCEEDED) {
// document is successfully loaded
}
});
当从服务器收到响应时,将会收到通知。
要检索内容,您可以使用 WebEngine.getDocument()
,它返回 org.w3c.dom.Document
目的。 dom API 有点笨拙(imo),但我认为在你的情况下你需要的是
String content = engine.getDocument().getDocumentElement().getTextContent();
然后您可以检查它是否“成功”
。
线程部分变得棘手。如果我理解正确的话,您基本上在这里运行一个几乎完全非 GUI 的应用程序,但您只需要一点 UI 来在启动时验证登录,然后想要返回主线程并执行代码。
如果这完全是 Swing(用于登录验证),我将按如下方式处理:
从主线程中,调用 SwingUtilities.invokeAndWait(...)
并传入一个显示模式对话框的 Runnable
。 invokeAndWait
将阻止主线程的执行,直到 Runnable
(在 AWT 线程上执行)完成。通过使用模态对话框,您可以阻止代码的执行,直到对话框被关闭为止,因此现在 Runnable 直到对话框被关闭才会完成,因此主线程将阻塞,直到对话框被关闭为止。因此,现在您所需要做的就是安排在验证登录时关闭该对话框。
通过在 Swing 对话框中放置一个 JFXPanel
,这会变得稍微复杂一些。现在,您需要使用 Platform.runLater(...)
将修改 JavaFX 组件的任何代码传递给 JavaFX 应用程序线程,并且如果您更改任何 Swing 组件(例如关闭对话框),您需要使用 SwingUtilities.runLater(...)
将其交还给 AWT 事件线程。
这里有一个快速演示。我只是在这里使用了一个按钮,按下该按钮会将“成功”加载到 WebView 中。显然,您可以通过加载真实的登录屏幕来替换它。
import java.awt.Window;
import javafx.application.Platform;
import javafx.concurrent.Worker;
import javafx.embed.swing.JFXPanel;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.web.WebEngine;
import javafx.scene.web.WebView;
import javax.swing.JDialog;
import javax.swing.SwingUtilities;
import org.w3c.dom.Document;
public class WaitingForLogin {
public WaitingForLogin() throws Exception {
System.out.println("Showing window...");
// Run initAndShowUI() on AWT event thread. Block until that is complete:
SwingUtilities.invokeAndWait(() -> initAndShowUI());
System.out.println("Now running application");
for (int i=1; i <=10; i++) {
System.out.println("Counting: "+i);
Thread.sleep(500);
}
}
public void initAndShowUI() {
JDialog dialog = new JDialog((Window)null);
dialog.setModal(true);
JFXPanel jfxPanel = new JFXPanel();
Platform.runLater(() -> initJFX(jfxPanel, dialog));
dialog.add(jfxPanel);
dialog.setSize(400, 400);
dialog.setLocationRelativeTo(null);
// Since the dialog is modal, this will block execution (of the AWT event thread)
// until the dialog is closed:
dialog.setVisible(true);
}
private void initJFX(JFXPanel jfxPanel, Window dialog) {
// Create a web view:
WebView webView = new WebView();
WebEngine engine = webView.getEngine();
// Check for a new document being loaded. If the document just contains the
// text "Success", then close the dialog (unblocking all threads waiting for it...)
engine.getLoadWorker().stateProperty().addListener((obs, oldState, newState) -> {
if (newState == Worker.State.SUCCEEDED) {
Document doc = engine.getDocument();
if ("Success".equals(doc.getDocumentElement().getTextContent())) {
// Close dialog: this must be done on the AWT event thread
SwingUtilities.invokeLater(() -> dialog.dispose());
}
}
});
// Just for testing
// simulate login with simple button:
Button button = new Button("Login");
button.setOnAction(event -> engine.loadContent("Success", "text/plain"));
HBox controls = new HBox(button);
controls.setAlignment(Pos.CENTER);
controls.setPadding(new Insets(10));
jfxPanel.setScene(new Scene(new BorderPane(webView, null, null, controls, null)));
}
public static void main(String[] args) throws Exception {
new WaitingForLogin();
}
}
关于java - 使用 JFXPanel 为 native 应用程序构建 Facebook OAuth 登录流程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28946938/