【问题标题】:When does a JavaFX program call the start method?JavaFX 程序何时调用 start 方法?
【发布时间】:2014-05-19 04:26:55
【问题描述】:

我正在学习 JavaFX。而且我只能在 main 方法中看到一个 launch(args) 方法。当我调试到启动时。我看不到任何调用 start() 的语句。那么 JavaFX 程序什么时候调用 start 方法? 这是 launch(args) 源代码。

    public static void launch(String... args) {
    // Figure out the right class to call
    StackTraceElement[] cause = Thread.currentThread().getStackTrace();

    boolean foundThisMethod = false;
    String callingClassName = null;
    for (StackTraceElement se : cause) {
        // Skip entries until we get to the entry for this class
        String className = se.getClassName();
        String methodName = se.getMethodName();
        if (foundThisMethod) {
            callingClassName = className;
            break;
        } else if (Application.class.getName().equals(className)
                && "launch".equals(methodName)) {

            foundThisMethod = true;
        }
    }

    if (callingClassName == null) {
        throw new RuntimeException("Error: unable to determine Application class");
    }

    try {
        Class theClass = Class.forName(callingClassName, true,
                           Thread.currentThread().getContextClassLoader());
        if (Application.class.isAssignableFrom(theClass)) {
            Class<? extends Application> appClass = theClass;
            LauncherImpl.launchApplication(appClass, args);
        } else {
            throw new RuntimeException("Error: " + theClass
                    + " is not a subclass of javafx.application.Application");
        }
    } catch (RuntimeException ex) {
        throw ex;
    } catch (Exception ex) {
        throw new RuntimeException(ex);
    }
}

【问题讨论】:

  • 只是对JavaFX有点熟悉,我猜launch方法将一个事件放到将调用start的事件队列中,这样start就可以保证在内部被调用它的 gui 线程的上下文...JavaFX 怎么称呼它...
  • 你在 try/catch 块中查看过LauncherImpl.launchApplication() 吗?
  • 是的,我试过了,它停在了 launchLatch.await();
  • @deathlee 这意味着有另一个线程在某处工作......想知道那是什么线程。

标签: java javafx


【解决方案1】:

LauncherImpl 调用Application#start,但这样做是通过PlatformImpl.runAndWait 将实际调用置于JavaFX 事件队列中。这是在Preloader 启动后完成的

Application#launch 调用LauncherImpl.launchApplication,它会创建一个Thread 并调用launchApplication1launchApplication,然后通过CountDownLatch 等待这个Thread 终止。

这个Thread 然后调用LauncherImpl.launchApplication1,它启动Preloader,如果指定,然后,基于关于Preloader 调用Application#start 的状态的数字决策,包装在runAndWait 调用中,以确保在 JavaFX 的 GUI/事件队列线程的上下文中调用 start...

这是基于 Java 8 的

更新...

LauncherImpl.launcherApplication1 调用的theApp.start 是您的Application 的实例。

Application 会通过 StackTrace... 查找您的课程名称...

StackTraceElement[] cause = Thread.currentThread().getStackTrace();

boolean foundThisMethod = false;
String callingClassName = null;
for (StackTraceElement se : cause) {
    // Skip entries until we get to the entry for this class
    String className = se.getClassName();
    String methodName = se.getMethodName();
    if (foundThisMethod) {
        callingClassName = className;
        break;
    } else if (Application.class.getName().equals(className)
            && "launch".equals(methodName)) {

        foundThisMethod = true;
    }
}

这将获取您的类的名称,然后使用 Class.forName 创建一个 Class 实例并将其传递给 LauncherImpl...

launcherApplication1 然后构造该类的一个新实例并将其分配给引用theApp,它是您的Application 的一个实例

PlatformImpl.runAndWait(new Runnable() {
        @Override public void run() {
            try {
                Constructor<? extends Application> c = appClass.getConstructor();
                app.set(c.newInstance());
                // Set startup parameters
                ParametersImpl.registerParameters(app.get(), new ParametersImpl(args));
                PlatformImpl.setApplicationName(appClass);
            } catch (Throwable t) {
                System.err.println("Exception in Application constructor");
                constructorError = t;
                error = true;
            }
        }
    });
}
final Application theApp = app.get();

然后它继续调用theApp.start,它正在调用你的Applicationstart 方法......我知道很奇怪,但它就在那里

【讨论】:

    【解决方案2】:

    JavaFX 程序既不需要 main() 方法,也不需要调用 Application.launch() 方法。

    您可以从应用程序中删除main() 方法,java application launcher 将直接在应用程序上调用init()(在启动器线程上)和start()(在JavaFX 应用程序线程上)方法。执行此操作时,将跳过在其他一些线程中讨论的整个 LauncherImpl 进程(以及通过 StackTrace 查找启动的类的奇怪确定)。

    请参阅相关的 Java 增强提案 JEP 153: "Enhance the java command-line launcher to launch JavaFX applications." 及其随附的问题跟踪链接 JDK-8001533 "java launcher must launch javafx applications"

    当然,如果除了 start() 方法之外,您确实还有一个 main() 方法,那么代码目前将沿着 MadProgrammer 在他的回答中概述的路径。

    在编写 JavaFX 程序时,最好假设永远不会调用 main()(尽管实际上,如果您不部署为小程序,很可能会调用 main() 只是为了阻止人们完全糊涂了)。

    Application javadoc中也描述了这个过程:

    Java 启动器加载并初始化指定的应用程序 JavaFX 应用程序线程上的类。如果没有 main 方法 Application 类,或者如果 main 方法调用 Application.launch(),然后是Application的一个实例 在 JavaFX 应用程序线程上构建。

    【讨论】:

      【解决方案3】:

      在发帖时,LauncherImpl 的源代码是here,第 837 行调用了start(...) 方法。源代码中的那段很可怕……

      【讨论】:

      • 是的,情况很严峻,我的老师告诉我不要在意。但我只是好奇......
      • 你的老师是对的。有一点你假设他们知道他们在做什么并且已经正确地实施了...... :)
      • 它也出现在第 755 行,但取决于Preloader...的使用和状态...
      • 是的,我看到了。在绝大多数情况下,没有预加载器(其中一个好主意是假设我们会弄清楚整个 Web 部署并使其工作......),所以除了少数几个之外,所有的代码都被跳过了案例。
      【解决方案4】:

      添加到 MadProgrammer 的答案:

      Application.class,第 241 行:

      public static void launch(String... args) {
          //...
              if (Application.class.isAssignableFrom(theClass)) {
                  Class<? extends Application> appClass = theClass;
                  LauncherImpl.launchApplication(appClass, args); // <-- This is where the application is launched
      
          //...
      }
      

      到这里(LauncherImpl.class 的第 118 行):

      public static void launchApplication(final Class<? extends Application> appClass,
              final String[] args) {
          launchApplication(appClass, savedPreloaderClass, args);
      }
      

      这里是什么(第 158 行):

      public static void launchApplication(final Class<? extends Application> appClass,
              final Class<? extends Preloader> preloaderClass,
              final String[] args) {
          //.......
          Thread launcherThread = new Thread(new Runnable() {
              @Override public void run() {
                  try {
                      launchApplication1(appClass, preloaderClass, args); // <-- go here
                  } catch (RuntimeException rte) {
          //....
      }
      

      第 674 行的一个 loooong 方法:

      private static void launchApplication1(final Class<? extends Application> appClass,
              final Class<? extends Preloader> preloaderClass,
              final String[] args) throws Exception {
          //... Lots of stuff 
          // Eventually, on line 755:
          currentPreloader.start(primaryStage);
      
          // More things for cases where the previous start() call isn't appropriate
          // Line 773:
          final AtomicReference<Application> app = new AtomicReference<>();
          // Line 790/791:
          Constructor<? extends Application> c = appClass.getConstructor(); // Gets constructor for your class
          app.set(c.newInstance()); // Creates a new instance of your class
          // Line 803:
          final Application theApp = app.get();
          // Line 837:
          theApp.start(primaryStage);
      }
      

      因为你的 JavaFX 项目扩展了 ApplicationappClass应该是你的 JavaFX 项目的类对象,所以你定义的 start() 方法将被 theApp.start() 调用。

      跳过了很多东西,因为来源太多太长了,无法在此处发布,但这是基本的调用链

      【讨论】:

      • 刚刚看到声明,发现声明是什么"theApp.start(primaryStage);" invokes 是 Application 中的 start() 方法。我仍然不知道程序何时在我自己的类中调用 start() 方法。抱歉这么晚才问,因为我才找到它。
      • @deathlee 啊,我明白了。我会做更多的挖掘。
      • @deathlee launchApplication1,构造Application 类的新实例并将其分配给theApp,因此,theApp.start 是JavaFX 调用应用程序的start 方法时...
      • @deathlee 用更多细节更新了我的答案,但 MadProgrammer 几乎涵盖了它。您的类扩展了Application,因此您定义的start() 方法将被调用。
      【解决方案5】:

      也只是学习 JavaFx,我的印象是它只是用你的类初始化,因为它是你主要参数之上的一个方法。我目前正在使用一个简单的类,它只包含一个主要参数和 start 方法,没有其他命令,并且 start 方法似乎在编译时执行,但它应该在

      上调用
      launch(args);
      

      这是我的主要论点的全部内容,这就是我执行 start 方法的方式

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-03-31
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-11-10
        相关资源
        最近更新 更多