Swing Worker应用举例

原文

在开发Java Swing应用程序的过程中,有两个原则是必须要牢记的:

    1.耗时的操作(例如从数据库查询大量数据,读取URI资源等)一定不能运行在EDT(事件派发线程)上,否则会导致Swing用户界面失去响应。
    2.只能在EDT线程上对Swing Components进行访问。
    基于上面两点原因,在一个Java Swing程序中,要想使用户界面响应灵敏,至少应该有两个线程;一个线程用来执行耗时操作,EDT线程用来执行所有与Swing Components的交互,例如更新文本,重绘图形等等。这就要求两个线程之间要相互通讯,给程序的开发带来了不少的难度,Swing Worker的出现从根本上解决了这个问题,使程序员快速开发反应灵敏的的Swing程序成为可能。SwingWoker被设计应用在此种场景下,你有一个耗时操作需要运行在后台,在该操作完成或部分完成时,你要利用操作返回的结果去更新用户界面。
    让我们假想有这样一个应用场景,我有一个保存联系人的文件,我需要从中读取并解析出所有联系人的信息,并及时更新在一个JTable中;假设这个文件非常的大,解析出所有联系人的信息需要花费几分钟的时间,如果不能很好的协调这个任务和EDT线程,则很有可能会造成用户在几分钟时间里得不到结果,而Swing界面处于无响应状态。在这种情况下,SwingWorker就是一个绝佳的选择。我们首先看一下SwingWorker的定义:
    public abstract class SwingWorker<T,V> extends Object implements RunnableFuture<T>
显然,这时一个抽象的模板类,在应用的时候,我们需要继承SwingWorker并实例化模板参数。那么,这两个模板类型究竟是什么意思呢,T参数代表的是你的耗时任务执行完成时返回的结果类型,V代表的是你的耗时任务部分完成时返回的结果类型。在我们的场景中,假设任务完成时我们需要一个List<BeanContact>(BeanContact是一个保存联系人信息的JavaBean),每当从文件中解析出一个联系人信息时,我们会新建一个BeanContact并需要更新到JTable中。那么我们的T就是List<BeanContact>,而V就是BeanContact,则应该定义如下的类:
     public class LoadContactsTask extends SwingWorker<List<BeanContact>, BeanContact> {
            

     @Override
    protected List<BeanContact> doInBackground() throws Exception {

        //To do the task and return the result
    }


     }
    从上面可以看到,我们还必须覆盖SwingWorker的doInBackground方法,该方法执行我们的耗时操作,并且返回模板实例化时的T类型结果。下面是具体的代码实现:
     

    @Override
    protected List<BeanContact> doInBackground() throws Exception {
        BufferedReader reader = new BufferedReader(new FileReader("c:/contacts.cff"));
        String line = null;
        while ((line = reader.readLine()) != null) {
            String[] strContacts = line.split(",");
            BeanContact contact = new BeanContact();
            contact.setName(strContacts[0]);
            contact.setSex(strContacts[1]);
            contact.setPhone(strContacts[2]);
            contact.setEmail(strContacts[3]);

            lineCnt++;
            publish(contact);/*********/
           contacts.add(contact);
            
            Thread.sleep(100);
        }
        return contacts;
    }

     该方法很简单,就是从文件中读取一个联系人的记录并且新建一个BeanContact实例添加到结果集中。我们需要注意的是其中的publish方法,该方法用来发布部分执行结果,每读取一个联系人信息,我们就用该方法把新建的BeanContact发布出去。我们需要知道的是,在publish若干个结果后(可能是一个或多个,由SwingWorker类实现)SwingWorker类的process方法会被自动回调,而我们可以在其中去更新用户界面,SwingWorker保证process方法中所有操作都运行在EDT线程中。下面是我们的具体实现:
    @Override
    protected void process(List<BeanContact> chunks) {
        if (progressHandle != null) {
            progressHandle.processInProgress(chunks, lineCnt * 100 / 10000);
        }
    }

    我们的实现中,process中会调用IProgressHandle(自定义的一个接口,用来更新用户界面,详见后面代码)的processInProgress方法来更新用户界面,大家会注意到process方法的参数是一个List<BeanContact>,为什么不是一个BeanContact呢,答案就是我们在上面讲过的,有可能publish若干次后才调用process方法。
    与此类似,在doInBackground完成后,SwingWorker会自动调用done方法,下面是我们的实现:
    @Override
    protected void done() {
        if (progressHandle != null) {
            progressHandle.processComplete(contacts);
        }
    }
    客户端如何来使用用SwingWork呢,很简单,只需要新建一个实例并且调用它的execute方法即可,他会自动调用doInBackground方法来完成操作;以下是完整的代码实现:
    
    

/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package swingworkertest;

/**
 *
 * @author Administrator
 */
public class BeanContact {
    private String name=null;
    private String sex=null;
    private String phone=null;
    private String email=null;

    /**
     * @return the name
     */
    public String getName() {
        return name;
    }

    /**
     * @param name the name to set
     */
    public void setName(String name) {
        this.name = name;
    }

    /**
     * @return the sex
     */
    public String getSex() {
        return sex;
    }

    /**
     * @param sex the sex to set
     */
    public void setSex(String sex) {
        this.sex = sex;
    }

    /**
     * @return the phone
     */
    public String getPhone() {
        return phone;
    }

    /**
     * @param phone the phone to set
     */
    public void setPhone(String phone) {
        this.phone = phone;
    }

    /**
     * @return the email
     */
    public String getEmail() {
        return email;
    }

    /**
     * @param email the email to set
     */
    public void setEmail(String email) {
        this.email = email;
    }
    
}


/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

/*
 * Contacts.java
 *
 * Created on 2011-6-25, 10:40:13
 */
package swingworkertest;

import javax.swing.JFileChooser;

/**
 *
 * @author Administrator
 */
public class Contacts extends javax.swing.JFrame {

    /** Creates new form Contacts */
    public Contacts() {
        initComponents();
        handle = new DefaultProgressHandle();
        handle.setTable(jTable1);
        handle.setProgressBar(jProgressBar1);
    }

    /** This method is called from within the constructor to
     * initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is
     * always regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">                          
    private void initComponents() {

        jButton1 = new javax.swing.JButton();
        jTextField1 = new javax.swing.JTextField();
        jScrollPane1 = new javax.swing.JScrollPane();
        jTable1 = new javax.swing.JTable();
        jButton2 = new javax.swing.JButton();
        jProgressBar1 = new javax.swing.JProgressBar();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

        jButton1.setText("浏览");
        jButton1.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButton1ActionPerformed(evt);
            }
        });

        jTextField1.setEditable(false);

        jTable1.setModel(new javax.swing.table.DefaultTableModel(
            new Object [][] {

            },
            new String [] {
                "姓名", "性别", "电话", "电子邮件"
            }
        ) {
            Class[] types = new Class [] {
                java.lang.String.class, java.lang.String.class, java.lang.String.class, java.lang.String.class
            };

            public Class getColumnClass(int columnIndex) {
                return types [columnIndex];
            }
        });
        jScrollPane1.setViewportView(jTable1);

        jButton2.setText("加载联系人");
        jButton2.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                jButton2ActionPerformed(evt);
            }
        });

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
                    .addComponent(jScrollPane1, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 557, Short.MAX_VALUE)
                    .addComponent(jProgressBar1, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 557, Short.MAX_VALUE)
                    .addComponent(jButton2, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 557, Short.MAX_VALUE)
                    .addGroup(layout.createSequentialGroup()
                        .addComponent(jButton1, javax.swing.GroupLayout.DEFAULT_SIZE, 97, Short.MAX_VALUE)
                        .addGap(18, 18, 18)
                        .addComponent(jTextField1, javax.swing.GroupLayout.PREFERRED_SIZE, 442, javax.swing.GroupLayout.PREFERRED_SIZE)))
                .addContainerGap())
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addContainerGap()
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jButton1)
                    .addComponent(jTextField1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                .addComponent(jButton2)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
                .addComponent(jScrollPane1, javax.swing.GroupLayout.PREFERRED_SIZE, 120, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(jProgressBar1, javax.swing.GroupLayout.PREFERRED_SIZE, 22, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
        );

        layout.linkSize(javax.swing.SwingConstants.VERTICAL, new java.awt.Component[] {jButton1, jButton2, jTextField1});

        pack();
    }// </editor-fold>                       

    private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {                                        

        ContactsFileFilter filter = new ContactsFileFilter();
        
        JFileChooser chooser = new JFileChooser();
        chooser.setFileFilter(filter);
        int returnVal = chooser.showOpenDialog(null);
        if(returnVal == JFileChooser.APPROVE_OPTION){
            jTextField1.setText(chooser.getSelectedFile().getAbsolutePath());
        }
    }                                       

    private void jButton2ActionPerformed(java.awt.event.ActionEvent evt) {                                         
        // TODO add your handling code here:
        LoadContactsTask task = new LoadContactsTask(jTextField1.getText());
        task.setProgressHandle(handle);
        task.execute();
    }                                       

    /**
     * @param args the command line arguments
     */
    public static void main(String args[]) {
        java.awt.EventQueue.invokeLater(new Runnable() {

            public void run() {
                Contacts contacts = new Contacts();
                contacts.setTitle("Contacts");;
                contacts.setVisible(true);
            }
        });
    }
    DefaultProgressHandle handle = null;
    // Variables declaration - do not modify                     
    private javax.swing.JButton jButton1;
    private javax.swing.JButton jButton2;
    private javax.swing.JProgressBar jProgressBar1;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JTable jTable1;
    private javax.swing.JTextField jTextField1;
    // End of variables declaration                   
}


/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package swingworkertest;

import java.io.File;
import javax.swing.filechooser.FileFilter;

/**
 *
 * @author Administrator
 */
public class ContactsFileFilter extends FileFilter{

    public boolean accept(File pathname) {
        if(pathname.isDirectory()){
            return true;
        }else{
            return pathname.getName().endsWith(".cff");
        }
    }

    @Override
    public String getDescription() {
        return "Text Files";
    }
    
}


/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package swingworkertest;

import java.util.List;
import javax.swing.JProgressBar;
import javax.swing.JTable;
import javax.swing.table.DefaultTableModel;

/**
 *
 * @author Administrator
 */
public class DefaultProgressHandle implements IProgressHandle {

    private JTable table = null;
    private JProgressBar progressBar = null;

    public void processInProgress(List<BeanContact> contacts, int progress) {
        DefaultTableModel model = (DefaultTableModel) table.getModel();
        for (BeanContact contact : contacts) {
            String[] strArray = {contact.getName(), contact.getSex(), contact.getPhone(), contact.getEmail()};
            model.addRow(strArray);
        }
        progressBar.setValue(progress);
    }

    public void processComplete(List<BeanContact> contacts) {
        progressBar.setValue(progressBar.getMaximum());
    }

    /**
     * @param table the table to set
     */
    public void setTable(JTable table) {
        this.table = table;
    }

    /**
     * @param progressBar the progressBar to set
     */
    public void setProgressBar(JProgressBar progressBar) {
        this.progressBar = progressBar;
    }
}


/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package swingworkertest;

import java.util.List;

/**
 *
 * @author Administrator
 */
public interface IProgressHandle {
    public abstract void processInProgress(List<BeanContact> contacts,int progress);
    public abstract void processComplete(List<BeanContact> contacts);
}


/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package swingworkertest;

import java.io.BufferedReader;
import java.io.FileReader;
import java.util.ArrayList;
import java.util.List;
import javax.swing.SwingWorker;

/**
 *
 * @author Administrator
 */
public class LoadContactsTask extends SwingWorker<List<BeanContact>, BeanContact> {

    private String fileName = null;
    private IProgressHandle progressHandle = null;
    private List<BeanContact> contacts = null;
    private int lineCnt = 0;

    public LoadContactsTask(String fileName) {
        this.fileName = fileName;
        contacts = new ArrayList<BeanContact>();
    }

    @Override
    protected List<BeanContact> doInBackground() throws Exception {
        BufferedReader reader = new BufferedReader(new FileReader("c:/contacts.cff"));
        String line = null;
        while ((line = reader.readLine()) != null) {
            String[] strContacts = line.split(",");
            BeanContact contact = new BeanContact();
            contact.setName(strContacts[0]);
            contact.setSex(strContacts[1]);
            contact.setPhone(strContacts[2]);
            contact.setEmail(strContacts[3]);

            lineCnt++;
            publish(contact);
            contacts.add(contact);
            
            Thread.sleep(100);
        }
        return contacts;
    }

    /**
     * @param progressHandle the progressHandle to set
     */
    public void setProgressHandle(IProgressHandle progressHandle) {
        this.progressHandle = progressHandle;
    }

    @Override
    protected void process(List<BeanContact> chunks) {
        if (progressHandle != null) {
            progressHandle.processInProgress(chunks, lineCnt * 100 / 10000);
        }
    }

    @Override
    protected void done() {
        if (progressHandle != null) {
            progressHandle.processComplete(contacts);
        }
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值