我們已經看到 JFace UI 架構提供了在對話框中顯示作業進度的基本支援(請參閱長時間執行的作業,以取得詳細資料)。在並行基礎架構中,我們也檢視了並行和長時間執行的作業的平台執行時期支援。現在,我們要檢視在 org.eclipse.ui.progress 套件中,平台 UI 如何加強這個基礎架構。這個套件提供了在工作台中顯示工作進度的 UI,也定義了 UI 執行緒中執行之工作的其他支援。
首先,讓我們查看可能會執行的不同類型背景作業,以及它們在工作台使用者介面中的顯示方式。
使用者起始工作是使用所觸發的工作。工作台會在模型進度對話框中自動顯示使用者工作,對話框中有一個可讓使用者在背景中執行作業並繼續工作的按鈕。有一項廣域喜好設定用來指示使用者工作是否應該一律在背景中執行。就本身來說,使用者工作使用 (Job#setUser) 在「工作 API」中區別。使用者工作的範例包括建置、移出專案、與儲存庫同步、匯出外掛程式以及搜尋。
自動觸發工作對使用者有意義,但不是使用者所起始的作業。這些工作會顯示在進度視圖及狀態行中,但它們執行時,不會出現模型進度對話框。範例包括自動建置和已排定的同步化。
系統作業不是使用者所觸發且可視為平台實作細節。這些工作是設定系統旗標 (Job#setSystem) 來建立。系統工作的範例包括智慧式移入小組件,或計算視圖的裝飾和註解。
在給定可能同時發生許多事項的環境之後,使用者需要下列項目:
已啟動長時間執行的作業的指示。
使用者工作顯示在提供立即回饋的進度對話框中,自動觸發的工作顯示在狀態行和進度視圖中。會影響組件的工作應該是組件所排定或登錄的工作,因此,工作台能夠提示使用者某個執行中的項目會影響組件。
作業已結束的指示。
當使用者工作結束時,使用者可以很容易得知,因為這時會關閉進度對話框。如果是非使用者工作,則有一些回饋機制可供使用。如果工作是組件所排定或登錄的工作,當工作完成時,會顯示組件的進度提示。如果工作傳回錯誤,狀態行的右下角會出現錯誤指示器,顯示發生錯誤的提示。
有用的新結果或新資訊的指示,且不會使用對話框來取得焦點。
當作業完成時,使用者可以直接將結果呈現給使用者。如果是非使用者工作,建議您不要用對話框來顯示結果,才不會岔斷使用者。例如,工作啟動時,以及顯示在這個視圖的結果不會岔斷使用者的工作流程時,才能開啟視圖。另外,您也可以新增工作內容,來指示它應該保留在進度視圖中,且它提供了一個用來顯示結果的動作。在此情況下,當工作仍在進度視圖中,且有結果要提供給使用者時,狀態行右下角會出現一個警告指示。
感覺能夠控制執行中的項目,有能力監視和取消背景作業。
使用者工作提供使用者最好的控制能力,因為使用者工作可以輕易地被取消,
並且透過進度對話框的詳細資料標籤來提供暫停執行或並行執行作業的有力指示。請注意,只有在外掛程式使用 IProgressService#busyCursorWhile 或 IProgressService#runInUI 時,
才會顯示提供詳細資料區的加強型進度對話框。另外,進度視圖也可用來存取執行中的工作。
所有已安裝外掛程式的一致進度報告。
使用進度服務 API 的好處是使用者會有一致的進度體驗。
工作台進度服務 (IProgressService) 是工作台進度支援的主要介面。它可以從工作台中取得,之後,便可用來顯示背景作業以及在 UI 執行緒中執行的作業的進度。這個類別的主要用途是提供執行中的作業的單點購物功能,外掛程式開發人員不需要決定在給定狀態中應該用哪個機制來顯示進度。另一個好處是這些方法所顯示的進度對話框,能夠很好地支援某項作業為其他作業所阻斷時的指示, 同時也提供解決衝突的使用者控制能力。如果可能的話,應該利用 IProgressService#busyCursorWhile 來執行長時間執行的作業:
IProgressService progressService = PlatformUI.getWorkbench().getProgressService(); progressService.busyCursorWhile(new IRunnableWithProgress(){ public void run(IProgressMonitor monitor) { //執行非 UI 工作 } });
這個方法最初會建立一個工作中游標,如果作業持續時間超出指定的時間臨界值,就會用一個對話框來取代它。這個方法比使用進度對話框好,因為如果作業的執行時間較短,就不會顯示進度對話框。如果您的作業必須更新 UI,您一律可以使用 Display.asyncExec 或 Display.syncExec 來執行修改 UI 的程式碼。
如果作業必須完全在使用者介面執行緒中執行,則應該使用 IProgressService#runInUI。如果作業暫停執行,這個方法也會顯示一個進度對話框,使用者可以進行控制,讓使用者可以進行控制。
progressService.runInUI( PlatformUI.getWorkbench().getProgressService(), new IRunnableWithProgress() { public void run(IProgressMonitor monitor) { //執行 UI 工作 } }, Platform.getWorkspace().getRoot());
第三個參數可以是空值,或是作業的排程規則。在本例中,我們指定工作區的根目錄,基本上,當這項使用者介面作業執行時,這會鎖定工作區。
您也可以用進度服務來登錄工作系列的圖示,使進度視圖能夠在執行中的工作旁顯示這個圖示。以下是顯示自動建置工作系統如何與其圖示產生關聯的範例:
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);
IWorkbenchSiteProgressService 包括一個 API,當工作在執行中時,可排定其他工作來變更工作台組件的外觀。如果您的外掛程式正在執行會影響某個組件的狀態之背景作業,您可以透過這個組件來排定工作,使用者會得到組件在工作中的回饋。以下是一個範例:
IWorkbenchSiteProgressService siteService = (IWorkbenchSiteProgressService)view.getSite().getAdapter(IWorkbenchSiteProgressService.class); siteService.schedule(job, 0 /* now */, true /* use the half-busy cursor in the part */);
工作台在 IProgressConstants 中定義工作的進度相關內容。這可用來控制工作在進度視圖中的顯示方式。它們可用來告訴進度視圖在工作完成之後將工作保留 (IProgressConstants#KEEP_PROPERTY) 在視圖中, 或每次只在視圖中保留一個 (IProgressConstants#KEEPONE_PROPERTY) 工作。您也可以建立某個動作 (IProgressConstants#ACTION_PROPERTY) 與某項工作的關聯性。當工作有相關的動作時,進度視圖會顯示一個超鏈結,供使用者執行動作。您也可以瞭解某項使用者工作目前是否出現在進度對話框 (IProgressConstants#PROPERTY_IN_DIALOG) 中。當有可用的動作時,狀態行右下角會出現提示。下列使用這些內容:
Job job = new Job("Do Work") { public IStatus run(IProgressMonitor monitor) { // 執行部分工作。 // 如果沒有在進度對話框中執行的話,便只將已完成的工作保留在進度視圖中 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() { // 顯示結果 } }; job.setProperty(IProgressConstants.ACTION_PROPERTY, gotoAction); job.setUser(true); job.schedule();
可能的話,應該在 UI 執行緒之外執行長時間執行的作業。不過,當作業目的是更新 UI 的話,不一定能避免這個情況。SWT 執行緒作業問題說明如何利用 SWT Display 來做到這一點。工作台會定義一個特殊工作 UIJob,它的 run 方法是在 SWT asyncExec 內執行的。UIJob 的子類別應該實作 runInUIThread 方法,而不是 run 方法。
WorkbenchJob 繼承 UIJob,因此,工作台必須在執行中,才能排定或執行這個工作。同樣地,在 UI 執行緒中應該避免過量的工作,因為在 UI 工作的持續時間內,不會自動更新 UI。