SWT中的UI线程



SWT中的UI线程

SWT作为一种桌面程序,比普通的Java程序要多一个UI线程,UI线程负责不断地画出显示的UI控件,当 然这个UI线程还要负责事件的处理。什么是事件呢?例如单击按钮或是按下键盘,系统都会生成一个事件放在事件队列中,即接下来UI线程按顺序处理队列中的 事件。SWT中Display对象就是一个UI线程,并且负责管理队列中的事件。

以下代码,读者并不陌生,在之前使用的SWT程序中都用到过,下面来仔细分析一下它的详细情况。

//当窗口未释放时

while (!shell.isDisposed()) {

   //如果display对象事件队列中没有了等待的事件,就让该线程进入等待状态

   if (!display.readAndDispatch())

       display.sleep();

}

可以这样理解UI线程:当程序启动后,如果用户不进行任何操作,该UI线程就进入了等待状态。一旦触发了某个 事件,比如说单击了某个按钮或是按下了键盘上的按键,这时在事件队列中就等待了一个事件,此时UI线程就处理队列中的事件,直至队列中的事件全部处理完 毕,又恢复了睡眠状态。处理事件的过程也就是响应用户操作的过程。

这就产生了一个问题,当某一个队列中的事件是一个非常耗时的事件时,比如说是检索文件或者是查询大量数据的数 据库时,这时,用户就需要长时间的等待。所以在这种情况下,就需要为长时间处理的程序单独开辟出一个线程来执行,不影响UI线程继续处理其他事件了,这样 给用户的感觉就是,虽然后台运行着程序,但也不会影响界面上的操作。

11.3 其他线程访问UI线程

理解了线程的基本知识,再重新看一下9.7节中进度条的示例程序。这个程序是在开始运行窗口时就在后台启动了一个线程,这个后台线程每隔0.1秒更新运行一次设置滚动条的值。可以看到在run()方法体中有一段代码读者可能有疑问,重新加注释后的代码如下:

//创建一个线程,该线程每0.1秒更新一次滚动条的值

Runnable runnable = new Runnable() {

   public void run() {

       //线程运行的主体

       for (int i = minimus; i < maximum; i++) {

           try {

               //让线程睡眠0.1秒

               Thread.sleep(100);

           } catch (InterruptedException e) {

               e.printStackTrace();

           }

           //如果使用以下一行代码更新滚动条的值,运行时会出现Invalid thread access运行错误

           //progressBar.setSelection(progressBar.getSelection() + 1);

           //让UI线程更新滚动条的值

          display.asyncExec(new Runnable() {

               //这也是一个线程,该线程的功能是更新滚动条的值,一瞬间就结束了

               //并且这个线程是被UI线程调用的

               public void run() {

                   if (progressBar.isDisposed())

                       return;

                   progressBar.setSelection(progressBar.getSelection() + 1);

               }

           });

       }

   }

};

//启动这个线程

new Thread(runnable).start();

在设置滚动条的progressBar的值时,为什么不能直接使用 progressBar.setSelection (progressBar.getSelection() + 1)代码来直接设置滚动条的值呢?这是因为滚动条对象是UI界面上的控件,它是由UI线程创建的。若要访问UI界面上的对象必须通过UI线程来访问,就是 说在非UI线程中调用UI对象是不允许的,这是出于线程安全的考虑。正因为如此,只能通过另一种方式来更新进度条的值,解决方案就是需要再开辟一个线程, 专门更新滚动条的值,这个线程交给UI线程来调用。

Display对象中负责调用其他线程的方法有以下3种:

  ● asyncExec(Runnable runnable):异步启动新的线程。所谓异步就是,UI线程不会等待runnable对象执行结束后再继续进行,就是说UI线程可以和runnable对象所在的线程同时运行。

  ● syncExec(Runnable runnable):同步启动新的线程。所谓同步就是,UI线程会等待runnable对象执行结束后才会继续进行,当runnable对象是耗时大的线 程时,尽量不要采用此种方式。另外,对于该种方式创建的线程可通过getSyncThread()方法获得线程对象。

  ● timerExec(int milliseconds,Runnable runnable):指定一段时间再启动新的线程。用此方法创建的线程,将会在指定的时间后再启动线程。当然用此方法创建的线程启动后,与UI线程是异步 的。如果指定的时间为负数,将不会按时启动线程。

另外Display对象中,与UI线程相关的方法如下所示:

  ● 获得当前的UI线程对象的方法:getThread(),返回Thread对象。

  ● 使UI线程处于休眠状态:sleep()。

  ● 唤醒UI线程:wake()。

 

 

(1)编写一个MutiTaskTestDrive类,作为该系统的入口。该类比较简单,代码如下:

MutiTaskTestDrive.java

package com.fengmanfei.ch11;

import org.eclipse.swt.widgets.Display;

import com.fengmanfei.util.ImageFactory;

public class MutiTaskTestDrive {

   public static void main(String[] args) {

       Display display = Display.getDefault();

       MutiTaskGUI mutiTask= new MutiTaskGUI();

       mutiTask.getShell().open();

       while (!mutiTask.getShell().isDisposed()) {

           if (!display.readAndDispatch()) {

               display.sleep();

           }

       }

       ImageFactory.dispose();

       display.dispose();

   }

}

(2)MutiTaskGUI类为主窗口类,也就是放置表格的窗口,该类具体的代码如下:

MutiTaskGUI.java

package com.fengmanfei.ch11;

import org.eclipse.swt.SWT;

import org.eclipse.swt.events.SelectionAdapter;

import org.eclipse.swt.events.SelectionEvent;

import org.eclipse.swt.layout.GridData;

import org.eclipse.swt.layout.GridLayout;

import org.eclipse.swt.widgets.Button;

import org.eclipse.swt.widgets.Shell;

import org.eclipse.swt.widgets.Table;

import org.eclipse.swt.widgets.TableColumn;

public class MutiTaskGUI {

   private Shell shell = null;

   private Table table = null;

   public MutiTaskGUI( ){

       //构造方法中调用初始化窗口的方法

       init();

   }

   //初始化窗口方法

   public void init() {

       shell = new Shell();

       shell.setLayout(new GridLayout());

       shell.setText("多线程");

       Button bt = new Button ( shell , SWT.NONE);

       bt.setText("开始一个任务");

       // 创建表格对象

       table = new Table(shell, SWT.BORDER);

       table.setLayoutData( new GridData(SWT.FILL,SWT.FILL,true,true));

       table.setHeaderVisible(true);

       table.setLinesVisible(true);

       String[] header = new String[]{"任务","进度","操作"};

       // 创建表头

       for (int i = 0; i < 3; i++) {

           TableColumn col = new TableColumn(table, SWT.NONE);

           col.setText( header[i] );

       }

       //设置表头宽度

       table.getColumn(0).setWidth(80);

       table.getColumn(1).setWidth(150);

       table.getColumn(2).setWidth(80);

       shell.pack();

       //注册创建任务按钮事件

       bt.addSelectionListener( new SelectionAdapter(){

           //当单击创建一个任务按钮时

           public void widgetSelected(SelectionEvent e) {

               //首先创建一个Task对象

               Task task = new Task ( table );

               //然后在表格中添加一行

               task.createTableItem();

               //最后启动该任务,该任务为一个线程

               task.start();

           }

       });

   }

   //获得和设置属性的getter和setter方法

   public Shell getShell() {

       return shell;

   }

   public void setShell(Shell shell) {

       this.shell = shell;

   }

   public Table getTable() {

       return table;

   }

   public void setTable(Table table) {

       this.table = table;

   }

}

该类需要注意的地方是,“开始一个任务”按钮事件的处理。当单击该按钮时,就创建一表格中的一行,并且启动一个线程。添加表格中的一行和启动线程是使用Task对象来完成的。

(3)Task类继承自Thread,并覆盖了run()方法,具有线程的特性。Task类具体实现的代码如下:

Task.java

package com.fengmanfei.ch11;

import org.eclipse.swt.SWT;

import org.eclipse.swt.custom.TableEditor;

import org.eclipse.swt.events.SelectionAdapter;

import org.eclipse.swt.events.SelectionEvent;

import org.eclipse.swt.widgets.Button;

import org.eclipse.swt.widgets.ProgressBar;

import org.eclipse.swt.widgets.Table;

import org.eclipse.swt.widgets.TableItem;

import com.fengmanfei.util.ImageFactory;

//该Task类继承自Thread,并且覆盖了run()方法

public class Task extends Thread {

   //该类的一些属性

   private Table table = null;

   //是否停止的标志

   private boolean done = false;

   //声明进度条对象

   private ProgressBar bar = null;

   private int min = 0;

   private int max = 100;

   public Task(Table table) {

       this.table = table;

   }

   //创建表格中的一行

   public void createTableItem() {

       TableItem item = new TableItem(table, SWT.NONE);

       item.setText(this.getName());

       item.setImage(ImageFactory.loadImage(table.getDisplay(), ImageFactory.PROGRESS_TASK));

       // 创建一个进度条

       bar = new ProgressBar(table, SWT.NONE);

       bar.setMinimum(min);

       bar.setMaximum(max);

       // 创建一个可编辑的表格对象

       TableEditor editor = new TableEditor(table);

       editor.grabHorizontal = true;

       editor.grabVertical = true;

       // 将进度条绑定到第二列中

       editor.setEditor(bar, item, 1);

       //重新创建一个可编辑的表格对象

       editor = new TableEditor(table);

       editor.grabHorizontal = true;

       editor.grabVertical = true;

       //创建一个按钮

       Button stop = new Button(table, SWT.NONE);

       stop.setText("Stop");

       editor.setEditor(stop, item, 2);

       stop.addSelectionListener(new SelectionAdapter() {

           //当停止按钮按下时,设置停止标记true

           public void widgetSelected(SelectionEvent e) {

               if (!isDone())

                   setDone(true);

           }

       });

   }

   //线程方法体,与前面单个的进度条的程序类似

   public void run() {

       for (int i = min; i < max; i++) {

           try {

               Thread.sleep(100);

           } catch (InterruptedException e) {

               e.printStackTrace();

           }

           table.getDisplay().asyncExec(new Runnable() {

               public void run() {

                   if (bar.isDisposed())

                       return;

                   bar.setSelection(bar.getSelection() + 1);

               }

           });

           //如果停止,则结束该线程

           if (isDone()) {

               break;

           }

       }

   }

   //获得和设置属性的getter和setter方法

   public Table getTable() {

       return table;

   }

   public void setTable(Table table) {

       this.table = table;

   }

   public boolean isDone() {

       return done;

   }

   public void setDone(boolean done) {

       this.done = done;

   }

}

Task类中的run()方法体中的代码与之前单个进度条的处理方式类似。从以上的程序代码中可以看出,类中大量使用了bean的方式,也就是通过一些getter和setter方法来设置和访问类的属性,又将常用的操作封装为方法,这才是涉及结构合理的类。

小结:


转载于:https://my.oschina.net/piorcn/blog/386225

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值