Obsługa współbieżności w środowisku roboczym

Wiadomo już, że środowisko interfejsu użytkownika JFace udostępnia podstawową obsługę wyświetlania postępu czynności w oknie dialogowym (szczegółowe informacje na ten temat zawiera sekcja Długotrwałe operacje). W sekcji Infrastruktura współbieżności omówiono obsługę środowiska wykonawczego platformy w zakresie współbieżności i długotrwałych operacji. Tematem niniejszej sekcji jest sposób rozszerzenia tej infrastruktury przez interfejs użytkownika platformy za pomocą pakietu org.eclipse.ui.progress. Pakiet ten umożliwia wyświetlanie postępu zadań w środowisku roboczym oraz definiuje dodatkową obsługę dla zadań uruchamianych w wątku interfejsu użytkownika.

Istnieją różne rodzaje operacji działających w tle i różne sposoby wyświetlania informacji o nich przez interfejs użytkownika środowiska roboczego:


W środowisku, w którym wiele zadań może być wykonywanych jednocześnie, użytkownik powinien mieć dostęp do:

Usługa postępu

Usługa postępu w środowisku roboczym (IProgressService) to główny interfejs obsługi wyświetlania postępu w środowisku roboczym. Można ją uzyskać ze środowiska roboczego, a następnie użyć do wyświetlania postępu zarówno operacji wykonywanych w tle, jak i operacji uruchamianych w wątku interfejsu użytkownika. Głównym celem tej klasy jest zapewnienie kompleksowej obsługi uruchamianych operacji i zwolnienie programistów modułów dodatkowych z obowiązku decydowania, który mechanizm powinien być używany do wyświetlania postępu w danej sytuacji. Inną zaletą jest to, że okno dialogowe postępu wyświetlane przy użyciu tych metod zapewnia sygnalizację sytuacji, w których jedna operacja jest blokowana przez inną, i przekazuje sterowanie użytkownikowi w celu rozwiązania konfliktu. O ile to możliwe, długotrwałe operacje powinny być uruchamiane przy użyciu interfejsu IProgressService#busyCursorWhile:

   IProgressService progressService = PlatformUI.getWorkbench().getProgressService();
   progressService.busyCursorWhile(new IRunnableWithProgress(){
         public void run(IProgressMonitor monitor) {
         //wykonanie pracy niezwiązanej z interfejsem użytkownika
      }
   });

Metoda ta powoduje początkowo wyświetlenie kursora zajętości i zastępuje go oknem dialogowym postępu, gdy operacja trwa dłużej niż określony limit czasu. Zaletą tej metody w porównaniu z korzystaniem z okna dialogowego postępu jest to, że dla krótkotrwałych operacji okno dialogowe postępu nie będzie wyświetlane. Jeśli operacja musi aktualizować interfejs użytkownika, można użyć wątku Display.asyncExec lub wątku Display.syncExec, aby uruchomić kod modyfikujący interfejs użytkownika.

W przypadku, gdy operacja musi być w całości wykonana w wątku interfejsu użytkownika, należy użyć metody IProgressService#runInUI. Wyświetla ona okno postępu i przekazuje sterowanie użytkownikowi także wtedy, gdy operacja zostanie zablokowana.

   progressService.runInUI(
      PlatformUI.getWorkbench().getProgressService(),
      new IRunnableWithProgress() {
         public void run(IProgressMonitor monitor) {
         //wykonanie pracy związanej z interfejsem użytkownika
         }
      },
      Platform.getWorkspace().getRoot());

Trzeci parametr może mieć wartość NULL lub może być regułą planowania dla operacji. W takiej sytuacji podaje się katalog główny obszaru roboczego, co blokuje obszar roboczy na czas działania tej operacji interfejsu użytkownika.

W usłudze postępu można także zarejestrować ikonę dla rodziny zadań, dzięki czemu w widoku postępu ikona ta będzie wyświetlana obok wykonywanego zadania. Oto przykład powiązania rodziny zadań automatycznego budowania z ikoną:

   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);

Informowanie o zajętości części

Interfejs IWorkbenchSiteProgressService zawiera funkcje API na potrzeby planowania zadań, zmieniające wygląd środowiska roboczego podczas wykonywania zadania. Jeśli moduł dodatkowy uruchamia w tle operacje wpływające na stan danej części, można zaplanować zadanie przy jej użyciu, dzięki czemu użytkownik będzie informowany o tym, że część jest zajęta. Oto przykład:

   IWorkbenchSiteProgressService siteService =
      (IWorkbenchSiteProgressService)view.getSite().getAdapter(IWorkbenchSiteProgressService.class);
   siteService.schedule(job, 0 /* teraz */, true /* wewnątrz części użyj kursora częściowej zajętości */);

Właściwości postępu dla zadań

Środowisko robocze definiuje właściwości postępu dla zadań przy użyciu interfejsu IProgressConstants . Służą one do sterowania sposobem wyświetlania informacji o zadaniu w widoku postępu. Pozwalają one skonfigurować widok postępu w taki sposób, aby zachowywał (IProgressConstants#KEEP_PROPERTY) zadanie w widoku po jego zakończeniu lub żeby przechowywał zadania w widoku pojedynczo (IProgressConstants#KEEPONE_PROPERTY). Z zadaniem można także powiązać akcję (IProgressConstants#ACTION_PROPERTY). Gdy z zadaniem jest powiązana akcja, w widoku postępu wyświetlany jest odsyłacz hipertekstowy, który służy do uruchomienia akcji. Można także dowiedzieć się, czy zadanie użytkownika jest w danej chwili wyświetlane w oknie dialogowym postępu (IProgressConstants#PROPERTY_IN_DIALOG). Gdy akcja jest dostępna, z prawej strony u dołu w wierszu statusu wyświetlana jest wskazówka. Omawiane właściwości zostały użyte w poniższym przykładzie:

   Job job = new Job("Do Work") {
         public IStatus run(IProgressMonitor monitor) {
         // wykonywania zadania
         // zakończone zadanie zostaje w widoku postępu tylko, jeśli nie było wyświetlane w oknie dialogowym postępu
         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() {
         // wyświetla wyniki
      }
   };
   job.setProperty(IProgressConstants.ACTION_PROPERTY, gotoAction);
   job.setUser(true);
   job.schedule();

Zadania środowiska roboczego

O ile to możliwe, długotrwałe operacje powinny być wykonywane poza wątkiem interfejsu użytkownika. Nie jest to jednak możliwe, gdy celem operacji jest zaktualizowanie interfejsu użytkownika. W sekcji Zagadnienia dotyczące wątków SWT objaśniono, w jaki sposób można to zrobić przy użyciu widgetu Display z pakietu SWT. Środowisko robocze definiuje specjalne zadanie, UIJob, którego metoda uruchamiania działa wewnątrz wątku asyncExec pakietu SWT. Podklasy klasy UIJob powinny implementować metodę runInUIThread zamiast metody run.

Klasa WorkbenchJob rozszerza klasę UIJob w taki sposób, że zadanie może zostać zaplanowane lub uruchomione tylko wtedy, gdy działa środowisko robocze. Jak zawsze należy unikać nadmiernego obciążenia wątku interfejsu użytkownika, ponieważ interfejs użytkownika nie będzie odświeżany podczas trwania zadania UIJob.