【问题标题】:Swing app doesn't display in some circumstancesSwing 应用程序在某些情况下不显示
【发布时间】:2012-03-02 17:44:28
【问题描述】:

我正在编写一个实用程序来帮助测试一个更大的系统,如果我通过系统运行它,我的 UI 根本不会显示。当我自己运行它时它工作正常。更多详情:

  1. 我正在测试的系统有许多进程,由控制器管理。部署后,controller.exe 会生成并终止子进程,包括我的 java 应用程序。当由控制器启动时,我的应用程序运行良好(确实可以工作、生成日志等),但 Swing UI 根本不呈现。
  2. 我可以自己从命令行启动我的应用程序,并且 UI 显示得很好。根据 Process Explorer,路径、命令行和当前目录都与从控制器启动应用时观察到的值相同。
  3. 当应用程序不可见时,单击进程资源管理器中的“置于前面”会弹出一个对话框,显示。 “找不到此进程的可见窗口。”在手动生成的进程上单击相同的按钮会使 Swing UI 按预期显示在前面。
  4. 我正在 Windows XP 中对此进行测试。控制器进程在 SYSTEM 下运行。我使用"at 09:45 /interactive cmd.exe" trick 为我的手动启动生成一个命令提示符作为系统。 Process Explorer 验证这两种方法(手动/控制器)是否将 java 进程生成为 SYSTEM。
  5. 两个进程之间唯一明显的区别是父进程。系统进程作为 controller.exe 的子进程生成,而我的手动执行是 cmd.exe 的子进程。
  6. 我已经使用 jdwp 远程调试到不可见进程,一切正常。 UI 代码执行,没有抛出异常,我的 JLists 甚至被它们监视的数据填充。据我所知,一切正常;我就是什么都看不到。

如果这已在其他地方得到回答,我深表歉意。我尽我所能四处搜索,但我能找到的所有问题都是关于某些组件无法呈现,而不是整个应用程序无法显示。

谢谢!

编辑: 根据反馈,我换了一个尽可能小的演示程序。这是完整的代码:

import java.awt.BorderLayout;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;

public class AceProbe
{
  public static void main(String[] args)
  {
    SwingUtilities.invokeLater(new Runnable()
    {
      @Override
      public void run()
      {
        JFrame frame = new JFrame("Visible?");
        frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
        frame.getContentPane().add(new JLabel("Test"), BorderLayout.CENTER);
        frame.pack();
        frame.setVisible(true);
      }
    });
  }
}

从命令行运行它会按预期显示窗口。但是,当控制器生成进程时,什么都不会显示。当由控制器生成时,我可以使用-agentlib:jdwp=transport=dt_socket,suspend=y,server=y,address=8000 远程调试进程,并验证所有线程都按预期生成,并且没有抛出异常。

我的直觉是控制器处于一些奇怪的图形配置中,并且由于父进程是新的 java 进程,也许 Swing 没有使用正确的 GraphicsDevice?我尝试添加此代码:

GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
for(GraphicsDevice d : ge.getScreenDevices())
{
  Log.println(d);
  for (GraphicsConfiguration c : d.getConfigurations())
  {
    Log.println(c);
    Log.println(c.getBounds());
  }
}

日志包含以下内容:

Win32GraphicsDevice[screen=0]
sun.awt.Win32GraphicsConfig@13f459d[dev=Win32GraphicsDevice[screen=0],pixfmt=0]
java.awt.Rectangle[x=0,y=0,width=1920,height=1080]

这似乎表明只有一个设备具有一个配置,所以我有点茫然。

【问题讨论】:

    标签: java swing windows-xp


    【解决方案1】:

    您对Concurency in Swing 有疑问。 Swing 是单线程的,所有到 Swing GUI 的输出都必须在 EDT 上完成。

    • 基本上,您可以通过将输出包装到 GUI 中 invokeLaterinvokeAndWait 来解决这个问题

    • 正确的方法是从SwingWorkerRunnable#Thread 调用后台任务(在Runnable 中,输出必须包装到invokeLaterinvokeAndWait

    编辑

    import java.awt.*;
    import java.util.*;
    import javax.swing.*;
    
    public class SSCCE extends JFrame {
    
        private static final long serialVersionUID = 1L;
    
        public SSCCE() {
            super("SSCCE");
            setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            setContentPane(createContent());
            pack();
            centerFrameOnMonitor(0);
            setVisible(true);
        }
    
        private JComponent createContent() {
            JPanel ret = new JPanel();
            JButton button = new JButton("I have a tooltip");
            button.setToolTipText("I wonder where this tooltip is going to popup!");
            ret.add(button);
            return ret;
        }
    
        /**
         * Centers this frame in the middle of the monitor with the monitorIndex specified. This method assumes that all monitors are laid out
         * horizontally. The 0th index monitor would be the one furthest left.
         * @param monitorIndex
         */
        private void centerFrameOnMonitor(int monitorIndex) {
            GraphicsEnvironment e = GraphicsEnvironment.getLocalGraphicsEnvironment();
            GraphicsDevice[] devices = e.getScreenDevices();
            if (monitorIndex < 0 || monitorIndex >= devices.length) {
                throw new RuntimeException("Monitor Index out of bounds: " + monitorIndex);
            }
            Arrays.sort(devices, new Comparator<GraphicsDevice>() {
    
                @Override
                public int compare(GraphicsDevice a, GraphicsDevice b) {
                    return (int) (a.getDefaultConfiguration().getBounds().getX() - b.getDefaultConfiguration().getBounds().getX());
                }
            });
            GraphicsConfiguration gc = devices[monitorIndex].getDefaultConfiguration();
            Rectangle gcBounds = gc.getBounds();
            Point p = new Point((int) (gcBounds.getX() + (gcBounds.getWidth() / 2 - this.getWidth() / 2)), (int) (gcBounds.getY() + (gcBounds.getHeight() / 2 - this.getHeight() / 2)));
            this.setLocation(p);
        }
    
        public static void main(String[] args) {
            SwingUtilities.invokeLater(new Runnable() {
    
                @Override
                public void run() {
                    SSCCE sSCCE = new SSCCE();
                }
            });
        }
    } 
    

    EDIT2:

    能力测试返回

    来自 HP Elite、WinXp、Java6、低配置 GPU

    【讨论】:

    • 感谢您的回复。我已经用 SwingWorker.invokeLater() 结束了发布。我的主要课程是 AceProbe。因此,当我从命令行运行 java -cp lib/* AceProbe 时,它​​可以工作。但是当控制器运行完全相同的命令时,它就不起作用了。
    • @Eric Grunzke 问题可能无处不在,更好的是根据可运行代码将您的问题拆分为单独的问题
    • 有道理。我编辑了原始帖子,以表明问题存在于最小、简单的 UI 中。有没有更好的拆分方式,或者我可以提供一些信息?
    • GPU 驱动有两个问题,或者 GPU 包含两个内核,请您用Capabilities Test 测试或运行代码(请参阅我的编辑)
    • JComboBox 有一个选项 == 也只有一个显示器被初始化和插入,其余的请看我的编辑
    【解决方案2】:

    解决方案是将应用拆分为 2 个组件(数据收集和 ui/处理)并让它们通过RMI 进行通信。

    事实证明,Windows 服务无法生成 GUI。我做了一些sc querying 并发现controller.exe 是一项服务。由于它是 java 进程的父进程,java 获得相同的服务权限,这意味着没有 gui。有some workarounds,但它们的存在是为了向后兼容,在新代码中确实应该避免使用。

    重申 java 没有引发任何异常是很重要的。似乎这种情况应该抛出一些东西,表明 java 进程没有创建新窗口的权限,但代码“正常”运行。窗口永远不会出现。

    底线:如果另一个进程正在启动您的 vm,并且您没有收到错误但没有 ui,请检查以确保您的 vm 没有作为服务启动。如果是,则需要将 ui 代码提取到单独的进程中。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-08-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多