Resource Change Listener
介面:IResourceChangeListener
IWorkspace workspace = ResourcesPlugin.getWorkspace(); IResourceChangeListener listener = new IResourceChangeListener() { public void resourceChanged(IResourceChangeEvent event) { System.out.println("Something changed!"); } }; workspace.addResourceChangeListener(listener); //... some time later one ... workspace.removeResourceChangeListener(listener);
在資源改變的通知期間,workspace 會
locked 來避免產生更多的通知。
Resource API
Resource API 在執行
creating,copying,moving 和 deleting
操作時會向外廣播資源改變事件。
資源操作會嵌套,如 Ifile.move 操作會觸發一個
Ifile.create 操作建立新的檔案,然後再觸發一個 Ifile.delete
操作刪除老檔案。所以一個 Ifile.move 操作嵌套了一個
Ifile.create 操作和一個
Ifile.delete 操作,但是以上操作只會通知一次。
Batching Changes
當有一批資源改變事件需要通知時,需要採用Batching的方式來提高效能。這樣能保證只有一個資源改變事件被廣播了出去,而不是多個。
IWorkspace workspace = ResourcesPlugin.getWorkspace(); final IProject project = workspace.getRoot().getProject("My Project"); IWorkspaceRunnable operation = new IWorkspaceRunnable() { public void run(IProgressMonitor monitor) throws CoreException { int fileCount = 10; project.create(null); project.open(null); for (int i = 0; i < fileCount; i++) { IFile file = project.getFile("File" + i); file.create(null, IResource.NONE, null); } } }; workspace.run(operation, null);
eclipse 3.0不再保證IworkspaceRunnable它在執行期間會阻止其他的通知發生。這樣做的原因是為了保證能夠在通知發生期間能夠使UI相應使用者事件。
eclipse 3.0中引入了一個新類WorkspaceJob,可以將大量的Workspace資源改變操作放到一個Job中在後台執行。
一個更強大的特點當資源在Workspace外部被更改ResourceChangeListener也會被通知到,比如直接對檔案系統的檔案變更。由於大多數的作業系統並沒有這種機制,所以需要執行Iresource.refreshLocal操作後,資源改變會通知所有的Listener。
IResourceChangeEvent
l Event Type
PRE_CHANGE,POST_CHANGE,PRE_BUILD,POST_BUILD,PRE_CLOSE,POST_CLOSE,PRE_DELETE,POST_DELETE
l IresourceDelta
IresourceDelta.getKind()
IResourceDelta.ADDED
IResourceDelta.REMOVED
IResourceDelta.CHANGED
IresourceDelta.getFlags()
IResourceDelta.CONTENT
IResourceDelta.REPLACED
IResourceDelta.REMOVED
IResourceDelta.MOVED_FROM
IresourceDelta.getMovedFromPath()
IResourceDelta.MOVED_TO
IresourceDelta.getMovedToPath()
IResourceDelta.MARKERS
(IMarkerDelta[] markers = delta.getMarkerDeltas())
Listener的效能
Resource Change Listener的實現應該是輕量級的,並且能夠快速執行。資源改變的通知會被經常的發出,如果Listener的效能不高,將會影響整個平台的效能。如果在Listener中有大量的工作需要做,最好將它放到後台線程中執行。
使用IResourceDelta.findMember(IPath)可以快速定位到我們關注的IresourceDelta,而不必按樹狀結構依次向下通知。
IresourceDelta.accept(IResourceDeltaVistor)來訪問我們關心的ResourceDeltaVistor樹的分支。
安全執行緒問題:我們不能控制我們的Listener會在哪一個線程中執行。Workspace操作會出現在任何的線程中,Resource
Change Listener也會運行在觸發該操作的任何線程中。如果我們想讓我們的更新操作出現在一個特定的線程中,我們不得不確保代碼被post到那個線程。通常我們要更新使用者介面,就先得到UI的Display對象,使用Display.syncExec()或者Display.asyncExec()方法。
如果採用非同步方式執行,需要注意ResourceDelta都有一個“expire date”.如果傳遞一個IresourceDelta到另外一個線程,如果Listener的ResourceChanged()方法已經在另外一個線程中返回,對IresourceDelta的引用將會發生錯誤。請確保Listener不會一直儲存Resource
Delta的引用,因為這些Resource Delta的數量是很大的,如果一直保持對其引用,會導致memory leak。
Sample
public class DocIndexUpdater implements IResourceChangeListener { private TableViewer table; //assume this gets initialized somewhere private static final IPath DOC_PATH = new Path("MyProject/doc"); public void resourceChanged(IResourceChangeEvent event) { //we are only interested in POST_CHANGE events if (event.getType() != IResourceChangeEvent.POST_CHANGE) return; IResourceDelta rootDelta = event.getDelta(); //get the delta, if any, for the documentation directory IResourceDelta docDelta = rootDelta.findMember(DOC_PATH); if (docDelta == null) return; final ArrayList changed = new ArrayList(); IResourceDeltaVisitor visitor = new IResourceDeltaVisitor() { public boolean visit(IResourceDelta delta) { //only interested in changed resources (not added or removed) if (delta.getKind() != IResourceDelta.CHANGED) return true; //only interested in content changes if ((delta.getFlags() & IResourceDelta.CONTENT) == 0) return true; IResource resource = delta.getResource(); //only interested in files with the "txt" extension if (resource.getType() == IResource.FILE && "txt".equalsIgnoreCase(resource.getFileExtension())) { changed.add(resource); } return true; } }; try { docDelta.accept(visitor); } catch (CoreException e) { //open error dialog with syncExec or print to plugin log file } //nothing more to do if there were no changed text files if (changed.size() == 0) return; //post this update to the table Display display = table.getControl().getDisplay(); if (!display.isDisposed()) { display.asyncExec(new Runnable() { public void run() { //make sure the table still exists if (table.getControl().isDisposed()) return; table.update(changed.toArray(), null); } }); } } }
參考文獻:
1、http://www.eclipse.org/articles/Article-Resource-deltas/resource-deltas.html
2、http://tech.ccidnet.com/art/3741/20031215/542015_1.html
3、http://wenku.baidu.com/view/54dc16eb6294dd88d0d26b0a.html