【问题标题】:Java SWT asyncExec() freezes window on dragJava SWT asyncExec() 在拖动时冻结窗口
【发布时间】:2014-10-03 07:16:04
【问题描述】:

我只需要为 SWT 线程测试编写测试代码,现在卡住了 Display.asyncExec() 奇怪的行为。

addShellListener(new ShellAdapter() {
        @Override
        public void shellActivated(ShellEvent e) {
            Display.getDefault().asyncExec(new Runnable(){

                @Override
                public void run() {
                    for(int i=0;i<1000;i++)
                    {
                        getLabel().setText(i+"");
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException e) {
                            // TODO Auto-generated catch block
                            e.printStackTrace();
                        }
                    }

                }

            });
        }
    });

我只是在 shell 启动时使用 asyncExec 运行循环的 Runnable,它每秒更新 shell 内的标签值,模拟计时器。一切都很好并且滴答作响,直到我尝试拖动窗口。窗口在拖动时冻结(包括“勾选”标签)并出现“程序没有响应”通知,只有通过 Java VM 进程终止才能退出。

【问题讨论】:

    标签: java window swt drag freeze


    【解决方案1】:

    我不太确定我是否理解用例,但是为了每秒更新标签,我建议枯萎生成一个后台线程,每秒触发标签更新或使用 timerExec() 进行更新和为下一次 update() 安排另一个 timerExec():

    timerExec() 变体

    private class LabelUpdater implements Runnable {
      @Override
      public void run() {
        if( !label.isDisposed() ) {
          label.setText( ... );
          label.getParent().layout();
          display.timerExec( 1000, new LabelUpdater() );
        }
      }
    }
    
    shell.addListener( SWT.Activate, new Listener() {
      @Override
      public void handleEvent( Event event ) {
        display.timerExec( 1000, new LabelUpdater() );
      }
    } );
    

    后台线程变体

    private class LabelUpdater implements Runnable {
      @Override
      public void run() {
        if( !label.isDisposed() ) {
          label.setText( ... );
          label.getParent().layout();
        }
      }
    }
    
    Runnable runnable = new Runnable() {
      @Override
      public void run() {
        while( !shell.isDisposed() ) {
          display.asyncExec( new LabelUpdater() );
          try {
            Thread.sleep( 1000 );
          } catch( InterruptedException ignore ) {
            Thread.interrupted();
          }
        }
      }
    };
    Thread thread = new Thread( runnable );
    thread.setDaemon( true );
    thread.start();
    

    请注意,shell.isDisposed() 检查不是线程安全的,但实际上不会造成伤害,因为LabelUpdater 必须检查它更新的小部件是否仍然活动。 (有关更详细的讨论,请参阅 here

    【讨论】:

    • 我之前写了一些类似于第一个示例的东西,它运行良好,没有阻塞,但是它有非常奇怪的行为:似乎超时时间不时变化,但仍然没有达到 1 秒的间隔,导致计时器随着时间的推移越来越慢,虽然我不知道这是怎么可能的!
    • timerExec() 以及 asyncExec 使用各自窗口系统的设施。对于您的用例来说,拥有精确的内部结构很重要吗?你是如何测量时间间隔的? “后台线程变量”是否提供更好的结果?
    • 昨晚我发现了这种奇怪行为的原因:它是事件驱动的,并且由于某种原因在 Windows 上(可能也在其他平台上)shellActivated 事件发生 每个时间窗口'激活':在打开时,在窗口之间切换等......
    • 嗯,既然你提到了,这种行为是设计使然,适用于所有平台。 ActivateDeactivate 事件在 shell 被点击、显示、隐藏或带到前台/后台时触发。在什么情况下应该开始和停止定期标签更新?
    • 我只记得写了一个帮助类来保持 SWT UI 是最新的,它显示了相对日期(比如“刚刚”、“一个 houg 之前”等)。需要给出一个 Widget 和一个 Runnable。只要 Widget 没有被释放,Runnable 就会定期执行。代码可以在这里找到:gist.github.com/rherrmann/43a9b3645694a2febef8也许这也解决了你的问题。
    【解决方案2】:

    您提供给Display.asyncExecRunnable 在SWT 用户界面线程中运行。如果您在此可运行文件中使用Thread.sleep,则整个用户界面将在睡眠期间被阻止,因此不要这样做

    您可以使用 sleep 来代替

    Display.getDefault().timerExec(1000, runnable);
    

    安排一个新的可运行文件在 1000 毫秒后运行。

    【讨论】:

    • 问题是 timerExec 在给定的毫秒数之后执行 Runnable,这使得创建循环任务过于复杂。我寻求类似于 swing 中使用的经典线程编程的行为,但具有 SWT 功能。
    • 抱歉,对于 SWT,您必须这样做。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-04-29
    • 1970-01-01
    • 1970-01-01
    • 2020-05-25
    • 2013-01-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多