Eclipse 4綜述


Eclipse 4簡介

Eclipse SDK 4.x基於E4孵化器項目,是新一代構建基於Eclipse的工具和富客戶端桌面應用的平台。它使得開發和組裝基於Eclipse平台的應用和工具要更加容易。第一個版本(4.0)發布於2010年7月28日,4.1發布於2011年6月22日,2012年將發布Eclipse 4.2。Eclipse 3.8將和4.2同時發布,同時3.x也將停止更新。

Eclipse 4包含:

  • 基於模型的用戶界面和用於程序樣式的基於CSS的聲明機制。這使得設計和自定義應用程序界面變得更加容易,也給UI布局帶來更大的靈活性,可以使UI看起來與IDE完全不同。
  • 新的面向服務的編程模型,可以更容易地使用Eclipse平台提供的應用程序服務。
  • 支持依賴注入。
  • 提供了一個兼容層,已有的Eclipse 3.x程序也可以利用Eclipse 4應用程序平台的新功能。

架構概述

Eclipse 4應用程序平台與Eclipse 3.x應用程序平台非常類似。如全部JDT和PDE、大部分Platform都和Eclipse 3.x完全相同。E4AP與Eclipse 3.x平台的不同之處在於Workbench的實現(如org.eclipse.ui.workbench.plugin),以及這個新實現所依賴的技術。在發布之前,這些技術(模型化的UI、依賴注入、基於服務的編程模型、基於CSS的樣式)稱為“e4”,而現在,我們叫它Eclipse 4應用程序平台(E4AP)。在E4AP上端,4.0 Workbench提供了一個3.x Workbench APIs的實現,稱為兼容層,為已有的Eclipse 3.x應用提供向后兼容。

E4AP架構

模型化UI

Eclipse 4應用程序的布局現在完全支持模型。它與Web頁的DOM類似,描述用戶界面的布局和結構,盡管還包含其他對用戶不可見的元素(如命令和handler)。

模型化UI為自定義應用程序外觀提供非常靈活的方式。應用程序的結構比3.x的透視圖工廠和擴展更容易理解,因為每個元素的容器模型都設計得更好。

模型本身使用EMF創建和維護的,並使用EMF風格的模式來創建和添加元素。對模型的改變將立即反應在運行的應用程序中。

模型元素

Eclipse 4中的模型是一組接口,都以M作為前置,並公開了很多getter、setter方法。Eclipse 4的模型繼承了上一代Eclipse應用程序平台的最佳實踐。模型化的UI描述了窗體、透視圖、stacks or tiles和part,也吸收了Eclipse 3.4中的命令/處理程序/綁定這個模型。

E4AP提供了MApplicationElementMUILable等抽象元素,以及MWindowMPerspectiveMPartMMenu等具體元素。詳細內容可以參考這里

CSS樣式

E4AP的一個主要改進就是重新思考如何處理應用程序的主題和樣式。它可以對控件、窗體、對話框應用樣式。這一開始可能會感到很陌生。不過UI的層次結構與HTML類似。如SWT窗體或對話框包含一個根容器Shell,它又包含一些CompositeGroup元素,每個元素又可以包含CTabFolderTextTreeTable等。

CSS映射

使用CSS選擇器可以通過type#id.class這樣的方式來指定元素。從E4AP到SWT的映射如下:

  • type對應Java組件類(如ButtonComposite等)。
  • 元素可以包含很多類。E4AP公開了模型化UI元素的接口類型(如MPartMTrimmedWindow)及其標簽(通過類的特性(attribute))。類的特性也可以訪問SWT組件的數據值。
  • id對應模型化元素的elementId。

這里有CSS屬性與SWT控件方法的映射表。

應用樣式

在CSS文件中,我們使用相關SWT控件的標示符,如下面的CSS文件:

Label {
    font: Verdana 8px;
    color: black;
}

Composite Label {
    color: black;
}


Text {
    font: Verdana 8px;
}

Composite Text {
    background-color: white;
    color: black;
}

SashForm {
    background-color: #c1d5ef;
}


.MTrimBar {
    background-color: #e3efff #c1d5ef;
    color: white;
    font: Verdana 8px;
}

Shell {
    background-color: #e3efff #c1d5ef 60
}

要讓你的程序使用CSS文件,可以有兩種方式:

  • 對產品指定applicationCSS文件。
  • 使用主題管理器

如果程序樣式固定,就使用第一種方式。打開RCP項目的plugin.xml文件,選擇extensioni選項卡,向org.eclipse.core.runtime.products擴展點添加applicationCSS屬性。該屬性的值是指向CSS文件的URI,格式約定為platform:/plugin/BundleSymbolicName/path/file這種格式。例如:

platform:/plugin/com.example.e4.rcp.todo/css/default.css

設置applicationCSS

這樣,我們的程序在一開始就將應用該樣式。

第二種方法要更靈活一些。我們定義一個對於org.eclipse.e4.ui.css.swt.theme擴展點(定義ID和對CSS文件的指針)的擴展。然后為產品定義cssTheme屬性。主題管理器允許我們在運行時選擇樣式,和注冊新主題。

我們創建org.eclipse.e4.ui.css.swt.theme擴展點的兩個擴展,如下圖

創建一個css/red.css文件:

CTabItem, ToolBar, Button, CBanner, CoolBar {
    font-size: 9;
    background-color: red;
}

對項目添加cssTemplate參數:

創建下面這個handler將選擇紅色樣式:

import javax.inject.Named;

import org.eclipse.e4.core.di.annotations.Execute;
import org.eclipse.e4.ui.css.swt.theme.IThemeEngine;
import org.eclipse.e4.ui.workbench.IWorkbench;


public class ThemeSwitchHandler {
    @Execute
    public void switchTheme(IThemeEngine engine) {
        engine.setTheme("de.vogella.e4.todo.redtheme", true);
    }
}

在應用程序中添加一個調用上面handler的菜單。運行程序,就可以通過菜單選擇紅色主題:

此外,我們還可以指定某個控件的標簽,並在CSS文件中定義這些標簽。我們可以用下面的代碼來設置標簽:

Label label = new Label(parenet, SWT.NONE);
label.setData("org.eclipse.e4.ui.css.id","MyCSSTagForLabel");

CSS文件可以這樣定義該標簽的樣式:

#MyCSSTagForLabel{
    color:#blue;
}

依賴注入

Eclipse平台經過10年的發展,仍然存在以下問題:

  1. 代碼需要頻繁使用全局單例訪問器(如PlatformPlatformUI)或請求較深的依賴鏈(如獲取IStatusLineManager)。單例服務對RAP和Riena這種應用服務器來說,問題多多。
  2. 單例與提供程序的消費者緊密耦合,並且禁止重用。
  3. 不夠動態。
  4. ……

E4AP使用依賴注入來解決這些問題。客戶端代碼不需要知道如何訪問服務,只需要描述所需的服務,而由平台負責配置適當的服務。它提供了與JSR 330兼容的基於注解的依賴注入框架,與Spring類似。注入器定義在多個插件中:org.eclipse.e4.core.diorg.eclipse.e4.core.di.extensionsorg.eclipse.e4.ui.di

在Eclipse 3.x中,視圖需要通過PlatformUI單例和part site的狀態行管理器來訪問Eclipse幫助系統:

class MyView extends ViewPart {
    public void createPartControl(Composite parent) {
        Button button = ...;
        PlatformUI.getWorkbench().getHelpSystem().setHelp(button, "com.example.button.help.id");

        getViewSite().getActionBars().getStatusLineManager().setMessage("Configuring system...");
    }
}

而在E4AP中,part是POJO,應用程序服務是直接注入進來的:

class MyView {
    @Inject
    public void create(Composite parent, IWorkbenchHelpSystem help) {
        Button button = ...;
        help.setHelp(button, "com.example.button.help.id");

        slm.setMessage("Configuring system...");
    }
}

DI的好處有:

  • 客戶端可以編寫POJO和所需的服務列表。
  • 更利於測試。

而DI也有一些缺點:

  • 服務發現:無法使用代碼自動完成功能來找到可用的服務。
  • 調試加載失敗的注入項時會很困難。

與依賴注入相關的注釋,詳見這里

上下文

E4AP使用上下文IEclipseContext接口)向應用程序提供公共服務。普通代碼不需要使用或了解上下文。應該在Java類中使用@Inject注解來接收必要的服務,不應該直接使用IEclipseContext

Eclipse 3.x存在以下問題,可以由IEclipseContext和依賴注入來解決:

  1. 頻繁使用全局單例訪問器(如PlatformPlatformUI等等)
  2. 生產者與消費者之間的耦合過於緊密,同時很難重用
  3. 對上下文變化的動態響應不是增量的
  4. IEvaluationContext包含全局狀態(可根據上下文的改變而變化)
  5. 當前解決方案不是多線程的(計算發生在UI線程)
  6. 尺寸變化
    • 當前解決方案不會因為服務的生命周期短而減小尺寸
    • 當前解決方案會因為服務多而增大尺寸
  7. 包含太多相似的並行樹(控件樹、服務位置樹等)
  8. 不跟蹤服務的消費者
  9. 客戶端代碼需要了解Eclipse代碼庫的內部
  10. 不支持由運行時的其他服務組成的服務查找

E4AP的上下文存儲了可用的服務,並提供了OSGi服務查找。由於不太可能向Eclipse上下文請求可用的服務,並且不建議直接調用上下文中的方法,所以了解能夠注入哪些內容是十分重要的。

模型為例,它們都是MContext的實例,包含在自身的上下文中。例如,MWindowMPart都是MContext,所以可以這樣查詢模型對象的上下文:

// window == mwindow
MWindow window = (MWindow) mwindow.getContext().get(MWindow.class.getName());
// part == mpart
MWindow part = (MPart) mpart.getContext().get(MPart.class.getName());

這些模型對象都存在於上下文中,所以可以直接將它們注入到客戶端代碼中:

public class AccountsPart {
    @Inject
    private MPart part;

    @Inject
    private MWindow window;

    void setDirty(boolean dirty) {
        part.setDirty(dirty);
    }
}

為了鼓勵重用,你應該注入所需的最小公分母。例如,如果只關注讓part變dirty,只需要:

public class AccountsPart {
    @Inject
    private MDirtyable dirtyable;

    void setDirty(boolean dirty) {
        dirtyable.setDirty(dirty);
    }
}

這樣其他非MPartMDirtyable實現也可以復用你的代碼。這導致了一個非常有趣的現象,即一個模型接口的上層接口也會添加到上下文中。例如:

public class AccountsPart {
    @Inject
    private MDirtyable dirtyable;

    @Inject
    private MUILabel label;

    @Inject
    private MContext context;

    @Inject
    private MPart part;
}

所有的字段都是相同的MPart實例。

關於上下文的詳細內容,請參考這里

事件模型

Eclipse 4使用了一個發布/訂閱事件模型的全局事件總線(global event bus)。《E4中的事件處理》描述了其原理。全局事件總線實現在OSGi事件引起之上,可使用org.eclipse.e4.core.services.events.IEventBroker訪問。

IEventBroker提供了一些方法,可以訂閱、取消訂閱總線上的事件,以及向總線發布事件。

獲取IEventBroker

可以通過EclipseContext來獲取IEventBroker的一個實例:

…
private IEclipseContext eclipseContext;
…
IEventBroker eventBroker = eclipseContext.get(IEventBroker.class);

或通過依賴注入:

@Inject
IEventBroker eventBroker;

發布事件

向全局事件總線發布事件十分簡單,只需調用一下兩個方法之一:

IEvent  Broker.post(String topic, Object data) // synchronous delivery

IEventBroker.send(String topic, Object data) // asynchronous delivery

例如:

...
foo.Bar payload = getPayload();
boolean wasDispatchedSuccessfully = eventBroker.send(TOPIC_STRING, payload);

如果sendpost命令的payload是普通Java對象,將把它作為包含IEventBroker.DATA鍵的屬性附加到OSGi事件上。如果payloadDictionaryMap,所有的值都將作為包含相應鍵的屬性進行添加。

響應事件

聲明和相應事件包含兩種方法:依賴注入和通過IEventBroker訂閱。

Eclipse推薦在任何時候都使用依賴注入來注冊和相應事件。它的代碼更少,更容易閱讀和維護,包含較少的匿名內部類。(但實際上E4代碼庫並沒有使用這種方法來訂閱事件。)

@Inject @Optional
void closeHandler(@UIEventTopic(''TOPIC_STRING'') foo.Bar payload) {
    // Useful work that has access to payload.  The instance of foo.Bar that the event poster placed on the global event bus with the topic ''TOPIC_STRING''
}

應該將事件處理方法定義為私有的,這樣可以更清晰,也能避免直接調用。依賴注入機制可以注入私有字段和方法。(目前定義成私有方法會有bug,可以定義為包級私有。)

如果你的類沒有使用依賴注入,則只能用IEventBroker來訂閱:

IEventBroker eventBroker;
…
void addSubscribers() {

    eventBroker.subscribe(TOPIC_STRING, closeHandler);
    …
}

void removeSubscribers() {
    eventBroker.unsubscribe(closeHandler);
    …
}
…
private org.osgi.service.event.EventHandler closeHandler = new EventHandler() {
    public void handleEvent(Event event) {

    // Useful work that has access
    foo.Bar payload = (foo.Bar) event.getProperty(IEventBroker.DATA);
}

參考資料


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM