Usando o modo de rosqueamento de swingworker
É muito importante que os desenvolvedores de balanço usem a simultaneidade com cuidado. Um bom programa de giro usa mecanismos de simultaneidade para criar interfaces de usuário que não perdem resposta - independentemente do tipo de interação do usuário, o programa sempre pode responder a ele. Para criar um programa responsivo, os desenvolvedores devem aprender a usar o Multithreading na estrutura do swing.
Um desenvolvedor de swing lidará com os seguintes tipos de threads:
(1) Triques iniciais, esses threads executarão o código do aplicativo de inicialização.
(2) O encadeamento de despacho de eventos, todos os códigos de processamento de eventos são executados aqui. A maioria dos códigos que interage com a estrutura de swing também deve executar este thread.
(3) Os threads de trabalhadores, também conhecidos como threads de fundo, executarão todas as tarefas demoradas.
Os desenvolvedores não precisam criar esses threads explicitamente em seu código: eles são fornecidos pela estrutura de tempo de execução ou swing. O trabalho dos desenvolvedores é usar esses threads para criar programas de giro responsivos e persistentes.
Como todos os outros programas em execução em plataformas Java, um programa de swing pode criar threads adicionais e pools de threads, o que requer o uso da abordagem que será introduzida neste artigo. Este artigo apresentará os três threads acima. A discussão dos threads de trabalhadores envolverá o uso da classe javax.swing.swingworker. Esta classe possui muitos recursos úteis, incluindo comunicação e colaboração entre tarefas de threads do trabalhador e outras tarefas de threads.
1. Tópico inicial
Cada programa gera uma série de threads no início da lógica do aplicativo. Em um programa padrão, existe apenas um desses threads: este tópico chamará o método principal na classe principal do programa. Em um applet, o thread inicial é um construtor do objeto Applet, que chamará o método init; Essas ações podem ser executadas em um único thread, ou em dois ou três encadeamentos diferentes, tudo dependendo da implementação específica da plataforma Java. Neste artigo, chamamos esse tipo de thread inicial threads.
Em um programa de giro, não há muito o que fazer no thread inicial. Sua tarefa mais básica é criar um objeto executável que inicialize a GUI e orquestra os objetos usados para executar eventos no thread de expedição de eventos. Depois que a GUI for criada, o programa será conduzido principalmente por eventos da GUI, cada um dos quais causará a execução de um evento no thread de expedição de eventos. O código do programa pode orquestrar tarefas adicionais para threads acionados por eventos (desde que sejam executados rapidamente para que não interfiram no processamento de eventos) ou criem threads trabalhadores (usados para executar tarefas demoradas).
Uma tarefa de criação de GUI da GUI de orquestração de threads inicial é chamando javax.swing.swingutilities.invokelater ou javax.swing.swingutilities.invokeandwait. Ambos os métodos recebem um parâmetro exclusivo: o Runnable é usado para definir novas tarefas. A única diferença entre eles é: o Invocador apenas orquestra a tarefa e retorna; InvoKeandWait aguardará a execução da tarefa antes de retornar.
Veja o seguinte exemplo:
Swingutilities.invokelater (new runnable ()) {public void run () {createandshowgui (); }} No applet, a tarefa de criar a GUI deve ser colocada no método init e usar invocarandwait; Caso contrário, o processo inicial será possível antes que a GUI seja criada, o que pode causar problemas. Em outros casos, as tarefas de criação da GUI da orquestração geralmente são as últimas no thread inicial a ser executado; portanto, ambos usam Invocelater ou Invokeandwait.
Por que o thread inicial não cria a GUI diretamente? Porque quase todo o código usado para criar e interagir com os componentes de giro deve ser executado no thread de despacho de eventos. Essa restrição será discutida abaixo.
2. Tópico de distribuição de eventos
O código de processamento do evento Swing é executado em um thread especial, chamado de thread de expedição de eventos. A maior parte do código que chama o método de giro é executada neste thread. Isso é necessário porque a maioria dos objetos oscilantes é "não-thread segura".
Você pode pensar na execução do código como executando uma série de tarefas curtas no thread de expedição de eventos. A maioria das tarefas é chamada por métodos de manuseio de eventos, como o ActionListener.actionPormed. As tarefas restantes serão orquestradas pelo código do programa, usando o Invokelater ou o InvoKeandWait. As tarefas no thread de despacho de eventos devem poder ser executadas rapidamente. Caso contrário, os eventos não processados serão atrasados e a interface do usuário se tornará "responsiva".
Se você precisar determinar se o seu código é executado no thread de despacho de eventos, ligue para javax.swing.swingutilities.iseventDispatchThread.
3 fios de trabalhador e trabalhador
Quando um programa de swing precisa executar uma tarefa longa, geralmente será feito usando um thread de trabalhador. Cada tarefa é executada em um thread trabalhador, que é uma instância da classe javax.swing.swingworker. A classe dos swingworker é uma classe abstrata; Você precisa definir sua subclasse para criar um objeto de swingworker; Geralmente use classes internas anônimas para fazer isso.
O SwingWorker fornece alguns recursos de comunicação e controle:
(1) A subclasse do swingworker pode definir um método, feito. Quando a tarefa em segundo plano for concluída, ela será automaticamente chamada pelo thread de despacho de eventos.
(2) A classe de swingworker implementa java.util.concurrent.future. Essa interface permite que as tarefas em segundo plano forneçam um valor de retorno para outros threads. Os métodos nessa interface também fornecem as funções que permitem a desfazer das tarefas de segundo plano e determinam se as tarefas de segundo plano foram concluídas ou revogadas.
(3) As tarefas de segundo plano podem fornecer resultados intermediários chamando swingworker.bublish, e o encadeamento de despacho de eventos chamará esse método.
(4) Tarefas de fundo podem definir propriedades de ligação. As alterações no atributo de ligação desencadearão eventos e o encadeamento de despacho de eventos chamará o manipulador de eventos para lidar com esses eventos acionados.
4. Tarefas de back -end simples
Aqui está um exemplo, essa tarefa é muito simples, mas é uma tarefa potencialmente demorada. TumbleItem Applet importa uma série de arquivos de imagem. Se esses arquivos de imagem forem importados através do thread inicial, haverá um atraso antes que a GUI seja exibida. Se esses arquivos de imagem forem importados no thread de despacho do evento, a GUI poderá deixar temporariamente a resposta.
Para resolver esses problemas, a classe Tumbleitem cria e executa uma instância da classe StringWorker quando é inicializada. O método doinbackground desse objeto é executado em um thread trabalhador, importa a imagem em uma matriz Imageicon e retorna uma referência a ele. Em seguida, o método feito é executado no encadeamento de despacho de eventos, obtenha a referência retornada e coloque -o na variável de membro IMGs da classe Applet. Fazer isso permite que a classe TumbleItem crie uma GUI imediatamente sem ter que esperar pela conclusão da Importação da imagem.
O código de amostra a seguir define e implementa um objeto de swingworker.
Trabalhador swingworker = novo swingworker <imageicon [], void> () {@Override public imageicon [] doinbackground () {final imageicon [] inerimgs = new imageicon [nimgs]; for (int i = 0; i <nimgs; i ++) {inerimgs [i] = loadImage (i+1); } retornar innerimgs; } @Override public void done () {// Remova o rótulo "Carregando imagens". animator.removeall (); loopslot = -1; tente {imgs = get (); } catch (interruptedException ignore) {} catch (java.util.concurrent.executionException e) {string why = null; Causação de arremesso = e.getcause (); if (causa! = null) {why = caus.getMessage (); } else {why = e.getMessage (); } System.err.println ("Erro Recuperação do arquivo:" + Por quê); }}}}; Todas as subclasses herdadas do swingworker devem implementar doinbackground; A implementação do método feito é opcional.
Observe que o SwingWorker é uma classe de paradigma com dois parâmetros. O primeiro parâmetro de tipo especifica o tipo de retorno do doinbackground. É também um tipo de método get, que pode ser chamado por outros threads para obter o valor de retorno do Doinbackground. O segundo parâmetro de tipo especifica o tipo de resultado intermediário. Este exemplo não retorna o resultado intermediário, por isso está definido como anulado.
Usando o método GET, você pode fazer a referência aos IMGs do objeto (criado no thread do trabalhador) ser usado no encadeamento de despacho do evento. Isso permite compartilhar objetos entre threads.
Na verdade, existem duas maneiras de devolver o objeto pela aula doinbackground.
(1) Não há parâmetros para chamar o swingworker.get. Se a tarefa em segundo plano não for concluída, o método GET será bloqueado até que seja concluído.
(2) Chamada SwingWorker. Se a tarefa em segundo plano não for concluída, bloqueie até concluir - a menos que o tempo limite expire, nesse caso, será lançado um java.util.concurrent.timeoutException.
5. Tarefas com resultados intermediários
É útil ter uma tarefa de back -end de trabalho fornecer resultados intermediários. As tarefas de back -end podem chamar o método swingworker.bublish para fazer isso. Este método aceita muitos parâmetros. Cada parâmetro deve ser especificado pelo segundo parâmetro de tipo do swingworker.
O swingworker.process pode ser substituído para salvar os resultados fornecidos pelo método de publicação. Este método é chamado pelo thread de expedição de eventos. O conjunto de resultados do método de publicação é geralmente coletado por um método de processo.
Vamos dar uma olhada nos exemplos fornecidos pelo filpper.java. Este programa gera uma série de testes booleanos aleatórios java.util.random através de uma tarefa de segundo plano. É como um experimento de arremesso de moedas. Para relatar seus resultados, a tarefa de segundo plano usa um objeto Flippir.
Classe estática privada Flippair {private Final Long Heads, Total; Flippair (cabeças longas, total longo) {this.heads = cabeças; this.Total = Total; }} As cabeças representam o resultado de verdadeiro; O total representa o número total de arremessos.
O programa de fundo é uma instância do FilPask:
Classe privada Fliptask estende o swingworker <void, Flippiair> {
Como a tarefa não retorna um resultado final, não há necessidade de especificar qual é o primeiro parâmetro de tipo, use o vazio. Após cada "arremesso", a tarefa chama publicação:
@OverrideProtected void Doinbackground () {Long Heads = 0; total longo = 0; Aleatório aleatório = novo aleatório (); while (! iscancelled ()) {total ++; if (random.nextBoolean ()) {cabeças ++; } publicar (New Flippiair (cabeças, total)); } retornar nulo;} Como a publicação é frequentemente chamada, muitos valores de Flippair serão coletados antes que o método do processo seja chamado pelo thread de expedição de eventos; O processo se concentra apenas no último conjunto de valores retornados a cada vez e o usa para atualizar a GUI:
Processo de void protegido (pares de listas) {Flippair par = par.get (par.Size () - 1); headstext.settext (string.format ("%d", par.heads)); totalText.Settext (String.Format ("%d", par.total)); DevText.Settext (String.Format ("%. 10G", ((duplo) par.heads)/((duplo) par.total) - 0,5));} 6. Cancelar a tarefa de segundo plano
Ligue para o swingworker.Cancel para cancelar uma tarefa de segundo plano em execução. A tarefa deve ser consistente com seu próprio mecanismo de desfazer. Existem duas maneiras de fazer isso:
(1) Quando uma interrupção for recebida, ela será encerrada.
(2) CHAMADA SWINGWORKER.ISCELED. Se as chamadas do SwingWorker cancelarem, o método retornará true.
7. Bind atributos e métodos estaduais
O SwingWorker suporta propriedades vinculadas, o que é muito útil ao se comunicar com outros threads. Fornece duas propriedades vinculativas: progresso e estado. O progresso e o estado podem ser usados para acionar as tarefas de processamento de eventos nos threads de despacho de eventos.
Ao implementar um ouvinte de mudança de propriedade, o programa pode capturar alterações em andamento, estado ou outras propriedades vinculativas.
7.1A variável encadernada de progresso
A variável de ligação de progresso é uma variável inteira com um intervalo de 0 a 100. Predefiniu o setter (os métodos SwingWorker.setProgress protegidos) e getter (os swingworker.getProgress).
7.2A variável encadernada no estado
As mudanças nas variáveis de ligação do estado refletem o processo de alteração do objeto de swingworker durante seu ciclo de vida. Esta variável contém um tipo de enumeração de swingworker.StateValue. Valores possíveis são:
(1) pendente
Esse estado dura um tempo para saber a partir da criação do objeto que o método doinbackground é chamado.
(2) começou
Esse estado dura um momento antes de o método doinbackground ser chamado até que o método feito seja chamado.
(3) feito
O tempo restante em que o objeto existe permanecerá nesse estado.
Se você precisar retornar o valor do estado atual, poderá chamar o swingworker.getState.
7.3STATUS Métodos
Dois métodos fornecidos pela interface futura também podem relatar o status das tarefas de segundo plano. Se a tarefa for cancelada, o ISCanceled retorna verdadeiro. Além disso, se a tarefa for concluída, ou seja, ela será concluída normalmente ou cancelada, o ISDONE retorna true.
Usando contêiner de nível superior
O Swing fornece 3 classes de contêiner de nível superior: JFrame, JDialog, Jappt. Ao usar essas três classes, você deve prestar atenção aos seguintes pontos:
(1). Para ser exibido na tela, cada componente da GUI deve fazer parte da hierarquia contendo. A hierarquia de inclusão é uma estrutura de árvore do componente, e o contêiner de nível superior é sua raiz.
(2). Cada componente da GUI só pode ser incluído uma vez. Se um componente já estiver em um contêiner e tentar adicioná -lo a um novo contêiner, o componente será removido do primeiro contêiner e adicionado ao segundo contêiner.
(3). Cada contêiner de nível superior possui um painel de conteúdo. Geralmente, este painel de conteúdo conterá (direta ou indiretamente) todos os componentes visuais da GUI de contêiner de nível superior.
(4). Você pode adicionar uma barra de menus ao recipiente superior. Normalmente, essa barra de menus é colocada no recipiente superior, mas fora do painel de conteúdo.
1. Recipientes de nível superior e hierarquias de inclusão
Cada programa que usa o componente giratório possui pelo menos um contêiner de nível superior. Este contêiner de nível superior é o nó raiz que contém a hierarquia-esta hierarquia conterá todos os componentes de giro que aparecerão neste contêiner de nível superior.
Normalmente, um aplicativo baseado em GUI em swing separado possui pelo menos uma hierarquia de inclusão e seu nó raiz é um JFrame. Por exemplo, se um aplicativo tiver uma janela e duas caixas de diálogo, o aplicativo terá três níveis de inclusão, ou seja, haverá três contêineres de nível superior. Uma hierarquia de contenção toma o JFrame como seu nó raiz, e duas outras hierarchas de contenção têm um jdialog como seu nó raiz.
Um applet baseado em componentes de giro contém pelo menos uma hierarquia de inclusão e pode ser determinado que um deles deve ser feito com um japplet como nó raiz. Por exemplo, um applet com uma caixa de diálogo, ele terá dois níveis de inclusão. O componente na janela do navegador será colocado em uma hierarquia de contenção e seu nó raiz é um objeto Japplet. A caixa de diálogo terá uma hierarquia contendo e seu nó raiz é um objeto JDialog.
2. Adicione componentes ao painel de conteúdo
A operação de código a seguir é obter o painel de conteúdo do quadro no exemplo acima e adicionar um rótulo amarelo:
frame.getContentPane (). Add (YellowLabel, borderlayout.center);
Conforme mostrado no código, você deve primeiro encontrar o painel de conteúdo do contêiner de nível superior e implementá-lo através do método getContentPane. O painel de conteúdo padrão é um contêiner intermediário simples, que herda do JComponent, usando uma borderlayout como gerente de painel.
A personalização de um painel de conteúdo é simples - configure um gerenciador de painel ou adicione fronteiras. Deve -se notar aqui que o método getContentPane retornará um objeto de contêiner, não um objeto JComponent. Isso significa que, se você precisar tirar proveito de algumas das funcionalidades do JComponent, também deverá digitar o valor de retorno ou criar seu próprio componente como painel de conteúdo. Nosso exemplo geralmente usa o segundo método. Porque o segundo método é mais claro e claro. Outra maneira de usá-lo às vezes é simplesmente adicionar um componente autodefinado ao painel de conteúdo para cobrir completamente o painel de conteúdo.
Se você criar seu próprio painel de conteúdo, tenha cuidado para garantir que seja opaco. Um JPanel opaco será uma boa escolha. Observe que, por padrão, o layout do JPanel é gerenciado como FlowLayout e você pode substituí -lo por outro gerenciador de layout.
Para que um componente seja um painel de conteúdo, você precisa usar o método setContentPane do contêiner de nível superior, por exemplo:
// Crie um painel e adicione componentes a ele.jpanel contentPane = new JPanel (new BorderLayout ()); contentPane.setBorder (algumborder); contentPane.add (SOMECOMPONENT, borderlayout.center); contentPane.add (outro componente, borderlayout.page_end); pane.//contentPane.SETOPAQUE(TRUE);
NOTA: Não use recipientes transparentes como painéis de conteúdo, como jscrollpane, jsplitpane e jtabbedpane. Um painel de conteúdo transparente fará com que os componentes sejam confundidos. Embora você possa tornar o componente de balanço transparente opaco através do método Setopaque (True), ele não parecerá correto quando alguns componentes estiverem definidos para serem completamente opacos. Por exemplo, um painel de tags.
3. Adicionando uma barra de menu
Em teoria, cada contêiner de nível superior pode ter uma barra de menus. Mas os fatos mostram que a barra de menus aparece apenas em quadro ou applet. Para adicionar uma barra de menus ao contêiner de nível superior, você precisa criar um objeto Jmenubar, montar alguns menus e chamar o método setjmenubar. A instância do ToplevelDemo adiciona uma barra de menus ao seu quadro através do código a seguir.
frame.setjmenubar (cianmenubar);
4. O painel da raiz
Cada contêiner de nível superior conta com um contêiner intermediário implícito chamado contêiner raiz. Este contêiner raiz gerencia o painel de conteúdo e a barra de menus, juntamente com dois ou mais outros contêineres (consulte o painel em camadas, etc.). Normalmente, você não precisa saber sobre o uso do contêiner de raiz do componente de swing. No entanto, se você deseja interceptar um clique do mouse ou desenhar em vários componentes, precisará conhecer o contêiner raiz.
O acima foi descrito sobre o painel de conteúdo e a barra de menus opcional e não será repetido aqui. Os outros dois componentes contidos no contêiner raiz são o painel de layout e o painel de vidro. O painel de layout contém diretamente o painel de menus e o painel de conteúdo e permite classificar os outros componentes adicionados pelas coordenadas z. Os painéis de vidro são geralmente usados para interceptar ações de entrada que ocorrem na camada superior e também podem ser usadas para desenhar em vários componentes.