O tal SwingWorker

Eu nunca vi um exemplo de SwingWorker que eu conseguisse entender.

Tenho 3 sistemas grandes em Swing e o carregamento de grandes quantidades de dados ainda está sendo feito do tipo old fashion, gostaria de uma pequena luz de quem conhece bem esse SwingWorker porque tenho certeza que isso vai otimizar muito meu carregamento de dados (algumas vezes lista com 10 mil conhecimentos de transportes, ou 40 mil Orçamentos - bd Firebird).

Exemplo:

  • Quero carregar todos os clientes (beans) em uma lista (JComboBox), tenho um método no meu DAO que retorna um ArrayList, gostaria de fazer isso em background com SwingWorker.

Cheguei até aqui, e nada mais:

public class LoadClientes extends SwingWorker<List<ClienteFormBean>, Void> {
    Connection conn = Conexao.getConexaoSis();
    @Override
    protected List<ClienteFormBean> doInBackground() {
        ArrayList<ClienteFormBean> lista = new ClienteDAO(conn).list(false);
        return lista;
        //return domainManager.findAll();
    }

    @Override
    public void done() {
        // O que vai aqui?
    }
}

Como eu uso isso no meu EDT?

Obrigado

Bem, não conheço o swingworker, mas acredito que usando threads você consegue chegar neste efeito desejado.

Neste micro não tem como fazer nenhum teste pra te servir de exemplo, então vai ter de dar uma procurada, mas a idéia é você criar a thread, fazer a consulta e colocar ou atualizar alguma lista, e sinalizar de volta pro programa que a consulta acabou, para a janela ser desenhada.

Flw

Eu tenho um exemplinho simples e antigo, vc deve estar usando um swingWorker mais novo.
Mas nele tu pode executar um processo de três maneiras.
Usando swingWorker, usando uma thread e nao usando nada. Talvez tu vendo em execução
tu perceba a diferença! É meio grandinho o código pois inclui o swingWorker junto!

public class SwingThread extends JPanel {
	private JButton threadButton;
	private JButton nonThreadbutton;
	private JButton swingWorkerButton;
	private JProgressBar progressBar;

	public SwingThread() {
		this.add(this.getProgressBar());
		this.add(this.getSwingWorkerButton());
		this.add(this.getThreadButton());
		this.add(this.getNonThreadbutton());
	}

	public JButton getThreadButton() {
		if (this.threadButton == null) {
			this.threadButton = new JButton("Thread Process");
			this.threadButton.setPreferredSize(new Dimension(200, 22));
			this.threadButton.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					executeThread();
				}
			});
		}
		return this.threadButton;
	}

	public JButton getNonThreadbutton() {
		if (this.nonThreadbutton == null) {
			this.nonThreadbutton = new JButton("Process");
			this.nonThreadbutton.setPreferredSize(new Dimension(200, 22));
			this.nonThreadbutton.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					execute();
				}
			});
		}
		return this.nonThreadbutton;
	}

	public JButton getSwingWorkerButton() {
		if (this.swingWorkerButton == null) {
			this.swingWorkerButton = new JButton("SwingWorker Process");
			this.swingWorkerButton.setPreferredSize(new Dimension(200, 22));
			this.swingWorkerButton.addActionListener(new ActionListener() {
				public void actionPerformed(ActionEvent e) {
					executeSwingWorker();
				}
			});
		}
		return this.swingWorkerButton;
	}

	public JProgressBar getProgressBar() {
		if (this.progressBar == null) {
			this.progressBar = new JProgressBar();
			this.progressBar.setPreferredSize(new Dimension(300, 22));
		}
		return this.progressBar;
	}

	public void execute() {
		new MyProcess().execute(this.progressBar);
	}

	public void executeSwingWorker() {
		final SwingWorker s = new SwingWorker() {
			public Object construct() {
				execute();
				return null;
			}
		};
		s.start();
	}

	public void executeThread() {
		Runnable r = new Runnable() {
			public void run() {
				execute();
			}
		};
		new Thread(r).start();
	}

	public static void createAndShowGUI() {
		JFrame frame = new JFrame();
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setContentPane(new SwingThread());
		frame.pack();
		frame.setVisible(true);
	}

	public static void main(String[] args) {
		SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				createAndShowGUI();
			}
		});
	}
}
class MyProcess {
	public void execute(JProgressBar progressBar) {
		System.out.println("Process...");
		int value = 0;
		while (value <= 100) {
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			progressBar.setValue(++value);
			System.out.print(".");
			if (value == 20 || value == 50 || value == 80) {
				JOptionPane.showMessageDialog(progressBar, "Valor está em "
						+ value);
			}
		}
		System.out.println("\n...end");
		JOptionPane.showMessageDialog(progressBar, "Processo Finalizado...");
	}
}

abstract class SwingWorker {
	private Object value; // see getValue(), setValue()

	private static class ThreadVar {
		private Thread thread;

		ThreadVar(Thread t) {
			thread = t;
		}

		synchronized Thread get() {
			return thread;
		}

		synchronized void clear() {
			thread = null;
		}
	}

	private ThreadVar threadVar;

	protected synchronized Object getValue() {
		return value;
	}

	private synchronized void setValue(Object x) {
		value = x;
	}

	public abstract Object construct();

	public void finished() {
	}

	public void interrupt() {
		Thread t = threadVar.get();
		if (t != null) {
			t.interrupt();
		}
		threadVar.clear();
	}

	public Object get() {
		while (true) {
			Thread t = threadVar.get();
			if (t == null) {
				return getValue();
			}
			try {
				t.join();
			} catch (InterruptedException e) {
				Thread.currentThread().interrupt(); // propagate
				return null;
			}
		}
	}

	public SwingWorker() {
		final Runnable doFinished = new Runnable() {
			public void run() {
				finished();
			}
		};

		Runnable doConstruct = new Runnable() {
			public void run() {
				try {
					setValue(construct());
				} finally {
					threadVar.clear();
				}

				SwingUtilities.invokeLater(doFinished);
			}
		};

		Thread t = new Thread(doConstruct);
		threadVar = new ThreadVar(t);
	}

	public void start() {
		Thread t = threadVar.get();
		if (t != null) {
			t.start();
		}
	}
}

Muito obrigado amigo.

Pergunta: por que deve ter essa classe SwingWorker? No java 6 ja tem essa classe, certo?

Agora eu consegui obter um resultado usando o SwingWorker do java 6, porém eu gostaria que a minha barra de progresso indicasse o andamento em percentual, mas não sei como fazer para isso acontecer, por enquanto estou mostrando a barra intermitente.
Essa consulta retorna uma lista com 220 mil itens.

[code]SwingWorker<ArrayList, Void> worker = new SwingWorker<ArrayList, Void>() {

    @Override
    public ArrayList<Orcamento> doInBackground() {
        progressBar.setString("Carregando...");
        progressBar.setIndeterminate(true);
        ArrayList<Orcamento> lista = new com.sikgraf.dao.OrcamentoDAO(c).lista();

        return lista;
    }

    @Override
    public void done() {
        ArrayList<Orcamento> lista = null;
        try {
            lista = get();
            ArrayListComboBoxModel modelOrcamentos = new ArrayListComboBoxModel(lista);
            modelOrcamentos.insertElementAt("-- Relação de Orçamentos --",0);
            cbOrcamentos.setModel(modelOrcamentos);
            cbOrcamentos.repaint();
            cbOrcamentos.getModel().setSelectedItem(cbOrcamentos.getModel().getElementAt(0));
        } catch (InterruptedException ex) {
            Logger.getLogger(OrcamentoUI.class.getName()).log(Level.SEVERE, null, ex);
        } catch (ExecutionException ex) {
            Logger.getLogger(OrcamentoUI.class.getName()).log(Level.SEVERE, null, ex);
        }
        progressBar.setIndeterminate(false);
        progressBar.setString("Carregado(s) "+(lista==null?"0":lista.size())+" Orçamentos");
    }
};
worker.execute();[/code]

[quote=javer]Muito obrigado amigo.

Pergunta: por que deve ter essa classe SwingWorker? No java 6 ja tem essa classe, certo?[/quote]

Porque os componentes swing não são thread safe. Vc precisa delegar a atualização deles a outra thread caso o processo que for ser executado for muito demorado. Dê uma olhada na documentação, lá fala em várias partes que os componentes não são thread safe.

Quanto a sua dúvida do progresso, vc precisaria saber qual o tamanho final da sua consulta e saber qual o registro atual, aí é só vc usar regra de três para calcular o progresso atual.

Porque quando eu tento executar 2 threads como a que mandei anteriormente eu recebo uma Exception como segue?

java.util.concurrent.ExecutionException: java.lang.NullPointerException at java.util.concurrent.FutureTask$Sync.innerGet(FutureTask.java:222) at java.util.concurrent.FutureTask.get(FutureTask.java:83) at javax.swing.SwingWorker.get(SwingWorker.java:583) at br.com.kooky.sikget.ui.ConhecimentoUI$16.done(ConhecimentoUI.java:1538)

Olha a excessão: java.util.concurrent.ExecutionException: java.lang.NullPointerException

Vc está tentando acessar algum membro não estático de uma referência nula.

Olha onde o erro está sendo gerado (linha 1538 da sua classe): at br.com.kooky.sikget.ui.ConhecimentoUI$16.done(ConhecimentoUI.java:1538)