Suporte de concorrência da área de trabalho

Já vimos que o quadro da UI de JFace faculta suporte básico para mostrar o progresso das tarefas num diálogo (consulte Operações de longa execução para detalhes). Na Infra-estrutura de simultaneidade, revimos o suporte de tempo de execução da plataforma para simultaneidade e operações de longa execução. Agora veremos como a UI da plataforma aperfeiçoa esta infra-estrutura no pacote org.eclipse.ui.progress. Este pacote faculta a UI para mostrar progresso de trabalhos na área de trabalho e define suporte adicional para trabalhos que sejam executados no módulo da UI.

Primeiro, vejamos os diferentes tipos de operações em segundo plano que podem estar em execução e como são apresentados na UI da área de trabalho:


Perante um ambiente em que várias coisas acontecem ao mesmo tempo, o utilizador precisa do seguinte:

Serviço de progresso

O serviço de progresso da área de trabalho (IProgressService) é a interface principal para o suporte de progresso na área de trabalho. Pode ser obtido junto da área de trabalho e depois usado para mostrar progresso, tanto para operações em segundo plano como para aquelas executadas no módulo da UI. A principal finalidade desta classe consiste em facultar um ponto de compra para operações em execução, evitando assim a necessidade de os programadores de plug-in decidirem qual o mecanismo a utilizar para mostrar progresso em dada situação. Outra vantagem é que o diálogo de progresso apresentado com estes métodos faculta bom suporte para indicar quando uma operação está bloqueada por outra, e dá ao utilizador o controlo de resolver o conflito. Sempre que possível, as operações de longa execução devem ser executadas com IProgressService#busyCursorWhile:

   IProgressService progressService = PlatformUI.getWorkbench().getProgressService();
   progressService.busyCursorWhile(new IRunnableWithProgress(){
         public void run(IProgressMonitor monitor) {
         //realizar trabalho alheio à UI
      }
   });

Este método irá inicialmente mostrar um cursor ocupado, e substituí-lo por um diálogo de progresso se a operação demorar mais do que determinado limiar de tempo. A vantagem deste método sobre o uso de um diálogo de progresso é que este último não é mostrado se a operação for de curta execução. Se a operação vier a actualizar a UI, poderá sempre utilizar Display.asyncExec ou Display.syncExec para executar código que modifique a UI.

Se uma operação vier a ser executada na totalidade no módulo da UI; deverá ser usado IProgressService#runInUI. Este método também apresenta um diálogo de progresso se a operação for bloqueada e dá o controlo ao utilizador.

   progressService.runInUI(
      PlatformUI.getWorkbench().getProgressService(),
      new IRunnableWithProgress() {
         public void run(IProgressMonitor monitor) {
         //realizar trabalho da UI
         }
      },
      Platform.getWorkspace().getRoot());

O terceiro parâmetro pode ser nulo ou uma regra de agendamento para a operação. Neste exemplo, especificamos a raiz do espaço de trabalho que o irá essencialmente bloquear enquanto for executada esta operação de UI.

Também pode registar um ícone para uma família de trabalhos junto do serviço de progresso, para que a vista de progresso possa mostrar o ícone junto ao trabalho em execução. De seguida é apresentado um exemplo que mostra como a família de trabalhos de construção automática é associada ao seu ícone:

   IProgressService service = PlatformUI.getWorkbench().getProgressService();
   ImageDescriptor newImage = IDEInternalWorkbenchImages.getImageDescriptor(
      IDEInternalWorkbenchImages.IMG_ETOOL_BUILD_EXEC);
   service.registerIconForFamily(newImage, ResourcesPlugin.FAMILY_MANUAL_BUILD);
   service.registerIconForFamily(newImage, ResourcesPlugin.FAMILY_AUTO_BUILD);

Mostrar que uma parte está ocupada

IWorkbenchSiteProgressService inclui uma API para agendadar trabalhos que alterem a aparência de uma parte de área de trabalho enquanto o trabalho estiver em execução. Se o plug-in estiver a executar operações em segundo plano que afectem o estado de uma parte, poderá agendadar o trabalho através da parte e o utilizador receberá feedback de que a parte está ocupada. Segue-se um exemplo:

   IWorkbenchSiteProgressService siteService =
      (IWorkbenchSiteProgressService)view.getSite().getAdapter(IWorkbenchSiteProgressService.class);
   siteService.schedule(job, 0 /* now */, true /* utilizar o cursor meio-ocupado na parte */);

Propriedades de Progresso para Trabalhos

A área de trabalho define propriedades relacionadas com progresso para trabalhos em IProgressConstants . Estas podem ser usadas para controlar como um trabalho é mostrado na vista de progresso. Estas podem ser usadas para dizer à vista de progresso que mantenha (IProgressConstants#KEEP_PROPERTY) o trabalho na vista depois deste terminar ou manter somente um único (IProgressConstants#KEEPONE_PROPERTY) trabalho de cada vez na vista. Também é possível associar uma acção IProgressConstants#ACTION_PROPERTY) a um trabalho. Quando um trabalho tiver uma acção associada, a vista de progresso mostra uma hiperligação para que o utilizador possa executar a acção. Também poderá saber se um trabalho está a ser mostrado num diálogo de progresso (IProgressConstants#PROPERTY_IN_DIALOG). Será facultada uma pista no canto inferior direito da linha de estado quando estiver disponível uma acção. O exemplo seguinte utiliza estas propriedades:

   Job job = new Job("Realizar Trabalho") {
         public IStatus run(IProgressMonitor monitor) {
      // realizar algum trabalho.
         // Manter o trabalho terminado na vista de progresso somente se não estiver em execução no diálogo de progresso
         Boolean inDialog = (Boolean)getProperty(IProgressConstants.PROPERTY_IN_DIALOG);
         if(!inDialog.booleanValue())
            setProperty(IProgressConstants.KEEP_PROPERTY, Boolean.TRUE);
      }
   };
   job.setProperty(IProgressConstants.ICON_PROPERTY, Plugin.getImageDescriptor(WORK_IMAGE));
   IAction gotoAction = new Action("Results") {
      public void run() {
         // mostrar os resultados
      }
   };
   job.setProperty(IProgressConstants.ACTION_PROPERTY, gotoAction);
   job.setUser(true);
   job.schedule();

Trabalhos de área de trabalho

Sempre que possível, as operações de longa execução devem ser executadas fora do módulo da UI. Todavia, isto nem sempre pode ser evitado quando a finalidade da operação for exactamente actualizar a UI. A secção Questões de módulos de SWT explica como se pode fazer isto no Ecrã do SWT. A área de trabalho define um trabalho especial, UIJob, cujo método run é executado dentro de um asyncExec de SWT. As subclasses de UIJob devem implementar o método runInUIThread e não o método run.

O WorkbenchJob estende UIJob de modo a que o trabalho só possa ser agendado ou executado quando a área de trabalho estiver em execução. Como sempre, recomenda-se evitar trabalho excessivo no módulo da UI porque esta não será renovada enquanto decorrer o Trabalho de UI (UI Job).