- 浏览: 150204 次
- 性别:
- 来自: 北京
文章分类
最新评论
-
EclipseEye:
fair_jm 写道不错 蛮详细的 谢谢分享
SWT/JFace专题 --- SWT中Display和多线程 -
fair_jm:
不错 蛮详细的 谢谢分享
SWT/JFace专题 --- SWT中Display和多线程
需求:在IDE(一款基于Eclipse的开发工具)下一次打开工程的时候,恢复上一次关闭前的状态。恢复粒度:和工程相关的。
1.先了解一下Eclipse本身的恢复功能:
经常用Eclipse做开发,我们知道,Eclipse的UI持久化及窗户恢复功能做的是很完善的,比如ProjectExplorer视图中工程的展开状态、Editor区域的文件的代码状态,各个视图的展现状态,Dialog,MenuBar,ToolBar等待,Eclipse都很好的恢复到了上次我们关闭前的状态。
在Eclipse关闭前他会启动UI持久化功能,把需要保持的各个控件的状态信息(IMemento对象)持久化到一个文件中(这个文件一般在Eclipse的安装目录的Workspace下:
..\.metadata\.plugins\org.eclipse.ui.workbench\workbench.xml).
恢复粒度:和整个IDE相关。
2.为什么要定制UI持久化框架
简单说就是Eclipse的本身持久化机制不能满足需要---不能满足需求中的对每个工程粒度的恢复。
需要做的就是为每一个IDE中的工程创建一份持久化文件及操作入口。
于是,先得弄清楚Eclipse的一整套状态恢复机制,找到其持久化和逆持久化的接口,然后展开定制工作。
3.debug方式跟踪Eclipse的UIPresent机制的内部实现
Eclipse什么实现的恢复功能呢,网上搜索资料当然好,但既然都是开源的东西,源码当然是做好的参考资料了。
浏览源码的最好方式就是:debug+断点--》一步步,代码的运行过程看的一清二楚。
(为了容易调试,可以创建一个RCP项目来模拟Eclipse进行调试,其实Eclipse本身也可以看做是一个复杂的RCP项目)
先从持久化入手(从恢复也一样),既然Eclipse工作台在关闭前时会进行持久化动作,那就通过在现在RCP程序的ApplicationWorkbenchWindowAdvisor类的方法:
在ret[0] = busyClose();前面打上断点,继续更近,最后进入到workbench的busyClose方法中:
tag1就是执行的条件getWorkbenchConfigurer().getSaveAndRestore(),通过跟踪代码可以看到其具体实现,在Workbench类中:
configurer.setSaveAndRestore(true);设置Workbench是否保存持久化操作,Eclipse中会通过XXXAdvisor(中文意思就是顾问)来设置这些配置项,管理着XXX的生命周期,
这里的XXX可以是Workbench,对应的是WorkbenchAdvisor,WorkbenchWindow,就对应WorkbenchWindowAdvisor--管理着IWorkbenchWindowConfigurer的设置。
对应RCP程序来说,如果想使程序具备持久化和恢复功能就需要在WorkbenchAdvisor的实现类ApplicationWorkbenchAdvisor中设置如下:
Tag2处的SafeRunner是一个封装了异常处理的类,会把异常单独通过一个方法供开发者重写,没有什么特殊点。
Tag3、tag4当然就是持久化的核心实现方法了,其中tag3会搜集并合并模型(从workbench到workbenchWindow到workbenchPage到各个view、editor、透视图等)最终到root模型中XMLMemento ;tag4当然是把模型保存到文件中,以便下次打开App时执行恢复操作。
下面来一点点分享,继续单步调试:
XMLMemento 实现了IMemento持久化模型接口,内部通过Dom的一套api对xml进行操作(关于xml的解析技术,在开发中也是经常用到的,有时间会对xml解析技术做一下归纳,这里先不过多的介绍)。XMLMemento 内部维护了两个字段:
private Document factory;//dom中的xml树形对象
private Element element;//doc中的root根元素
下面是createWriteRoot的实现,创建一个XMLMemento 的对象,作为整个持久化模型的根元素:
<workbench progressCount="34" version="2.0">
tag3保存了WorkbenchAdvisor中的持久化数据,默认在xml中形如:
<workbenchAdvisor/>
当然可以通过重写WorkbenchAdvisor的saveState方法,这样标签中会有相应的属性。
Tag4就是得到整个平台Workbench的WorkbenchWindow,是通过WindowManger的getWindows方法获得的。WindowManger内部维护着一个WorkbenchWindow的ArrayList。getWorkbenchWindows()方法就是获得list的一个copy。
最后的tag6就是保存editor区域的历史信息,默认标签<mruList/>,非关键内容,就不再说了,下面重点看一下关键实现tag5,同样是step into进去,核心类会比较复杂,不过没关系,一点点看:
<window height="704" width="1024" x="175" y="131">
<fastViewData fastViewLocation="1024"/>
接着,tag2保存了工具栏CoolBar、透视图bar等的状态信息。
然后tag3就开遍历WorkbenchWindow下的所有WorkbenchPage。(通常情况下也就一个
page)。
Tag4调用了WorkbenchPage.saveState()开始page页的保存,下面看一下该方法的关键实现部分:
1》通过EditorManager实现所有Editor区域信息的保存;(tag1)
2》通过ViewFactory实现所有View信息的保存;(tag2)
3》实现透视图相关状态的保存;(tag3)
代码分析到这里算告一段落,接下来就进入了具体每个View、Editor信息的保存动作。
上文件主要进行了信息保存的分析过程,其实信息的恢复过程也是类似,就是先找到保存的状态信息文件,然后以类似上面分析的层次关系,一步步完成恢复工作的。关于恢复的具体过程,可以在插件启动处(或直接在org.eclipse.ui.internal.Workbench.restoreState()这个方法上)打上断点一步步调试(不过前提的RCP需要设置可以恢复,上文已经说过),大概过程可以列一下:
先来看一下rcp的启动过程
RCP的启动方法Application.start(IApplicationContext context) 中:
创建Workben
在workbench执行eventloop(这是整个rcp的工作模式--事件监听模型)前,会先执行workbench的初始化工作init():
解析来就是dvisor.openWindows(),其中调用到了
于是,轰轰烈烈的恢复动作就开始了:
Tag1就是获得workbench.xml所在的路径,当然,如果定制的话这个路径就需要指定为自己自定义的文件路径了(比如和工程相关的)。
Tag2就是把xml中的信息转换成XMLMemento对象,具体解析方式就是dom的xml解析技术:
Tag3就是恢复的具体恢复实现方法,紧接着会调用doRestoreState方法,先获取Window对应的状态信息,创建WorkbenchWindow,并把状态信息restore进去
restoreState进行内容恢复:
Tag1就是通过EditorManager对所有的Editor区域的状态进行恢复;Tag2就是通过ViewFactory对View进行恢复;Tag3就是Perspective透视图的恢复功能。再往下就是具体的区域的恢复功能了。
文章到这里基本是对Eclipse的持久化和恢复功能的关键过程作了比较详细的阐述了,当然可以通过查看Eclipse源码了解更多的细节东西。
对Eclipse的持久化原理了解清楚以后,定制自己的恢复功能,比如当工程打开后加载状态文件,在工程关闭后保存状态文件等,就可以很容易实现了。当然在定制的过程中还是会遇到也许问题的,比如我们定制的过程中就遇到了诸如拖动编辑器时区域消失,加载窗户时区域闪烁和重启窗口后不能恢复到原来的状态等问题,不过在进一步研究一下Eclipse源码,这些bug或问题都能一个个找到合理的解决办法(源码真是好东西,多看Eclipse源码,呵呵)。
具体的View、Editor的恢复
定制完了恢复框架,接下来要做的就是具体的View和Editor内容的恢复了。比如ProjectView视图中要恢复Project Tree的展开状态、选中状态、滚动条的位置等信息;Editor区域打开文件的具体EditorPart的文件内容(EditorInput)等信息。
接下来分别举例说明:
1.首先,设置RCP允许恢复功能
这个上文提到过,就是在WorkbenchAdvisor中设置了configurer.setSaveAndRestore(true);表示允许恢复。
重写RCP中ApplicationWorkbenchAdvisor类的initialize()方法,设置setSaveAndRestore这个配置项为true。
下面说一下几种常用窗口的恢复功能:
2.View的恢复
以org.eclipse.cdt.internal.ui.cview.CView为例,进行说明(这是cdt的开源项目的代码实现):
(1)设置要恢复的变量名
(4)View UI状态的保存,重写saveState()方法:
3.Editor的恢复
例如要恢复一个ID为ID=“org.niub.test.Editor”的编辑器TestEditor
(1)扩展org.eclipse.ui.elementFactories,定义控件元素恢复工厂PersistentElementFactory
(2)实现元素恢复工厂PersistentElementFactory
(3)定义Editor的EditorInput,使得能实现Editor内容的恢复:
(4)指定把定制好的EditorInput对象,setInput到对于的Editor
1.先了解一下Eclipse本身的恢复功能:
经常用Eclipse做开发,我们知道,Eclipse的UI持久化及窗户恢复功能做的是很完善的,比如ProjectExplorer视图中工程的展开状态、Editor区域的文件的代码状态,各个视图的展现状态,Dialog,MenuBar,ToolBar等待,Eclipse都很好的恢复到了上次我们关闭前的状态。
在Eclipse关闭前他会启动UI持久化功能,把需要保持的各个控件的状态信息(IMemento对象)持久化到一个文件中(这个文件一般在Eclipse的安装目录的Workspace下:
..\.metadata\.plugins\org.eclipse.ui.workbench\workbench.xml).
恢复粒度:和整个IDE相关。
2.为什么要定制UI持久化框架
简单说就是Eclipse的本身持久化机制不能满足需要---不能满足需求中的对每个工程粒度的恢复。
需要做的就是为每一个IDE中的工程创建一份持久化文件及操作入口。
于是,先得弄清楚Eclipse的一整套状态恢复机制,找到其持久化和逆持久化的接口,然后展开定制工作。
3.debug方式跟踪Eclipse的UIPresent机制的内部实现
Eclipse什么实现的恢复功能呢,网上搜索资料当然好,但既然都是开源的东西,源码当然是做好的参考资料了。
浏览源码的最好方式就是:debug+断点--》一步步,代码的运行过程看的一清二楚。
(为了容易调试,可以创建一个RCP项目来模拟Eclipse进行调试,其实Eclipse本身也可以看做是一个复杂的RCP项目)
先从持久化入手(从恢复也一样),既然Eclipse工作台在关闭前时会进行持久化动作,那就通过在现在RCP程序的ApplicationWorkbenchWindowAdvisor类的方法:
@Override public boolean preWindowShellClose() { return super.preWindowShellClose(); }中设置断点(preWindowShellClose方法在RCP关闭前调用),调试状态下启动RCP程序,然后关闭该RCP,调试线程就挺在了刚才的断点处,然后F5(Step Into)进去看内部实现,就这样一步步的跟下去,跟到了WorkbenchWindow的close方法:
public boolean close() { final boolean[] ret = new boolean[1]; BusyIndicator.showWhile(null, new Runnable() { public void run() { ret[0] = busyClose(); } }); return ret[0]; }
在ret[0] = busyClose();前面打上断点,继续更近,最后进入到workbench的busyClose方法中:
private boolean busyClose(final boolean force) { ...... if (getWorkbenchConfigurer().getSaveAndRestore()) {// tag1 SafeRunner.run(new SafeRunnable() {//tag2 public void run() { XMLMemento mem = recordWorkbenchState();//tag3 // Save the IMemento to a file. saveMementoToFile(mem);//tag4 } public void handleException(Throwable e) { ....... } }); } ...... return true; }通过单步,结合方法的名称,很容判断出执行保存状态的代码部分,上面的代码片段就是Eclipse执行状态持久化的地方。代码后面有tag的注释字样,下面一一作一下解释:
tag1就是执行的条件getWorkbenchConfigurer().getSaveAndRestore(),通过跟踪代码可以看到其具体实现,在Workbench类中:
WorkbenchConfigurer getWorkbenchConfigurer() { if (workbenchConfigurer == null) { workbenchConfigurer = new WorkbenchConfigurer(); } return workbenchConfigurer; }WorkbenchConfigurer类是Workbench的一个配置管理类,可以设置Workbench的很多配置项的值,来满足不同用户的个性化需求,比如可以通过
configurer.setSaveAndRestore(true);设置Workbench是否保存持久化操作,Eclipse中会通过XXXAdvisor(中文意思就是顾问)来设置这些配置项,管理着XXX的生命周期,
这里的XXX可以是Workbench,对应的是WorkbenchAdvisor,WorkbenchWindow,就对应WorkbenchWindowAdvisor--管理着IWorkbenchWindowConfigurer的设置。
对应RCP程序来说,如果想使程序具备持久化和恢复功能就需要在WorkbenchAdvisor的实现类ApplicationWorkbenchAdvisor中设置如下:
@Override public void initialize(IWorkbenchConfigurer configurer) { super.initialize(configurer); configurer.setSaveAndRestore(true); }然后就有资格在tag1处,满足条件,进入持久化的动作。
Tag2处的SafeRunner是一个封装了异常处理的类,会把异常单独通过一个方法供开发者重写,没有什么特殊点。
Tag3、tag4当然就是持久化的核心实现方法了,其中tag3会搜集并合并模型(从workbench到workbenchWindow到workbenchPage到各个view、editor、透视图等)最终到root模型中XMLMemento ;tag4当然是把模型保存到文件中,以便下次打开App时执行恢复操作。
下面来一点点分享,继续单步调试:
private XMLMemento recordWorkbenchState() { XMLMemento memento = XMLMemento .createWriteRoot(IWorkbenchConstants.TAG_WORKBENCH); final IStatus status = saveState(memento); return memento; }
XMLMemento 实现了IMemento持久化模型接口,内部通过Dom的一套api对xml进行操作(关于xml的解析技术,在开发中也是经常用到的,有时间会对xml解析技术做一下归纳,这里先不过多的介绍)。XMLMemento 内部维护了两个字段:
private Document factory;//dom中的xml树形对象
private Element element;//doc中的root根元素
下面是createWriteRoot的实现,创建一个XMLMemento 的对象,作为整个持久化模型的根元素:
public static XMLMemento createWriteRoot(String type) throws DOMException{ Document document; try { document = DocumentBuilderFactory.newInstance() .newDocumentBuilder().newDocument(); Element element = document.createElement(type); document.appendChild(element); return new XMLMemento(document, element); } catch (ParserConfigurationException e) { // throw new Error(e); throw new Error(e.getMessage()); } }接着就是saveState(memento)对各个部分需要恢复的模型的合并:
/* * Saves the current state of the workbench so it can be restored later on */ private IStatus saveState(IMemento memento) { MultiStatus result = new MultiStatus(PlatformUI.PLUGIN_ID, IStatus.OK,WorkbenchMessages.Workbench_problemsSaving, null); // Save the version number. memento.putString(IWorkbenchConstants.TAG_VERSION, VERSION_STRING[1]);//tag1 // Save how many plug-ins were loaded while restoring the workbench if (progressCount != -1) {//tag2 memento.putInteger(IWorkbenchConstants.TAG_PROGRESS_COUNT, progressCount); } // Save the advisor state. IMemento advisorState = memento .createChild(IWorkbenchConstants.TAG_WORKBENCH_ADVISOR); result.add(getAdvisor().saveState(advisorState));//tag3 // Save the workbench windows. IWorkbenchWindow[] windows = getWorkbenchWindows(); for (int nX = 0; nX < windows.length; nX++) {//tag4 WorkbenchWindow window = (WorkbenchWindow) windows[nX]; IMemento childMem = memento .createChild(IWorkbenchConstants.TAG_WINDOW); result.merge(window.saveState(childMem));//tag5 } result.add(getEditorHistory().saveState( memento. createChild(IWorkbenchConstants.TAG_MRU_LIST)));//tag6 return result; }其中tag1、tag2中在xml文件中的显示形如:
<workbench progressCount="34" version="2.0">
tag3保存了WorkbenchAdvisor中的持久化数据,默认在xml中形如:
<workbenchAdvisor/>
当然可以通过重写WorkbenchAdvisor的saveState方法,这样标签中会有相应的属性。
Tag4就是得到整个平台Workbench的WorkbenchWindow,是通过WindowManger的getWindows方法获得的。WindowManger内部维护着一个WorkbenchWindow的ArrayList。getWorkbenchWindows()方法就是获得list的一个copy。
public IWorkbenchWindow[] getWorkbenchWindows() { if (windowManager == null) { return new IWorkbenchWindow[0]; } Window[] windows = windowManager.getWindows(); IWorkbenchWindow[] dwindows = new IWorkbenchWindow[windows.length]; System.arraycopy(windows, 0, dwindows, 0, windows.length); return dwindows; }然后遍历每一个WorkbenchWindow,开始执行其持久化操作,就是tag5。
最后的tag6就是保存editor区域的历史信息,默认标签<mruList/>,非关键内容,就不再说了,下面重点看一下关键实现tag5,同样是step into进去,核心类会比较复杂,不过没关系,一点点看:
public IStatus saveState(IMemento memento) { MultiStatus result = new MultiStatus(PlatformUI.PLUGIN_ID, IStatus.OK, WorkbenchMessages.WorkbenchWindow_problemsSavingWindow, null); //--------------tag1-begin--------- // Save the window's state and bounds. …… memento.putInteger(IWorkbenchConstants.TAG_X, normalBounds.x); memento.putInteger(IWorkbenchConstants.TAG_Y, normalBounds.y); memento.putInteger(IWorkbenchConstants.TAG_WIDTH, normalBounds.width); memento.putInteger(IWorkbenchConstants.TAG_HEIGHT, normalBounds.height); //--------------tag1-end--------- …… // save the width of the perspective bar IMemento persBarMem = memento .createChild(IWorkbenchConstants.TAG_PERSPECTIVE_BAR); if (perspectiveSwitcher != null) {//tag3 perspectiveSwitcher.saveState(persBarMem); } // / Save the order of the cool bar contribution items ICoolBarManager2 coolBarMgr = (ICoolBarManager2) getCoolBarManager2(); if (coolBarMgr != null) { …… } //--------------tag2-end--------- // Save each page. Iterator itr = pageList.iterator(); while (itr.hasNext()) {//tag3 WorkbenchPage page = (WorkbenchPage) itr.next(); // Save perspective. IMemento pageMem = memento .createChild(IWorkbenchConstants.TAG_PAGE); pageMem.putString(IWorkbenchConstants.TAG_LABEL, page.getLabel()); result.add(page.saveState(pageMem));//tag4 if (page == getActiveWorkbenchPage()) { pageMem.putString(IWorkbenchConstants.TAG_FOCUS, "true"); } …… } …… return result; }其中,tag1是和整个app窗体的大小、打开时在电脑屏幕的位置、fastView等信息等(在进行我们IDE定制时,在打开一个新的工程进行恢复时,IDE会在屏幕上移动,这样用户体验不好,于是就去掉了这个部分)。在xml中形如:
<window height="704" width="1024" x="175" y="131">
<fastViewData fastViewLocation="1024"/>
接着,tag2保存了工具栏CoolBar、透视图bar等的状态信息。
然后tag3就开遍历WorkbenchWindow下的所有WorkbenchPage。(通常情况下也就一个
page)。
Tag4调用了WorkbenchPage.saveState()开始page页的保存,下面看一下该方法的关键实现部分:
public IStatus saveState(IMemento memento) { // Save editor manager. IMemento childMem = memento .createChild(IWorkbenchConstants.TAG_EDITORS); result.merge(editorMgr.saveState(childMem));//tag1 // Save View childMem = memento.createChild(IWorkbenchConstants.TAG_VIEWS); result.merge(getViewFactory().saveState(childMem));//tag2 // Save each perspective in opened order Iterator itr = perspList.iterator(); while (itr.hasNext()) { Perspective persp = (Perspective) itr.next(); IMemento gChildMem = childMem .createChild(IWorkbenchConstants.TAG_PERSPECTIVE); result.merge(persp.saveState(gChildMem));//tag3 } …… return result; }从代码中一目了然,WorkbenchPage的保存操作,主要分为3个部分:
1》通过EditorManager实现所有Editor区域信息的保存;(tag1)
2》通过ViewFactory实现所有View信息的保存;(tag2)
3》实现透视图相关状态的保存;(tag3)
代码分析到这里算告一段落,接下来就进入了具体每个View、Editor信息的保存动作。
上文件主要进行了信息保存的分析过程,其实信息的恢复过程也是类似,就是先找到保存的状态信息文件,然后以类似上面分析的层次关系,一步步完成恢复工作的。关于恢复的具体过程,可以在插件启动处(或直接在org.eclipse.ui.internal.Workbench.restoreState()这个方法上)打上断点一步步调试(不过前提的RCP需要设置可以恢复,上文已经说过),大概过程可以列一下:
先来看一下rcp的启动过程
RCP的启动方法Application.start(IApplicationContext context) 中:
int returnCode = PlatformUI.createAndRunWorkbench(display, new ApplicationWorkbenchAdvisor());其中ApplicationWorkbenchAdvisor功能就是提供了Workbench的个性化配置和管理功能。
创建Workben
public static final int createAndRunWorkbench(final Display display, final WorkbenchAdvisor advisor) { final int[] returnCode = new int[1]; Realm.runWithDefault(SWTObservables.getRealm(display), new Runnable() { public void run() { …… // create the workbench instance Workbench workbench = new Workbench(display, advisor); // run the workbench event loop returnCode[0] = workbench.runUI(); } }); return returnCode[0]; }
在workbench执行eventloop(这是整个rcp的工作模式--事件监听模型)前,会先执行workbench的初始化工作init():
private boolean init() { ……//一系列的初始化工作 // create workbench window manager windowManager = new WindowManager(); …… // attempt to restore a previous workbench state try { U…… StartupThreading.runWithoutExceptions(new StartupRunnable() { public void runWithException() throws Throwable { advisor.preStartup(); if (isClosing() || !advisor.openWindows()) { bail[0] = true; } }}); …… } …… return true; }
解析来就是dvisor.openWindows(),其中调用到了
org.eclipse.ui.internal.WorkbenchConfigurer.restoreState()方法: public IStatus restoreState() { return ((Workbench) getWorkbench()).restoreState(); }
于是,轰轰烈烈的恢复动作就开始了:
IStatus restoreState() { // Read the workbench state file. final File stateFile = getWorkbenchStateFile();//tag1 …… SafeRunner.run(new SafeRunnable(x) { public void run() throws Exception { FileInputStream input = new FileInputStream(stateFile); BufferedReader reader = new BufferedReader( new InputStreamReader(input, "utf-8")); IMemento memento = XMLMemento.createReadRoot(reader);//tag2 …… // Restore the saved state final IStatus restoreResult = restoreState(memento);//tag3 reader.close(); …… } …… }); }
Tag1就是获得workbench.xml所在的路径,当然,如果定制的话这个路径就需要指定为自己自定义的文件路径了(比如和工程相关的)。
Tag2就是把xml中的信息转换成XMLMemento对象,具体解析方式就是dom的xml解析技术:
public static XMLMemento createReadRoot(Reader reader, String baseDir) throws WorkbenchException { …… try { DocumentBuilderFactory factory = DocumentBuilderFactory .newInstance(); DocumentBuilder parser = factory.newDocumentBuilder(); InputSource source = new InputSource(reader); if (baseDir != null) { source.setSystemId(baseDir); } …… Document document = parser.parse(source); NodeList list = document.getChildNodes(); for (int i = 0; i < list.getLength(); i++) { Node node = list.item(i); if (node instanceof Element) { return new XMLMemento(document, (Element) node); } } } catch (ParserConfigurationException e) { …… } …… }
Tag3就是恢复的具体恢复实现方法,紧接着会调用doRestoreState方法,先获取Window对应的状态信息,创建WorkbenchWindow,并把状态信息restore进去
private void doRestoreState(final IMemento memento, final MultiStatus status) { …… // Get the child windows. IMemento[] children = memento .getChildren(IWorkbenchConstants.TAG_WINDOW);// createdWindows = new WorkbenchWindow[children.length]; // Read the workbench windows. for (int i = 0; i < children.length; i++) { childMem = children[i]; final WorkbenchWindow [] newWindow = new WorkbenchWindow[1]; StartupThreading.runWithoutExceptions(new StartupRunnable() { public void runWithException() { newWindow[0] = newWorkbenchWindow(); newWindow[0].create(); }}); createdWindows[i] = newWindow[0]; windowManager.add(newWindow[0]); status.merge(newWindow[0].restoreState(childMem, null)); …… } }下面看一下WorkbenchWindow.restoreState()方法的实现,其主要实现就是遍历其下所有的WorkbenchPage相关的IMemento模型,然后创建WorkbenchPage并通过
restoreState进行内容恢复:
public IStatus restoreState(IMemento memento, IPerspectiveDescriptor activeDescriptor) { // 1.Restore the window advisor state. // 2.Restore actionbar advisor state. // 3.Read window's bounds and state. // 4.restore the width of the perspective bar // 5.Restore the cool bar order …… // 6.Recreate each page in the window. IWorkbenchPage newActivePage = null; IMemento[] pageArray = memento .getChildren(IWorkbenchConstants.TAG_PAGE); for (int i = 0; i < pageArray.length; i++) { final IMemento pageMem = pageArray[i]; …… // Open the perspective. final IAdaptable finalInput = input[0]; final WorkbenchPage [] newPage = new WorkbenchPage[1]; try { StartupThreading.runWithWorkbenchExceptions(new StartupRunnable(){ public void runWithException(){ newPage[0] = ((WorkbenchImplementation) Tweaklets .get(WorkbenchImplementation.KEY)).createWorkbenchPage(WorkbenchWindow.this, finalInput); }}); result.add(newPage[0].restoreState(pageMem, activeDescriptor)); …… } catch (WorkbenchException e) {} } …… return result; }然后到了WorkbenchPage.restoreState()方法实现:
public IStatus restoreState(IMemento memento, final IPerspectiveDescriptor activeDescriptor) { …… // Restore editor manager.//tag1 IMemento childMem = memento .getChild(IWorkbenchConstants.TAG_EDITORS); result.merge(getEditorManager().restoreState(childMem)); childMem = memento.getChild(IWorkbenchConstants.TAG_VIEWS);//tag2 result.merge(getViewFactory().restoreState(childMem)); …… // Restore perspectives.//tag3 final IMemento perspMems[] = childMem .getChildren(IWorkbenchConstants.TAG_PERSPECTIVE); final Perspective activePerspectiveArray [] = new Perspective[1]; for (int i = 0; i < perspMems.length; i++) { final IMemento current = perspMems[i]; persp.restoreState(current) …… } }
Tag1就是通过EditorManager对所有的Editor区域的状态进行恢复;Tag2就是通过ViewFactory对View进行恢复;Tag3就是Perspective透视图的恢复功能。再往下就是具体的区域的恢复功能了。
文章到这里基本是对Eclipse的持久化和恢复功能的关键过程作了比较详细的阐述了,当然可以通过查看Eclipse源码了解更多的细节东西。
对Eclipse的持久化原理了解清楚以后,定制自己的恢复功能,比如当工程打开后加载状态文件,在工程关闭后保存状态文件等,就可以很容易实现了。当然在定制的过程中还是会遇到也许问题的,比如我们定制的过程中就遇到了诸如拖动编辑器时区域消失,加载窗户时区域闪烁和重启窗口后不能恢复到原来的状态等问题,不过在进一步研究一下Eclipse源码,这些bug或问题都能一个个找到合理的解决办法(源码真是好东西,多看Eclipse源码,呵呵)。
具体的View、Editor的恢复
定制完了恢复框架,接下来要做的就是具体的View和Editor内容的恢复了。比如ProjectView视图中要恢复Project Tree的展开状态、选中状态、滚动条的位置等信息;Editor区域打开文件的具体EditorPart的文件内容(EditorInput)等信息。
接下来分别举例说明:
1.首先,设置RCP允许恢复功能
这个上文提到过,就是在WorkbenchAdvisor中设置了configurer.setSaveAndRestore(true);表示允许恢复。
重写RCP中ApplicationWorkbenchAdvisor类的initialize()方法,设置setSaveAndRestore这个配置项为true。
@Override public void initialize(IWorkbenchConfigurer configurer) { configurer.setSaveAndRestore(true); super.initialize(configurer); }
下面说一下几种常用窗口的恢复功能:
2.View的恢复
以org.eclipse.cdt.internal.ui.cview.CView为例,进行说明(这是cdt的开源项目的代码实现):
(1)设置要恢复的变量名
// Persistance tags. static final String TAG_SELECTION = "selection"; //$NON-NLS-1$ static final String TAG_EXPANDED = "expanded"; //$NON-NLS-1$ static final String TAG_ELEMENT = "element"; //$NON-NLS-1$ static final String TAG_PATH = "path"; //$NON-NLS-1$ static final String TAG_VERTICAL_POSITION = "verticalPosition"; //$NON-NLS-1$ static final String TAG_HORIZONTAL_POSITION = "horizontalPosition"; //$NON-NLS-1$ static final String TAG_WORKINGSET = "workingSet"; //$NON-NLS-1$(2)重写init()方法
private IMemento memento;//定义全局变量 @Override public void init(IViewSite site, IMemento memento) throws PartInitException { super.init(site, memento); this.memento = memento; }(3)View UI的恢复方法restoreState(),在View的createPartControl()中调用:
@Override public void createPartControl(Composite aParent) { ...... if (memento != null) { restoreState(memento); } memento = null; } void restoreState(IMemento memento) { CoreModel factory = CoreModel.getDefault(); getActionGroup().restoreFilterAndSorterState(memento); IMemento childMem = memento.getChild(TAG_EXPANDED); if (childMem != null) { ArrayList<ICElement> elements = new ArrayList<ICElement>(); IMemento[] elementMem = childMem.getChildren(TAG_ELEMENT); for (IMemento element2 : elementMem) { String p = element2.getString(TAG_PATH); if (p != null) { IPath path = new Path(p); ICElement element = factory.create(path); if (element != null) { elements.add(element); } } } viewer.setExpandedElements(elements.toArray()); } childMem = memento.getChild(TAG_SELECTION); if (childMem != null) { ArrayList<ICElement> list = new ArrayList<ICElement>(); IMemento[] elementMem = childMem.getChildren(TAG_ELEMENT); for (IMemento element2 : elementMem) { String p = element2.getString(TAG_PATH); if (p != null) { IPath path = new Path(p); ICElement element = factory.create(path); if (element != null) { list.add(element); } } } viewer.setSelection(new StructuredSelection(list)); } Tree tree = viewer.getTree(); //save vertical position ScrollBar bar = tree.getVerticalBar(); if (bar != null) { try { String posStr = memento.getString(TAG_VERTICAL_POSITION); int position; position = new Integer(posStr).intValue(); bar.setSelection(position); position = new Integer(posStr).intValue(); bar.setSelection(position); } catch (NumberFormatException e) { } } bar = tree.getHorizontalBar(); if (bar != null) { try { String posStr = memento.getString(TAG_HORIZONTAL_POSITION); int position; position = new Integer(posStr).intValue(); bar.setSelection(position); } catch (NumberFormatException e) { } }
(4)View UI状态的保存,重写saveState()方法:
@Override public void saveState(IMemento memento) { if (viewer == null) { if (this.memento != null) { //Keep the old state; memento.putMemento(this.memento); } return; } //save expanded elements Tree tree = viewer.getTree(); Object expandedElements[] = viewer.getExpandedElements(); if (expandedElements.length > 0) { IMemento expandedMem = memento.createChild(TAG_EXPANDED); for (Object expandedElement : expandedElements) { Object o = expandedElement; // Do not save expanded binary files are libraries. if (o instanceof IParent && !(o instanceof IArchiveContainer || o instanceof IBinaryContainer || o instanceof IBinary || o instanceof IArchive)) { IMemento elementMem = expandedMem.createChild(TAG_ELEMENT); ICElement e = (ICElement) o; IResource res = e.getResource(); if (res != null && res.getLocation() != null) { elementMem.putString(TAG_PATH, res.getLocation().toOSString()); } } } } //save selection Object elements[] = ((IStructuredSelection) viewer.getSelection()).toArray(); if (elements.length > 0) { IMemento selectionMem = memento.createChild(TAG_SELECTION); for (Object element : elements) { if (element instanceof ICElement) { ICElement e = (ICElement) element; IResource r = e.getResource(); if (r != null && r.getLocation() != null) { IMemento elementMem = selectionMem.createChild(TAG_ELEMENT); elementMem.putString(TAG_PATH, r.getLocation().toString()); } } } } //save vertical position ScrollBar bar = tree.getVerticalBar(); int position = bar != null ? bar.getSelection() : 0; memento.putString(TAG_VERTICAL_POSITION, String.valueOf(position)); //save horizontal position bar = tree.getHorizontalBar(); position = bar != null ? bar.getSelection() : 0; memento.putString(TAG_HORIZONTAL_POSITION, String.valueOf(position)); getActionGroup().saveFilterAndSorterState(memento); //Save the working set away if (workingSetFilter.getWorkingSet() != null) { String wsname = workingSetFilter.getWorkingSet().getName(); if (wsname != null) { memento.putString(TAG_WORKINGSET, wsname); } }if (viewer == null) { if (this.memento != null) { //Keep the old state; memento.putMemento(this.memento); } return; } //save expanded elements Tree tree = viewer.getTree(); Object expandedElements[] = viewer.getExpandedElements(); if (expandedElements.length > 0) { IMemento expandedMem = memento.createChild(TAG_EXPANDED); for (Object expandedElement : expandedElements) { Object o = expandedElement; // Do not save expanded binary files are libraries. if (o instanceof IParent && !(o instanceof IArchiveContainer || o instanceof IBinaryContainer || o instanceof IBinary || o instanceof IArchive)) { IMemento elementMem = expandedMem.createChild(TAG_ELEMENT); ICElement e = (ICElement) o; IResource res = e.getResource(); if (res != null && res.getLocation() != null) { elementMem.putString(TAG_PATH, res.getLocation().toOSString()); } } } } //save selection Object elements[] = ((IStructuredSelection) viewer.getSelection()).toArray(); if (elements.length > 0) { IMemento selectionMem = memento.createChild(TAG_SELECTION); for (Object element : elements) { if (element instanceof ICElement) { ICElement e = (ICElement) element; IResource r = e.getResource(); if (r != null && r.getLocation() != null) { IMemento elementMem = selectionMem.createChild(TAG_ELEMENT); elementMem.putString(TAG_PATH, r.getLocation().toString()); } } } } //save vertical position ScrollBar bar = tree.getVerticalBar(); int position = bar != null ? bar.getSelection() : 0; memento.putString(TAG_VERTICAL_POSITION, String.valueOf(position)); //save horizontal position bar = tree.getHorizontalBar(); position = bar != null ? bar.getSelection() : 0; memento.putString(TAG_HORIZONTAL_POSITION, String.valueOf(position)); getActionGroup().saveFilterAndSorterState(memento); //Save the working set away if (workingSetFilter.getWorkingSet() != null) { String wsname = workingSetFilter.getWorkingSet().getName(); if (wsname != null) { memento.putString(TAG_WORKINGSET, wsname); } } }业务逻辑很简单,就不做解释了。
3.Editor的恢复
例如要恢复一个ID为ID=“org.niub.test.Editor”的编辑器TestEditor
(1)扩展org.eclipse.ui.elementFactories,定义控件元素恢复工厂PersistentElementFactory
<extension point="org.eclipse.ui.elementFactories"> <factory class="org.niub.test.PersistentElementFactory" id="org.niub.test.PersistentElementFactory"> </factory> </extension>
(2)实现元素恢复工厂PersistentElementFactory
public class PersistentElementFactory implements IElementFactory { public static final String ID = "org.niub.test.PersistentElementFactory"; @Override public IAdaptable createElement(IMemento memento) { PersistentEditorInput editorInput = null; //当然,这个标签的名字可以任意指定,只要save和restore保存一致就行 IMemento editorMem = memento.getChild(ID); if (editorMem != null) { int tagIndex = editorMem.getInteger(PersistentEditorInput.TAG_ACTIVE_TAG_INDEX); int v_position = editorMem.getInteger(PersistentEditorInput.TAG_VERTICAL_POSITION); int h_position = editorMem.getInteger(PersistentEditorInput.TAG_HORIZONTAL_POSITION); //……可以添加对Editor的操作 //返回一个通过 editorInput = new PersistentEditorInput(ID); } // 返回一个editorInput 对象 return editorInput; } }
(3)定义Editor的EditorInput,使得能实现Editor内容的恢复:
public class PersistentEditorInput extends EditorInputAdapter implements IPersistableElement { public static final String TAG_ACTIVE_TAG_INDEX = "subTagIndex"; static final String TAG_VERTICAL_POSITION = "verticalPosition"; static final String TAG_HORIZONTAL_POSITION = "horizontalPosition"; private String editorId; public PersistentEditorInput(String editorId) { super(""); this.editorId = editorId; } // ------Persistent-------------------------------------------------- @Override public void saveState(IMemento memento) { IMemento editorMem = memento.createChild(editorId); if (ID.equals(editorId)) { TestEditor editor = EditorUtil.getTestEditor(); if (editor != null) { editorMem.putInteger(TAG_ACTIVE_TAG_INDEX, editor .getxxxSelect()); editorMem.putInteger(TAG_VERTICAL_POSITION, editor.getxxxScollerPositoin(SWT.V_SCROLL)); …… } } else { //可以添加其他类型的editor状态 } } @Override public String getFactoryId() {//指定恢复元素的Factory return PersistentElementFactory.ID; } @Override public IPersistableElement getPersistable() { return this; } @Override public boolean exists() { // Editor Persistent需要返回true return true; } }
(4)指定把定制好的EditorInput对象,setInput到对于的Editor
IEditorPart configEditor = EditorUtil.openEditor(new PersistentEditorInput(TestEditor.ID), TestEditor.ID); ......
发表评论
-
再说SWT中的滚动面板ScrolledComposite实现
2013-06-19 15:43 2339记得以前写过一篇关于滚动面板的文章 SWT中 Scrolle ... -
OSGi参考资料
2013-04-18 01:11 688基于 OSGi 的面向服务的组件编程 探索 OSGi 框架的组 ... -
CDT(编辑、调试)参考资料
2013-04-17 02:15 1119CDT编辑器 --------- 构建基于 CDT 的编辑器, ... -
Workspace Resource框架专题(3)处理工作空间资源更改事件
2013-04-17 01:44 13893 处理工作空间资源更改事件 工作空间API允许工具对它 ... -
Workspace Resource框架专题(2)workspace 框架API
2013-04-17 01:27 14962 工作空间API 本 ... -
Workspace Resource框架专题(1)Resource的概念
2013-04-17 01:12 13991 Resource的概念 如 ... -
如何恢复断点及Marker
2013-03-05 00:41 0如何恢复断点及Marker -
深入Workbench框架
2013-03-01 02:10 1749深入Workbench框架(结合UIPersistent) 1 ... -
Eclipse插件开发中的Action
2013-02-24 23:10 2015插入点用来定义菜单出 ... -
Eclipse开发中编辑器(Editors)和视图(View)总结
2013-02-24 22:58 29201.视图(Views) 视图( ... -
SWT/JFace专题 --- 对话框向导(Dialogs Wizards)
2013-02-24 22:42 2253对话框向导(Dialogs Wizar ... -
SWT/JFace专题 --- JFace
2013-02-24 22:37 1659JFace JFace是基于SWT的一套图形工具包,它没有为 ... -
SWT/JFace专题 --- SWT中Display和多线程
2013-02-24 15:25 3249Display 1.Display 的创建 一个SWT程序 ... -
SWT/JFace专题 --- SWT API 结构
2013-02-23 18:31 1085SWT API 结构 1.布局类(l ... -
Eclipse启动过程(源码级剖析)
2013-02-20 03:24 3426双击eclipse安装目录下的eclipse.exe运行后,会 ... -
SWT/JFace专题 --- SWT结构浅析
2013-02-23 17:02 1027SWT技术是一套基于Java的 ... -
Eclipse平台体系结构
2013-02-21 23:56 19191.Eclipse平台体系结构 1 ... -
RCP平台架构
2013-02-23 14:11 1495RCP 富客户端通常是指具有独立用户界面的客户端程序。富客户 ... -
Ant构建脚本相关
2013-02-18 01:26 0Ant构建脚本相关 -
CDT源码架构研究
2013-02-18 01:24 0CDT源码架构研究
相关推荐
标题中的"org.eclipse.paho.ui.app-1.0.0.zip"表明这是一个Eclipse Paho项目的用户界面应用程序的版本号为1.0.0的压缩包。Eclipse Paho是一个开源项目,它提供了多种编程语言的库和服务,用于在物联网(IoT)环境中...
5. **会话持久化**:在断开连接后,能够恢复先前的订阅和消息状态,保证连接的连续性。 6. **日志记录**:提供详细的日志输出,帮助开发者追踪和诊断问题。 7. **可视化界面**:用户友好的图形界面,使得测试过程...
安装这些插件后,Eclipse不仅具备了黑色主题,还增强了其文档支持、插件开发、Java持久化、JavaScript开发、用户界面、窗口系统、项目设置以及用户引导等功能,从而提升了开发者的整体使用体验。
在VE中,EMF作为基础,支持UI设计时的数据绑定和持久化。 3. **GEF (Graphical Editing Framework)**:GEF是Eclipse的一个图形编辑框架,用于构建图形化的编辑器。它提供了底层的绘图和交互机制,让开发者可以专注...
9. **持久化存储**:讲解如何使用Eclipse的模型对象持久化服务,保存和恢复用户数据和应用状态。 10. **团队协作与版本控制**:介绍如何将Eclipse RCP项目整合到版本控制系统如Git或SVN中,实现团队开发和代码管理...
6. **Remote System Explorer (RSE)**:org.eclipse.rse.ui_3.3.300.201610252046.jar提供了远程系统探索功能,允许开发者在Eclipse中管理和操作远程文件系统、数据库和应用程序服务器,极大地扩展了Eclipse的适用...
4. **JPT(Java Persistence Tools)**:`org.eclipse.jpt.*`文件与Java持久化工具有关,如`org.eclipse.jpt.core_2.2.1.v200908270220.jar`和`org.eclipse.jpt.ui_2.2.2.v200911250220.jar`,它们可能提供了对Java ...
5. **首选项存储**:属性页中的设置通常需要持久化,Eclipse提供了`IPreferenceStore`接口,用于读写用户的偏好设置。你可以通过`Platform.getPreferencesService()`获取服务,并根据需要使用`readBoolean`、`...
8. `org.eclipse.persistence.core_1.1.3.v20091002-r5404.jar`:Eclipse Persistence服务的核心库,用于支持Java持久化,如JPA(Java Persistence API)。 9. `org.eclipse.swt.win32.win32.x86_3.5.2.v3557f.jar`...
标题中的"org.eclipse.paho.ui.app-1.0.2-win32.x86_64.zip.zip"表明这是一个Eclipse Paho MQTT客户端用户界面应用的软件包,版本号为1.0.2,适用于Windows操作系统,且是64位架构。这个软件包的核心功能是提供MQTT...
这些插件的安装对于那些希望在Eclipse 3.4.2环境中使用图形化编辑功能的开发者来说至关重要。`vefor3.4`很可能是一个包含所有这些插件的压缩包,用户需要解压并按照Eclipse的插件安装步骤进行操作,通常包括将插件...
3. 数据持久化:理解如何将数据持久化,即使应用关闭也能恢复状态。 六、安全与优化 1. 加密通信:为了保护用户隐私,QQ客户端的通信过程应采用加密技术,如SSL/TLS。 2. 性能调优:通过合理的设计和代码优化,...
2. **第8章.rar** - 可能涉及到的是RCP应用中的数据持久化和数据库集成,包括使用JDBC、ORM工具(如Hibernate)与数据库交互,以及如何设计数据模型和事务管理。这些知识对于构建需要处理大量数据的RCP应用至关重要...
16. **EMF(Eclipse Modeling Framework)**:用于创建和操作模型的框架,常用于生成代码和持久化数据。 以上就是关于"Eclipse工具5"的一些核心知识点,具体的版本可能有额外的新功能和改进,但上述内容涵盖了...
【标题】"项目积累eclipse"是一个以Eclipse为核心开发工具的软件项目,它整合了前端的EasyUI、后端的Dubbo框架以及MyBatis作为持久层技术。这个项目旨在通过实际操作来积累编程经验,提升开发者在业务逻辑处理和代码...
`ibator`(IntelliJ IDEA的iBATIS Generator的Eclipse版本)是一款强大的数据库代码自动生成工具,它能够帮助开发者快速地生成Java持久层代码,包括实体类、Mapper接口及XML配置文件等,大大提高了开发效率。...
在本文中,我们将深入探讨如何基于Eclipse平台开发一款手机通讯录应用,主要涉及Java编程语言和Android开发环境。Eclipse作为一个强大的集成开发环境(IDE),对于Java开发者来说,是构建各种应用程序的理想选择,...
7. **持久化与首选项**:Eclipse RCP提供了内置的持久化支持,用于保存用户的配置和首选项。 8. **调试与测试**:Eclipse RCP的开发环境中集成了强大的调试工具,同时支持单元测试和集成测试。 9. **国际化与本地...
通过深入研究这些文件,开发者不仅可以了解Android应用开发的基本流程,还可以学习到如何设计和实现复杂的用户界面,以及如何处理网络请求、数据持久化、多线程等问题。此外,对于想要提升Android编程技能,尤其是对...