用Eclipse RCP & ECF 實現 Google Talk用戶端

來源:互聯網
上載者:User
大家用過Google Talk嗎?它是Google推出的一個IM,通訊協議是我們熟悉的Jabber協議。我通過這篇文章給大家簡單介紹一下如何利用ECF實現一個Google Talk用戶端。原始碼下載:http://www.blogjava.net/Files/reloadcn/Chat.rar

1.準備工作
先下載ECF:
www.eclipse.org/ecf

為了能夠測試我們這個用戶端是否能正常運行,我們還需要下載一個Goolge Talk用戶端:www.google.com/talk

當然,我們想要登陸Google的伺服器必須擁有一個GoogleMail帳號,由於現在GoogleMail帳號不是隨便申請的,需要GoogleMail使用者推薦才能申請,但也能通過一些網站進入GoogleMail申請頁面,大家可以留言,我可以邀請。

我們要建立一個Google Talk的用戶端,需要瞭解一些ECF的知識。大家可以去Eclipse主站獲得更多的資訊。

2.建立一個RCP Mail Example

我們先選擇建立Plugin Project,取名為“Chat”,當到嚮導頁的第二頁的時候,注意在“Would you to create a rich client platform”選項選擇“yes”,這樣確保你建立的是一個RCP工程,見:

當到最後一頁的時候,選擇Mail Template:

完成嚮導後我們將會得到一個簡單的RCP工程。

3.登陸的代碼

1)串連前工作
ECF是一個基於Eclipse的通訊平台,它其中一部分實現了Jabber協議。ECF有一個ClientContainer概念,其實就相當於一個維護用戶端的對象,它具有串連、中斷連線服務的方法,並且能夠添加一些通訊中的事件監聽器。所以,我們建立Google Talk用戶端首先就要擁有這麼一個對象,而且它在整個程式生命週期中是唯一的。
讓我們修改一下ChatPlugin中的代碼:
首先,我們在這個類裡增加一個私人變數clientContainer,並且給他加上Getter、Setter方法:

XMPPClientSOContainer clientContainer;
? public XMPPClientSOContainer getClientContainer() {
? ?   return clientContainer;
? }

? public void setClientContainer(XMPPClientSOContainer clientContainer) {
? ?   this.clientContainer = clientContainer;
? }

OK,試想一下,當我們在登陸Google伺服器的時候才會去使用這個clientContainer去串連伺服器,而且我們登陸的使用者資訊是需要儲存下來的,以供後面的代碼訪問,所以這個clientContainer的產生方式應該是Lazy的,並且我們還需要建立一個我們登陸帳戶的變數:

? private ID userID;

? public ID getUserID() {
? ?   return userID;
? }

? public void setUserID(ID userID) {
? ?   this.userID = userID;
? }

ECF中針對使用者的資訊是用ID來表示的,它是一個介面,ECF已經實現了一個XMPPID,正好是我們Jabber帳戶需要的。

clientContainer有一個connect方法去登陸伺服器,而且在串連後不再具有其他什麼動作。讀者會問:那什麼時候通知我們串連成功呢?並且使用者在伺服器端的好友怎麼獲得呢?

clientContainer只負責串連,上述的那些事情都屬於在串連伺服器過程中或者串連後,伺服器反饋給用戶端的資訊,這些資訊需要我們給clientContainer設定監聽器去捕獲。

其中有一個監聽器名為ISharedObjectContainerListener,這個監聽器能夠捕獲一些在串連過程和中斷連線過程中的事件,比如SharedObjectConnectedEvent (串連成功事件)、SharedObjectDisconnectedEvent (中斷連線成功事件),如果我們需要在用戶端串連上伺服器後做點什麼,那這個監聽器是必須的。

clientContainer.addListener(
? ? ? ? ? ? new ISharedObjectContainerListener() {
? ? ? ? ? ? public void handleEvent(IContainerEvent evt)
? ? ? ? ? ? ?   if (evt instanceof ISharedObjectContainerConnectedEvent) {
? ? ? ? ? ? ? ? ? ? ? // 串連伺服器成功後做點什麼呢?
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ?   if (evt instanceof ISharedObjectContainerDisconnectedEvent) {
? ? ? ? ? ? ? ? ? ? ? // 斷開伺服器成功後做點什麼呢?
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }

? ? ? ? ? ? }, null);

2)開始串連伺服器

我們看看clientContainer有一個connect方法。

這個方法需要有兩個參數:使用者的ID、串連上下文

使用者ID我們剛才已經說過了,它是ECF提出的一個概念,我們可以通過IDFactory產生它:

userID = IDFactory.getDefault().makeID(
? ? ? ? ? ? ? ? ? ? ? ? ? clientContainer.getConnectNamespace(),
? ? ? ? ? ? ? ? ? ? ? ? ? getUserName());

大家發現了嗎,上面代碼中的makeID方法需要兩個參數,一個參數我們可以從clientContainer獲得,它是串連名字空間,我的理解是某種協議。第二個是使用者名稱,這個參數在我們這裡是Google Talk的帳號,也就是GMail帳號,但是目前我們還沒有辦法從外部獲得,這我會在下面的內容中提到,到時候就可以將這個程式串起來,大家現在可以把它看作已經具備某些值。

好,我們已經有了ID,現在看看什麼如何建立上下文。串連上下文其實很簡單,我們可以這樣理解:就是在我們串連的時候,clientContainer會向用戶端所取一些相關的資訊,比如nikename,password,這樣理解起來就不麻煩了,而且在我們的這個Google Talk用戶端中,它也只會向我們索取password和username,來看看我們代碼就更清楚了:

clientContainer.connect(userID, new IConnectContext() {

? ? ?   public CallbackHandler getCallbackHandler() {
? ? ? ? ? return new CallbackHandler() { ?
? ? ? ? ? ? ? public void handle( Callback[] callbacks)throws IOException,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?   UnsupportedCallbackException {
? ? ? ? ? ? ? ? ?   if (callbacks == null) return;
? ? ? ? ? ? ? ? ? ? for (int i = 0; i < callbacks.length; i++) {
? ? ? ? ? ? ? ? ? ? ? ? if (callbacks【i】 instanceof NameCallback) {
? ? ? ? ? ? ? ? ? ? ? ?   NameCallback ncb = (NameCallback) callbacks【i】
? ? ? ? ? ? ? ? ? ? ? ?   ncb.setName(getUserName());
? ? ? ? ? ? ? ? ? ? ? ?   } else
? ? ? ? ? ? ? ? ? ? if (callbacks【i】 instanceof ObjectCallback) {
? ? ? ? ? ? ? ? ? ? ? ObjectCallback ocb = (ObjectCallback) callbacks【i】
? ? ? ? ? ? ? ? ? ? ? ocb.setObject(password);
? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?   }
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? }

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? };
? ? ? ? ? ? ? ? ? ? ? ? ? ?   }

? ? ? ? ? ? ? ? ? ? ? ? ? });

到目前為止,我們已經完成了串連這個環節,我們將這些代碼都封裝到ChatPlugin的login方法中,到時候通過外部的操作好調用。

4.開始登陸

我們利用SWT Dialog建立一個簡單的登陸對話方塊:

這個類需要有幾個屬性:使用者帳號、使用者密碼、對話方塊傳回值。

當我們點擊了Login後,對話方塊關閉,並將文本中的值賦給帳號和密碼這兩個屬性,傳回值設為SWT.OK;如果是Cancel的話那我們就直接關閉對話方塊,傳回值設定為SWT.CANCEL。

我們再到Mail RCP中提供的MessagePopupAction類中修改它的run方法:

public void run() {
? ?   if(ChatPlugin.getDefault().getClientContainer() != null) {
? ? ? ? MessageDialog.openInformation(window.getShell(),"Info","已經登陸了,請先登出再重新登陸");
? ? ? ? return;
? ?   }
? ?   LoginDialog dialog = new LoginDialog(window.getShell(),SWT.NONE);
? ?   dialog.open();
? ?   if(dialog.getDialogResult() == SWT.OK){
? ? ? ? ChatPlugin.getDefault().setPassword(dialog.getPassword());
? ? ? ? ChatPlugin.getDefault().setUserName(dialog.getUser());
? ? ? ? ChatPlugin.getDefault().login();
? ?   }
? }

代碼邏輯很清楚。當我們點擊這個按鈕的時候,就會彈出登陸的對話方塊,然後我們輸入資訊後就可以正常登陸了。

注意後面的代碼,我們將ChatPlugin中的使用者名稱和密碼先設定好後再調用登陸方法。如果登陸失敗的話會在ChatPlugin的login方法中捕獲到串連失敗的異常。

5.獲得我的好友們

怎麼去獲得我的好友呢?

剛才已經在前面提到了一點:clientContainer只負責去串連,而那些網路的事件需要我們去增加監聽器捕獲。獲得好友也是一樣的,我簡單說一下。

clientContainer可以通過getAdapter去獲得一個IPresenceContainer類型對象,這個對象可以增加監聽獲得好友資訊的監聽器,不僅如此,它還可以獲得訊息發送對象和訊息的監聽對象,這我會在後面介紹。
我們要想獲得好友資訊,就應該通過clientContainer獲得IPresenceContainer對象,然後給它增加一個能夠獲得好友事件的監聽器。

問題在這裡,我們應該在什麼時候去獲得這個對象呢?那這個監聽器介面是不是需要一些現有類去實現呢?

先說第一個問題:我們什麼時候去獲得這個對象,並為它增加監聽器

一般情況下,我們在登陸成功以前的時候是不會去捕獲我們的好友名單的訊息的,而且也捕獲不到,伺服器在沒有驗證我們的用戶端時,是不會發過來的,所以我們需要在登陸成功後去獲得這個對象,並為它增加一個監聽去。而這個對象也是需要作為一個私人變數存放起來,供其他類去訪問。所以我們需要在第3節中提到了監聽登陸成功的方法中寫這段代碼,由於篇幅問題,我不在這裡給出程式碼片段,讀者可以去看原始碼。

看看第二個問題:誰需要實現這個監聽器?

我們常見的IM中,都是有一個清單控制項儲存我們當前的使用者資訊的,所以我們在獲得好友名單後就需要往某些Viewer中增加一些內容,來表示這是我們的好友名單。

我在這個用戶端中,採用了一個View作為顯示好友名單的控制項,該View名為SimpleView,這個View具有一個TableViewer。該類的具體產生方法我不在多說,大家可以看看原始碼,我只說一下這個View如何去實現監聽獲得好友資訊的事件的。

我們讓它實現IPresenceListener介面,並修改handleSetRosterEntry方法:

public void handleSetRosterEntry(IRosterEntry entry) {
? ?   final IRosterEntry e1 = entry;
? ?   Display.getDefault().asyncExec(new Runnable() {
? ? ? ? public void run() {
? ? ? ? ? if(e1.getInterestType() ==InterestType.BOTH){
? ? ? ? ? roseters.add(e1);
? ? ? ? ? if(viewer.getInput() != roseters) viewer.setInput(roseters);
? ? ? ? ? viewer.refresh();
? ? ? ? ? }
? ? ? ? }
? ?   });
? }

這個方法就是截獲獲得好友資訊的介面函數,entry表示的是從伺服器獲得的一些和用戶端好友有關的資訊,每當獲得一個,判斷一下這個好友是否都在雙方的好友名單中,如果不是那就不要增加它;反之,我們就會把這個entry放到一個名位roseters的List對象中,然後重新整理viewer。這裡的viewer是剛才我們提到的TableViewer,做過SWT/JFace的讀者一定知道,這個類需要我們去為它添加兩個介面實現,一個是ContentProvider介面,一個是LabelProvier介面,這兩個介面代碼讀者可以看看我的源碼,這裡就不寫了。如果您對SWT/JFace不熟悉的話也沒關係,這方面的資料很多。
看看我們登陸後獲得好友名單是什麼樣的:

6.監聽訊息

有了剛才增加好友的經驗,我們現在就很容易解決這個問題。
同樣,監聽訊息還是由IPresenceContainer對象增加的監聽器來截獲的。
而我讓我們工程中一個名為View的類實現了這個監聽器,並且實現這個介面的方法如下:

public void handleMessage(ID fromID, ID toID, Type type, String subject, String messageBody) {
? ?   final ID id = fromID;
? ?   if(type == Type.CHAT){
? ?   final String message = messageBody;
? ?   Display.getDefault().asyncExec(new Runnable(){
? ? ? ? public void run(){
? ? ? ? ? try {
? ? ? ? ? ?   if(id.toURI().compareTo(chaterID.toURI()) ==0){
? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? String s = chaterID.toURI().getUserInfo().toString();
? ? ? ? ? ? ? ? s += " say: " + message +"/n";
? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? showText.append(s);
? ? ? ? ? ? ? ? View.this.getSite().getWorkbenchWindow()
? ? ? ? ? ? ? ? .getWorkbench().getActiveWorkbenchWindow()
? ? ? ? ? ? ? ? .getActivePage().activate(
? ? ? ? ? ? (IViewPart)ChatPlugin.getDefault().getMessageDialogForID(chaterID));
? ? ? ? ? ?   }
? ? ? ? ? } catch (URISyntaxException e) {
? ? ? ? ? ?   // TODO Auto-generated catch block
? ? ? ? ? ?   e.printStackTrace();
? ? ? ? ? }
? ? ? ? }
? ?   });}
? ?  
? }

可能讀者這會看上面的代碼會一頭霧水。我解釋一下:
變數chaterID是一個ID類型的,它其實是從剛才我們好友名單中,雙擊某一項時產生這個View對象的時候傳進來的,讓我們看看SimpleView 中的雙擊action的代碼:

doubleClickAction = new Action() {
? ? ? ? public void run() {
? ? ? ? ? ISelection selection = viewer.getSelection();
? ? ? ? ? IRosterEntry entry = (IRosterEntry) ((StructuredSelection) selection)
? ? ? ? ? ? ? ? .getFirstElement();
? ? ? ? ? View chatView = (View) ChatPlugin.getDefault()
? ? ? ? ? ? ? ? .getMessageDialogForID(entry.getUserID());
? ? ? ? ? if (chatView != null) {
? ? ? ? ? ?   SampleView.this.getSite().getWorkbenchWindow()
? ? ? ? ? ? ? ? ? .getWorkbench().getActiveWorkbenchWindow()
? ? ? ? ? ? ? ? ? .getActivePage().activate(chatView);
? ? ? ? ? }
? ? ? ? }
? ?   };

可以看出來,當我們雙擊某個好友的時候,就會從entry中得到他的ID,然後產生一個View,並將ID給View,所以View的chaterID就時這麼來的。

接著上面的解釋:
showText變數其實是一個StyleText對象,他專門負責顯示聊天資訊,而下面那一長段代碼讀者大可不必理會,那是為了使一個好友對應一個View而做的一些工作,大概瞭解即可,也可以去看原始碼獲得更多的資訊。

7.發送訊息

讓我們看看View類中的一段代碼:

messageText.addKeyListener(new KeyListener(){

? ? ? ? public void keyPressed(KeyEvent e) {
? ? ? ? ?
? ? ? ? }

? ? ? ? public void keyReleased(KeyEvent e) {
? ? ? ? ? if(e.character == '/r'){
? ? ? ? ? ?   sendMessage(messageText.getText());
? ? ? ? ? ?   messageText.setText("");
? ? ? ? ? }
? ? ? ? }
? ? ? ?
? ?   });

不難看出這段代碼的意思:當遇到輸入字元為斷行符號的時候,就調用sendMessage方法:

public void sendMessage(String message) {
? ?   if(this.getChaterID() == null) return;
? ?   String s = "你說:";
? ?   s+= message;
? ?  
? ?   ChatPlugin.getDefault().getPresenceContainer().getMessageSender()
? ? ? ? ? .sendMessage(ChatPlugin.getDefault().getUserID(),chaterID, null, null, message);
? ?  
? ?   showText.append(s + "/n");
? }

sendMessage方法是從ChatPlugin中獲得IPresenceContainer的messagesender去發送訊息的,發送訊息的函數第一個參數是寄件者的ID,第二個是接收者的ID(chaterID已經在上面講過了擷取的來源),最後一個是發送的訊息,中間兩個參數一個訊息類型和標題,他們可以為空白。

8.結束語
通過我們上面所說的如何去登陸、獲得好友名單、接收訊息和發送訊息,我們已經能夠簡單地建立一個Google Talk的用戶端了,但是還有很多功能沒有實現,比如添加好友、監聽好友狀態改變等等,這些都需要大家去增加。就講到這裡,我們下次再見。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.