【问题标题】:How to embed Angular 2+ app into JavaFX WebView as a GUI only?如何仅将 Angular 2+ 应用程序作为 GUI 嵌入 JavaFX WebView?
【发布时间】:2018-11-30 14:56:03
【问题描述】:

我正在开发一个简单的 Java 客户端应用程序,我想为其 GUI 使用 HTML 和 Web 应用程序框架,而不是使用 Swing 或 JavaFX。

所以我正在使用 Angular 6(使用 angular-cli 生成)设计、构建和测试我的整个应用程序,并且我想使用 JavaFX WebView 和 WebEngine 组件来指向我的 Angular UI。

是否有任何简单/开箱即用/最佳实践的方法来连接所有内容?

我希望用户刚刚启动应用程序,甚至没有意识到他正在浏览 webby。如果可能的话,我什至不会启动任何网络服务器。我只想要一个独立的应用程序,并且仅将 Angular 框架用于 GUI 目的。

如果我必须启动一个网络服务器,我想在幕后做,不希望用户意识到。

问题

  • 我试图让 WebEngine 指向已构建的 Angular 应用程序,但 this does not work 使用 Angular-cli 开箱即用;
  • 我尝试使用com.sun.net.httpserver 但是网上所有的解释都只是说如何实现自己的虚拟HttpHandler,所以我无法理解如何绑定服务器到我实际构建的 Web 应用程序(位于我的项目路径中的文件夹中)或如何将我的 Web 应用程序部署到 HttpServer 中,以正确者为准。

【问题讨论】:

  • 我不明白为什么会有反对票,也许我错过了什么?

标签: java angular javafx webview angular-cli


【解决方案1】:

最终我设法通过使用嵌入式 Jetty 为我的 Angular 应用程序提供服务来解决这个问题。

@Slf4j
public class MyEmbeddedAngularApp extends Application {

    private static final String APPLICATION_TITLE = "My Embedded Angular Application";

    /**
     * The service executing the server.
     */
    private ExecutorService executor = Executors.newSingleThreadExecutor();

    @Override
    public void start(Stage stage) throws Exception {
        startServer();

        //Create the root AnchorPane
        AnchorPane root = new AnchorPane();

        //Initialize the Web UI
        WebView browser = new WebView();
        WebEngine webEngine = browser.getEngine();
        webEngine.load("http://localhost:2342");

        //add WebView to root
        root.getChildren().add(browser);

        //Setting up the scene
        Scene scene = new Scene(root);
        stage.setTitle(APPLICATION_TITLE);
        stage.setScene(scene);

        //Let the show begin!
        stage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }

    private void startServer() throws Exception {
        Server server = new Server(2342);

        WebAppContext webapp = new WebAppContext();
        webapp.setContextPath("/");
        File warFile = new File("./src/main/resources/my-app/dist/my-app");
        webapp.setWar(warFile.getAbsolutePath());

        server.setHandler(webapp);
        server.setStopAtShutdown(true);

        server.start();

        server.dumpStdErr();

        executor.submit(() -> {
            try {
                server.join();
            } catch (InterruptedException e) {
                log.error("Could not start the server");
            }
        });
    }

}

【讨论】:

    【解决方案2】:

    你可以使用webkit嵌入任何HTML应用程序——jvm的webkit版本是相对最新的,我上次检查时,可能缺少一些功能,可能是WebGL。

    为了让它工作,不要使用file:// 协议,创建自己的网络服务器是多余的。您只需定义自己的 protocl 并在 java 层中声明即可。

    示例在这里:Registering and using a custom java.net.URL protocol

    这是我的自定义协议实现:

    /**
     * Register a protocol handler for URLs like this: <code>myapp:///pics/sland.gif</code><br>
     */
    public class MyURLConnection extends URLConnection
    {
    
        protected MyURLConnection(URL url) {
            super(url);
        }
    
        private byte[] data;
    
        @Override
        public void connect() throws IOException
        {
            if (connected)
            {
                return;
            }
            loadImage();
            connected = true;
        }
    
        public String getHeaderField(String name)
        {
            if ("Content-Type".equalsIgnoreCase(name))
            {
                return getContentType();
            }
            else if ("Content-Length".equalsIgnoreCase(name))
            {
                return "" + getContentLength();
            }
            return null;
        }
    
        public String getContentType()
        {
            String fileName = getURL().getFile();
            String ext = fileName.substring(fileName.lastIndexOf('.')+1);
            switch(ext){
            case "html":return "text/html";
            case "jpg":
            case "png":return "image/"+ext;
            case "js": return "application/javascript";
            }
            return "text/"+ext;
             // TODO: switch based on file-type "image/" + ext
        }
    
        public int getContentLength()
        {
            return data.length;
        }
    
        public long getContentLengthLong()
        {
            return data.length;
        }
    
        public boolean getDoInput()
        {
            return true;
        }
    
        public InputStream getInputStream() throws IOException
        {
            connect();
            return new ByteArrayInputStream(data);
        }
    
        /**
         * Reads all bytes from an input stream and writes them to an output stream.
         */
        private static long copy(InputStream source, OutputStream sink)
            throws IOException
        {
            long nread = 0L;
            byte[] buf = new byte[8192];
            int n;
            while ((n = source.read(buf)) > 0) {
                sink.write(buf, 0, n);
                nread += n;
            }
            return nread;
        }
    
        private void loadImage() throws IOException
        {
            if (data != null)
            {
                return;
            }
    
                String fileName = getURL().getFile();
                ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
                copy(this.getClass().getResourceAsStream(fileName),byteArrayOutputStream);
               data = byteArrayOutputStream.toByteArray();
    
        }
    
        public OutputStream getOutputStream() throws IOException
        {
            // this might be unnecessary - the whole method can probably be omitted for our purposes
            return new ByteArrayOutputStream();
        }
    
        public java.security.Permission getPermission() throws IOException
        {
            return null; // we need no permissions to access this URL
        }
    
    }
    
    public class MyURLHandler extends URLStreamHandler
    {
    
        @Override
        protected URLConnection openConnection(URL url) throws IOException
        {
            return new MyURLConnection(url);
        }
    
    }
    
    public class MyURLStreamHandlerFactory implements URLStreamHandlerFactory
    {
    
        public URLStreamHandler createURLStreamHandler(String protocol)
        {
            if (protocol.equals("myapp"))
            {
                return new MyURLHandler();
            }
            return null;
        }
    
    }
    

    然后在你的 javafx 启动中注册协议:

    public void start(Stage stage) {
        URL.setURLStreamHandlerFactory(new MyURLStreamHandlerFactory());
        // etc. 
    

    并在您的 webkit 中使用 url myapp://anyhost/yourfile.html(不使用任何主机,使用您想要的任何内容或修改协议)

    编辑: 我在 github 上的项目(正在进行中)上做到了:你只需要启动 WebDesktop 主类。它使用 src/main/resources 的资源 https://github.com/pdemanget/blue-browser

    【讨论】:

    • 我能够使这个解决方案与一个虚拟的 myfile.html 一起工作。但是 它不适用于 Angular 应用程序,即当我让 WebEngine 加载 srcindex.html 时它不会起作用i> 文件夹或 angular-cli 创建的 dist/my-app 文件夹。
    • 您真的使用了文件以外的其他协议吗?它与 javascript、内容类型和同源 plicy 相关:您必须使用 application/javascript 内容类型为您的 js 提供服务,并具有相同的源策略。我的 github 示例适用于 javascript 项目,您的 Angular 项目的编译 JS 版本应该以相同的方式工作。
    • @DanieleRepici 您是否尝试过不使用文件协议?
    • 是的,我做到了,但不幸的是它没有用。 this problem 可能有关联吗?编辑:我不这么认为,因为那仍然提到了文件协议...您是否尝试过使用已编译的 Angular 应用程序的解决方案?
    • 对不起,我现在让它工作了,不知道发生了什么。感谢您的支持。
    猜你喜欢
    • 1970-01-01
    • 2016-11-16
    • 2014-03-04
    • 2017-03-20
    • 1970-01-01
    • 2016-04-20
    • 2013-05-29
    • 2011-02-06
    • 2018-01-24
    相关资源
    最近更新 更多