我們已經看到 JFace UI 組織架構提供了在對話框中顯示作業進度的基本支援(請參閱長時間執行的作業,以取得詳細資料)。
在並行基礎架構中,我們也檢視了並行和長時間執行的作業的平台執行時期支援。
現在,我們要檢視在 org.eclipse.ui.progress 套件中,平台 UI 如何加強這個基礎架構。
這個套件提供了在工作台中顯示工作進度的 UI,也定義了 UI 執行緒中執行之工作的其他支援。
在介紹新的 API 之前,我們先檢視一些基本概念。 首先,我們必須區分不同種類的背景作業:
使用者起始。這些工作設為使用者工作 (Job#setUser)。 工作台會在模型進度對話框中自動顯示使用者工作,對話框中有一個可讓使用者在背景中執行作業並繼續工作的按鈕。 有一項廣域喜好設定用來指示使用者工作是否應該一律在背景中執行。
使用者工作的範例包括建置、移出專案、與儲存庫同步、匯出外掛程式以及搜尋。自動觸發。 這些作業對使用者有意義,但不是使用者所起始的作業。 這是工作的預設變式。 這些工作會顯示在進度視圖及狀態行中,但它們執行時,不會出現模型進度對話框。 範例包括自動建置和已排定的同步化。
系統作業。 不是使用者所觸發且可視為實作細節的作業。 這些工作是設定系統旗標 (Job#setSystem) 來建立。 系統工作的範例包括智慧式移入小組件,或計算視圖的裝飾和註釋。
在給定可能同時發生許多事項的環境之後,使用者需要:
已啟動長時間執行的作業的指示。
使用者工作顯示在提供立即回饋的進度對話框中,自動觸發的工作顯示在狀態行和進度視圖中。
另外,會影響組件的工作應該是組件所排定或登錄的工作,因此,工作台能夠提示使用者某個執行中的項目會影響組件。
作業已結束的指示。
當使用者工作結束時,使用者可以很容易得知,因為這時會關閉進度對話框。
但如果是非使用者工作,則有一些回饋機制可供使用。
如果工作是組件所排定或登錄的工作,當工作完成時,會顯示組件進度提示。
如果工作傳回錯誤,狀態行的右下角會出現錯誤指示器,顯示發生錯誤的提示。
有用的新結果或新資訊的指示,且背景作業不會顯示對話框來取得焦點。
當作業完成時,使用者可以直接將結果呈現給使用者。
如果是非使用者工作,建議您不要用對話框來岔斷使用者。
相反地,如果工作結果顯示在視圖中,當工作開始時,可以開啟視圖,結果會顯示在視圖中。
這不會中斷使用者的工作流程。
另外,您也可以新增工作內容來指示它應該保留在進度視圖中,且有一個用來顯示結果的動作。
當工作仍在進度視圖中,且有結果要提供給使用者時,狀態行右下角會出現一個警告指示。
感覺能夠控制執行中的項目,有能力監視和取消背景作業。
使用者工作提供使用者最好的控制能力,因為使用者工作可以輕易地被取消,
並且透過進度對話框的詳細資料標籤來提供暫停執行或並行執行作業的有力指示。
請注意,只有在使用者呼叫 IProgressService#busyCursorWhile 或 IProgressService#runInUI 時,
才會顯示提供詳細資料區的加強型進度對話框。
另外,進度視圖也可用來存取執行中的工作。
所有已安裝的外掛程式都必須以相同方式來一致地顯示進度。
使用進度服務 API 的好處是使用者會有一致的進度體驗。
之後,我們要進入如何使用新 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 的程式碼。 如果作業必須完全在 UI 執行緒中執行,您應該呼叫 IProgressService#runInUI。 再說明一次,這個方法的好處是,如果作業暫停執行,它會顯示一個進度對話框,使用者可以進行控制。
progressService.runInUI( PlatformUI.getWorkbench().getProgressService(), new IRunnableWithProgress() { public void run(IProgressMonitor monitor) { //執行 UI 工作 } }, Platform.getWorkspace().getRoot());
第三個參數可以是空值,或是作業的排程規則。
在這裡,我們指定工作區的根目錄,基本上,當這項 UI 作業執行時,這會鎖定工作區。
您也可以在進度服務中登錄工作系列的圖示,使進度視圖能夠在執行中的工作旁顯示這個圖示。
以下是自動建置作業如何與其圖示相關聯的範例:
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 half-busy cursor in 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。