简体   繁体   English

JavaFXPorts jfxmobile-plugin 未找到特定于平台的目录

[英]JavaFXPorts jfxmobile-plugin isn't finding platform specific directories

I have a JavaFX-based interface which I'm trying to build on multiple platforms using JavaFXPorts.我有一个基于 JavaFX 的接口,我正在尝试使用 JavaFXPorts 在多个平台上构建它。 Most of the code is platform-independent, but I have some utilities that call platform-specific code, eg for file system locations on Android vs desktop.大多数代码与平台无关,但我有一些实用程序可以调用特定于平台的代码,例如用于 Android 与桌面上的文件系统位置。 For example, in the MCVE below: building with gradle desktop or gradle android , I get:例如,在下面的 MCVE: building with gradle desktopgradle android ,我得到:

src/main/java/com/example/project/MyApp.java:30: error: cannot find symbol
        platformGreeting = new Label("hello, " + Util.getPlatform());
                                                 ^
  symbol:   variable Util
  location: class MyScene

But if I put both source files ( MyApp.java and Util.java ) together in any of the source directories ( main , desktop , android ) and run the corresponding task, it will build.但是,如果我将两个源文件( MyApp.javaUtil.java )放在任何源目录( maindesktopandroid )中并运行相应的任务,它将构建。

In the examples I've seen, they always implement a platform-independent interface under src/main , then implement it in each of the platform-specific directories, but I don't see why this should be necessary.在我看到的示例中,它们总是在src/main下实现一个独立于平台的接口,然后在每个特定于平台的目录中实现它,但我不明白为什么这是必要的。

├── build.gradle
├── settings.gradle
└── src
    ├── android
    │   └── java
    │       └── com
    │           └── example
    │               └── project
    │                   └── Util.java
    ├── desktop
    │   └── java
    │       └── com
    │           └── example
    │               └── project
    │                   └── Util.java
    └── main
        └── java
            └── com
                └── example
                    └── project
                        └── MyApp.java

build.gradle:构建.gradle:

buildscript {
    repositories {
        jcenter()
    }

    dependencies {
        classpath 'org.javafxports:jfxmobile-plugin:1.3.10'
    }
}

apply plugin: 'org.javafxports.jfxmobile'

mainClassName = 'com.example.project.MyApp'

repositories {
    jcenter()
}

dependencies {
}

jfxmobile {
    ios {
        forceLinkClasses = [ 'com.example.project.**.*' ]
    }

    android {
        applicationPackage = 'com.example.project'
    }
}

settings.gradle:设置.gradle:

rootProject.name = 'project'

src/main/java/com/example/project/MyApp.java: src/main/java/com/example/project/MyApp.java:

package com.example.project;

import javafx.application.Application;
import javafx.geometry.HPos;
import javafx.geometry.VPos;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.scene.control.Label;
import javafx.scene.layout.Region;

public class MyApp extends Application {
    private Scene scene;

    @Override public void start(Stage stage) {
        stage.setTitle("MyApp");
        scene = new Scene(new MyScene(), 750, 500);
        stage.setScene(scene);
        stage.show();
    }

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

class MyScene extends Region {
    final Label platformGreeting = new Label("hello, " + Util.getPlatform());

    public MyScene() {
        getChildren().add(platformGreeting);
    }

    @Override protected void layoutChildren() {
        double w = getWidth();
        double h = getHeight();
        layoutInArea(platformGreeting, 0, 0, w, h, 0, HPos.CENTER, VPos.CENTER);
    }

    @Override protected double computePrefWidth(double height) {
        return 750;
    }

    @Override protected double computePrefHeight(double width) {
        return 500;
    }
}

src/desktop/java/com/example/project/Util.java: src/desktop/java/com/example/project/Util.java:

package com.example.project;

public final class Util {
    private Util() {
        throw new UnsupportedOperationException();
    }

    public static final String getPlatform() {
        return "desktop";
    }
}

src/android/java/com/example/project/Util.java: src/android/java/com/example/project/Util.java:

package com.example.project;

public final class Util {
    private Util() {
        throw new UnsupportedOperationException();
    }

    public static final String getPlatform() {
        return "android";
    }
}

By the way the source sets are defined in JavaFXPorts, main is shared by all the platforms, and then each specific package is used by the given platform, ie desktop only on Desktop.顺便说一下,JavaFXPorts 中定义了源集, main由所有平台共享,然后每个特定的包由给定平台使用,即desktop仅在桌面上使用。

As main code is shared by all platforms, it can't contain specific platform code, and it doesn't really know about the platform code that was added to any of the platforms.由于main代码是所有平台共享的,它不能包含特定的平台代码,并且它并不真正知道添加到任何平台的平台代码。

This is why you get the compilation exception when trying to add Util to main .这就是为什么在尝试将Util添加到main时会出现编译异常的原因。

Solution 1解决方案1

A first approach can be done with Class.forName(className) .第一种方法可以使用Class.forName(className) On runtime, it will try to find the given class in the class path.在运行时,它会尝试在类路径中找到给定的类。

So this should initially work:所以这应该最初工作:

    try {
        Class<?> util = Class.forName("com.example.project.Util");
    } catch (ClassNotFoundException ex) {
        Logger.getLogger(BasicView.class.getName()).log(Level.SEVERE, null, ex);
    }

but then you have a problem: you can't really cast to Util in the main package.但是你有一个问题:你不能在主包中真正转换为Util

One possible solution is just using reflection:一种可能的解决方案是使用反射:

    try {
        Class<?> util = Class.forName("com.example.project.Util");
        Object newInstance = util.newInstance();
        Method method = util.getDeclaredMethod("getPlatform");
        String platform = (String) method.invoke(newInstance);
        System.out.println("Platform: " + platform));
    } catch (ClassNotFoundException | NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
        Logger.getLogger(BasicView.class.getName()).log(Level.SEVERE, null, ex);
    }

Where I modified Util for convenience:为方便起见,我修改了Util的地方:

public class Util {

    public Util() {
    }

    public final String getPlatform() {
        return "desktop";
    }
}

While this works perfectly fine, it is not very easy to work with.虽然这工作得很好,但使用起来并不容易。

Solution 2解决方案2

So let's add some neutral API to the main package, something that we can call more easily: an interface with our method of interest:因此,让我们向main包添加一些中性 API,我们可以更轻松地调用这些 API:带有我们感兴趣的方法的接口:

public interface Platform {

    String getPlatform();
}

and now let's implement it in our platform classes:现在让我们在我们的平台类中实现它:

public class Util implements Platform {

    public Util() {
    }

    @Override
    public final String getPlatform() {
        return "desktop";
    }
}

Now things get easier, as now you can get a Platform instance, and call getPlatform() :现在事情变得更容易了,因为现在您可以获得一个Platform实例,并调用getPlatform()

    try {
        Class<?> util = Class.forName("com.gluonhq.forname.Util");
        Platform platform = (Platform) util.newInstance();
        System.out.println("Platform: " + platform.getPlatform());
    } catch (ClassNotFoundException | InstantiationException | IllegalAccessException ex) {
        Logger.getLogger(BasicView.class.getName()).log(Level.SEVERE, null, ex);
    }

Solution 3: Charm Down解决方案 3:魅力四射

Charm Down is an open source library that provides different native services, with a neutral API that can be used from the main package, on top of the required platform specific code (Java for Desktop, Android or Objective-C for iOS). Charm Down 是一个开源库,可提供不同的本机服务,具有可从main包中使用的中性 API,以及所需的平台特定代码(用于桌面的 Java、Android 或用于 iOS 的 Objective-C)。

Using the Gluon plugin for your IDE, and creating a project that makes use of the jfxmobile plugin will include a few of the services included in Charm Down, as you can see in the build.gradle file:为您的 IDE 使用jfxmobile插件,并创建一个使用jfxmobile插件的项目将包括 Charm Down 中包含的一些服务,如您在build.gradle文件中build.gradle

jfxmobile {
    downConfig {
        version = '3.7.0'
        plugins 'display', 'lifecycle', 'statusbar', 'storage'
    }
    ...
}

If you have a look at how these services are implemented, there is a core plugin with several classes :如果你看看这些服务是如何实现的,有一个核心插件有几个

  • Platform
  • Services
  • ServicesFactory ... ServicesFactory ...

Now on your project, if you include Charm Down dependencies, you can make use of Platform.getCurrent().name() .现在在您的项目中,如果您包含 Charm Down 依赖项,则可以使用Platform.getCurrent().name()

System.out.println("Platform: " + Platform.getCurrent().name());

Not only that, you can get any service just with this:不仅如此,您还可以通过以下方式获得任何服务:

Services.get(DisplayService.class)
            .ifPresent(display -> System.out.println("Resolution: " + display.getScreenResolution()));

I'd suggest you have a look at how these classes are implemented.我建议你看看这些类是如何实现的。 And check the different services available so you don't implement them again.并检查可用的不同服务以免再次实施它们。 Many of the samples available here make use of one or more of those services.此处提供的许多示例都使用了这些服务中的一项或多项。

And if you want to implement a new one, I'd suggest you have a look at the Go Native sample ( code and tutorial ).如果你想实现一个新的,我建议你看看Go Native示例( 代码教程)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM