<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    月蝕傳說

    浮躁讓人失去理智
    posts - 25, comments - 101, trackbacks - 0, articles - 0
      BlogJava :: 首頁 ::  :: 聯(lián)系 :: 聚合  :: 管理

    用Eclipse RCP & ECF 實現(xiàn) Google Talk客戶端

    Posted on 2006-10-09 19:07 Dart 閱讀(1996) 評論(2)  編輯  收藏 所屬分類: ECF
    大家用過Google Talk嗎?它是Google推出的一個IM,通訊協(xié)議是我們熟悉的Jabber協(xié)議。我通過這篇文章給大家簡單介紹一下如何利用ECF實現(xiàn)一個Google Talk客戶端。源代碼下載:http://www.tkk7.com/Files/reloadcn/Chat.rar

    1.準(zhǔn)備工作
    先下載ECF:
    www.eclipse.org/ecf


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

    當(dāng)然,我們想要登陸Google的服務(wù)器必須擁有一個GoogleMail帳號,由于現(xiàn)在GoogleMail帳號不是隨便申請的,需要GoogleMail用戶推薦才能申請,但也能通過一些網(wǎng)站進(jìn)入GoogleMail申請頁面,大家可以上網(wǎng)搜索一下,我在這里就不多說了。


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

    2.建立一個RCP Mail? Example

    我們先選擇創(chuàng)建Plugin Project,取名為“Chat”,當(dāng)?shù)较驅(qū)ы摰牡诙摰臅r候,注意在“Would you to create a rich client platform”選項選擇“yes”,這樣確保你創(chuàng)建的是一個RCP工程,見下圖:



    當(dāng)?shù)阶詈笠豁摰臅r候,選擇Mail Template:


    完成向?qū)Ш笪覀儗玫揭粋€簡單的RCP工程。

    3.登陸的代碼

    1)連接前工作
    ECF是一個基于Eclipse的通訊平臺,它其中一部分實現(xiàn)了Jabber協(xié)議。ECF有一個ClientContainer概念,其實就相當(dāng)于一個維護(hù)客戶端的對象,它具有連接、斷開連接服務(wù)的方法,并且能夠添加一些通訊中的事件監(jiān)聽器。所以,我們創(chuàng)建Google Talk客戶端首先就要擁有這么一個對象,而且它在整個程序生命周期中是唯一的。
    讓我們修改一下ChatPlugin中的代碼:
    首先,我們在這個類里增加一個私有變量clientContainer,并且給他加上Getter、Setter方法:

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

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

    OK,試想一下,當(dāng)我們在登陸Google服務(wù)器的時候才會去使用這個clientContainer去連接服務(wù)器,而且我們登陸的用戶信息是需要保存下來的,以供后面的代碼訪問,所以這個clientContainer的生成方式應(yīng)該是Lazy的,并且我們還需要建立一個我們登陸帳戶的變量:

    ????private?ID?userID;

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

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

    ECF中針對用戶的信息是用ID來表示的,它是一個接口,ECF已經(jīng)實現(xiàn)了一個XMPPID,正好是我們Jabber帳戶需要的。

    clientContainer有一個connect方法去登陸服務(wù)器,而且在連接后不再具有其他什么動作。讀者會問:那什么時候通知我們連接成功呢?并且用戶在服務(wù)器端的好友怎么獲得呢?

    clientContainer只負(fù)責(zé)連接,上述的那些事情都屬于在連接服務(wù)器過程中或者連接后,服務(wù)器反饋給客戶端的信息,這些信息需要我們給clientContainer設(shè)置監(jiān)聽器去捕獲。

    其中有一個監(jiān)聽器名為ISharedObjectContainerListener,這個監(jiān)聽器能夠捕獲一些在連接過程和斷開連接過程中的事件,比如SharedObjectConnectedEvent (連接成功事件)、SharedObjectDisconnectedEvent (斷開連接成功事件),如果我們需要在客戶端連接上服務(wù)器后做點什么,那這個監(jiān)聽器是必須的。

    clientContainer.addListener(
    ??????????????????
    new
    ?ISharedObjectContainerListener()?{
    ???????????????????
    public?void
    ?handleEvent(IContainerEvent?evt)?
    ???????????????????????
    if?(evt?instanceof
    ?ISharedObjectContainerConnectedEvent)?{
    ?????????????????????????????????// 連接服務(wù)器成功后做點什么呢?
    ??????????????????????? }
    ???????????????????????
    if?(evt?instanceof
    ?ISharedObjectContainerDisconnectedEvent)?{
    ???????????????????????????????? // 斷開服務(wù)器成功后做點什么呢?
    ????????????????????????
    }
    ???????????????????}

    ???????????????????},?
    null);


    2)開始連接服務(wù)器

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

    這個方法需要有兩個參數(shù):用戶的ID、連接上下文

    用戶ID我們剛才已經(jīng)說過了,它是ECF提出的一個概念,我們可以通過IDFactory生成它:

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

    大家發(fā)現(xiàn)了嗎,上面代碼中的makeID方法需要兩個參數(shù),一個參數(shù)我們可以從clientContainer獲得,它是連接名字空間,我的理解是某種協(xié)議。第二個是用戶名,這個參數(shù)在我們這里是Google Talk的帳號,也就是GMail帳號,但是目前我們還沒有辦法從外部獲得,這我會在下面的內(nèi)容中提到,到時候就可以將這個程序串起來,大家現(xiàn)在可以把它看作已經(jīng)具備某些值。

    好,我們已經(jīng)有了ID,現(xiàn)在看看什么如何創(chuàng)建上下文。連接上下文其實很簡單,我們可以這樣理解:就是在我們連接的時候,clientContainer會向客戶端所取一些相關(guān)的信息,比如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);
    ?????????????????????????????????}
    ????????????????????????????????????????????????????????}
    ????????????????????????????????????????????????????}

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

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

    到目前為止,我們已經(jīng)完成了連接這個環(huán)節(jié),我們將這些代碼都封裝到ChatPlugin的login方法中,到時候通過外部的操作好調(diào)用。



    4.開始登陸

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


    這個類需要有幾個屬性:用戶帳號、用戶密碼、對話框返回值。

    當(dāng)我們點擊了Login后,對話框關(guān)閉,并將文本中的值賦給帳號和密碼這兩個屬性,返回值設(shè)為SWT.OK;如果是Cancel的話那我們就直接關(guān)閉對話框,返回值設(shè)置為SWT.CANCEL。

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

    ?public?void?run()?{
    ????????
    if(ChatPlugin.getDefault().getClientContainer()?!=?null
    )?{
    ????????????MessageDialog.openInformation(window.getShell(),
    "Info","已經(jīng)登陸了,請先注銷再重新登陸"
    );
    ????????????
    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();
    ????????}
    ????}

    代碼邏輯很清楚。當(dāng)我們點擊這個按鈕的時候,就會彈出登陸的對話框,然后我們輸入信息后就可以正常登陸了。

    注意后面的代碼,我們將ChatPlugin中的用戶名和密碼先設(shè)置好后再調(diào)用登陸方法。如果登陸失敗的話會在ChatPlugin的login方法中捕獲到連接失敗的異常。

    5.獲得我的好友們

    怎么去獲得我的好友呢?

    剛才已經(jīng)在前面提到了一點:clientContainer只負(fù)責(zé)去連接,而那些網(wǎng)絡(luò)的事件需要我們?nèi)ピ黾颖O(jiān)聽器捕獲。獲得好友也是一樣的,我簡單說一下。

    clientContainer可以通過getAdapter去獲得一個IPresenceContainer類型對象,這個對象可以增加監(jiān)聽獲得好友信息的監(jiān)聽器,不僅如此,它還可以獲得消息發(fā)送對象和消息的監(jiān)聽對象,這我會在后面介紹。
    我們要想獲得好友信息,就應(yīng)該通過clientContainer獲得IPresenceContainer對象,然后給它增加一個能夠獲得好友事件的監(jiān)聽器。

    問題在這里,我們應(yīng)該在什么時候去獲得這個對象呢?那這個監(jiān)聽器接口是不是需要一些現(xiàn)有類去實現(xiàn)呢?

    先說第一個問題:我們什么時候去獲得這個對象,并為它增加監(jiān)聽器

    一般情況下,我們在登陸成功以前的時候是不會去捕獲我們的好友列表的消息的,而且也捕獲不到,服務(wù)器在沒有驗證我們的客戶端時,是不會發(fā)過來的,所以我們需要在登陸成功后去獲得這個對象,并為它增加一個監(jiān)聽去。而這個對象也是需要作為一個私有變量存放起來,供其他類去訪問。所以我們需要在第3節(jié)中提到了監(jiān)聽登陸成功的方法中寫這段代碼,由于篇幅問題,我不在這里給出代碼片段,讀者可以去看源代碼。

    看看第二個問題:誰需要實現(xiàn)這個監(jiān)聽器?

    我們常見的IM中,都是有一個列表控件保存我們當(dāng)前的用戶信息的,所以我們在獲得好友列表后就需要往某些Viewer中增加一些內(nèi)容,來表示這是我們的好友列表。

    我在這個客戶端中,采用了一個View作為顯示好友列表的控件,該View名為SimpleView,這個View具有一個TableViewer。該類的具體生成方法我不在多說,大家可以看看源代碼,我只說一下這個View如何去實現(xiàn)監(jiān)聽獲得好友信息的事件的。

    我們讓它實現(xiàn)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();
    ????????????????}
    ????????????}
    ????????});
    ????}

    這個方法就是截獲獲得好友信息的接口函數(shù),entry表示的是從服務(wù)器獲得的一些和客戶端好友有關(guān)的信息,每當(dāng)獲得一個,判斷一下這個好友是否都在雙方的好友名單中,如果不是那就不要增加它;反之,我們就會把這個entry放到一個名位roseters的List對象中,然后刷新viewer。這里的viewer是剛才我們提到的TableViewer,做過SWT/JFace的讀者一定知道,這個類需要我們?nèi)樗砑觾蓚€接口實現(xiàn),一個是ContentProvider接口,一個是LabelProvier接口,這兩個接口代碼讀者可以看看我的源碼,這里就不寫了。如果您對SWT/JFace不熟悉的話也沒關(guān)系,這方面的資料很多。
    看看我們登陸后獲得好友列表是什么樣的:



    6.監(jiān)聽消息

    有了剛才增加好友的經(jīng)驗,我們現(xiàn)在就很容易解決這個問題。
    同樣,監(jiān)聽消息還是由IPresenceContainer對象增加的監(jiān)聽器來截獲的。
    而我讓我們工程中一個名為View的類實現(xiàn)了這個監(jiān)聽器,并且實現(xiàn)這個接口的方法如下:

    ????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對象的時候傳進(jìn)來的,讓我們看看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);
    ????????????????}
    ????????????}
    ????????};

    可以看出來,當(dāng)我們雙擊某個好友的時候,就會從entry中得到他的ID,然后生成一個View,并將ID給View,所以View的chaterID就時這么來的。

    接著上面的解釋:
    showText變量其實是一個StyleText對象,他專門負(fù)責(zé)顯示聊天信息,而下面那一長段代碼讀者大可不必理會,那是為了使一個好友對應(yīng)一個View而做的一些工作,大概了解即可,也可以去看源代碼獲得更多的信息。

    7.發(fā)送消息

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

    messageText.addKeyListener(new?KeyListener(){

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

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

    不難看出這段代碼的意思:當(dāng)遇到輸入字符為回車的時候,就調(diào)用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去發(fā)送消息的,發(fā)送消息的函數(shù)第一個參數(shù)是發(fā)送者的ID,第二個是接收者的ID(chaterID已經(jīng)在上面講過了獲取的來源),最后一個是發(fā)送的消息,中間兩個參數(shù)一個消息類型和標(biāo)題,他們可以為空。

    8.結(jié)束語
    通過我們上面所說的如何去登陸、獲得好友列表、接收消息和發(fā)送消息,我們已經(jīng)能夠簡單地創(chuàng)建一個Google Talk的客戶端了,但是還有很多功能沒有實現(xiàn),比如添加好友、監(jiān)聽好友狀態(tài)改變等等,這些都需要大家去增加。就講到這里,我們下次再見。




    評論

    # re: 用Eclipse RCP & ECF 實現(xiàn) Google Talk客戶端   回復(fù)  更多評論   

    2007-01-24 10:00 by alan
    你好,我現(xiàn)在正在用ECF來開發(fā)一個項目,其中有一些地方要向你請教,如果方便,請加我的QQ:43833911,謝謝!

    # re: 用Eclipse RCP & ECF 實現(xiàn) Google Talk客戶端   回復(fù)  更多評論   

    2007-01-25 17:24 by alon xiong
    你寫的這篇文章中ECF是什么版本的,怎么0.9.6通不過?

    只有注冊用戶登錄后才能發(fā)表評論。


    網(wǎng)站導(dǎo)航:
     
    主站蜘蛛池模板: 在线免费观看一区二区三区| 国产精品亚洲片在线va| 国产在线a不卡免费视频| 亚洲第一网站免费视频| 国产精品免费视频观看拍拍| 免费人成网上在线观看| 亚洲欧洲日产国产综合网| 亚洲精品视频在线看| 日韩免费三级电影| 97无码免费人妻超级碰碰夜夜| 中文字幕乱码一区二区免费| 九九免费精品视频在这里| 18禁亚洲深夜福利人口| 亚洲日本中文字幕天天更新| 亚洲国产精品白丝在线观看| 99久久精品国产亚洲| 亚洲国产一区国产亚洲 | 亚洲成在人线av| 亚洲自偷自偷图片| 亚洲一级黄色视频| AV在线亚洲男人的天堂| 亚洲AV无码乱码精品国产| 免费一看一级毛片人| 免费人成在线观看视频播放| 国产青草视频免费观看97| 老司机永久免费网站在线观看| 成年在线观看免费人视频草莓| 国产成人午夜精品免费视频| 蜜臀98精品国产免费观看| 猫咪免费人成网站在线观看| 18禁男女爽爽爽午夜网站免费| 最近免费mv在线电影| 日本成年免费网站| 成人五级毛片免费播放| 国内自产拍自a免费毛片| 免费毛片在线播放| 免费h成人黄漫画嘿咻破解版| 亚洲AV无码成H人在线观看| 亚洲av日韩av欧v在线天堂| 久久99亚洲综合精品首页| 亚洲成a人片在线观看日本|