基于openfire+smack開發(fā)Android即時(shí)聊天應(yīng)用[四]-單人聊天、群聊崭歧、發(fā)送接收文件等

這篇文章主要介紹如何實(shí)現(xiàn)點(diǎn)對(duì)點(diǎn)單人聊天隅很、多人的群聊、以及如何給對(duì)方發(fā)送文件率碾,如何發(fā)送圖片消息和語音消息等功能叔营。

1.單人聊天

1.首先創(chuàng)建聊天對(duì)象

/**
  * 創(chuàng)建聊天窗口
  * @param jid   好友的JID
  * @return
  */
 public Chat createChat(String jid) {
    if(isConnected()) {
        ChatManager chatManager = ChatManager.getInstanceFor(connection);
        return chatManager.createChat(jid);
    }
    throw new NullPointerException("服務(wù)器連接失敗,請(qǐng)先連接服務(wù)器");
 }

創(chuàng)建聊天對(duì)象時(shí)所宰,參數(shù)JID記得傳聊天JID(解釋請(qǐng)參考我的系列文章之基于openfire+smack開發(fā)Android即時(shí)聊天應(yīng)用[三]-賬號(hào)信息绒尊、添加好友、JID理解等)給好友發(fā)送文本消息

chat.sendMessage(message);

2.獲取聊天對(duì)象管理器

/**
  * 獲取聊天對(duì)象管理器
  * @return
  */
 public ChatManager getChatManager() {
    if(isConnected()) {
        ChatManager chatManager = ChatManager.getInstanceFor(connection);
        return chatManager;
    }
    throw new NullPointerException("服務(wù)器連接失敗仔粥,請(qǐng)先連接服務(wù)器");
 }

3.接收文本消息

//創(chuàng)建聊天對(duì)象管理器監(jiān)聽
private ChatManagerListener chatManagerListener = new ChatManagerListener() {
    @Override
    public void chatCreated(Chat chat, boolean createdLocally) {
        chat.addMessageListener(new ChatMessageListener() {
        @Override
        public void processMessage(Chat chat, Message message) {
            //接收到消息Message之后進(jìn)行消息展示處理婴谱,這個(gè)地方可以處理所有人的消息
        }
    });
    }
};
//設(shè)置聊天對(duì)象管理器處理監(jiān)聽
getChatManager().addChatListener(chatManagerListener);

上述代碼會(huì)在你創(chuàng)建聊天對(duì)象時(shí)對(duì)該聊天對(duì)象設(shè)置消息處理監(jiān)聽,當(dāng)接收到消息之后躯泰,會(huì)自動(dòng)調(diào)用processMessage方法進(jìn)行處理谭羔,我們可以在該方法中對(duì)接收到的消息進(jìn)行展示或其他處理,所有好友發(fā)送過來的消息都會(huì)通過該方法處理麦向。所以該監(jiān)聽最好在登陸之后進(jìn)行設(shè)置瘟裸,同時(shí)在斷開連接或是注銷時(shí)移除該監(jiān)聽。

2.群聊

1.創(chuàng)建群聊聊天室

/**
    * 創(chuàng)建群聊聊天室
    * @param roomName       聊天室名字
    * @param nickName       創(chuàng)建者在聊天室中的昵稱
    * @param password       聊天室密碼
    * @return
    */
   public MultiUserChat createChatRoom(String roomName, String nickName, String password) {
    if(!isConnected()) {
    throw new NullPointerException("服務(wù)器連接失敗诵竭,請(qǐng)先連接服務(wù)器");
    }
    MultiUserChat muc = null;  
try {  
    // 創(chuàng)建一個(gè)MultiUserChat  
    muc = MultiUserChatManager.getInstanceFor(connection).getMultiUserChat(roomName + "@conference." + connection.getServiceName());
    // 創(chuàng)建聊天室  
    boolean isCreated = muc.createOrJoin(nickName);
    if(isCreated) {
        // 獲得聊天室的配置表單  
        Form form = muc.getConfigurationForm();  
        // 根據(jù)原始表單創(chuàng)建一個(gè)要提交的新表單话告。  
        Form submitForm = form.createAnswerForm();
        // 向要提交的表單添加默認(rèn)答復(fù)  
        List fields = form.getFields();
        for(int i = 0; fields != null && i < fields.size(); i++) {
            if(FormField.Type.hidden != fields.get(i).getType() &&
                                          fields.get(i).getVariable() != null) {  
            // 設(shè)置默認(rèn)值作為答復(fù)  
            submitForm.setDefaultAnswer(fields.get(i).getVariable());  
            }  
        }
        // 設(shè)置聊天室的新?lián)碛姓? 
        List owners = new ArrayList();  
        owners.add(connection.getUser());// 用戶JID  
        submitForm.setAnswer("muc#roomconfig_roomowners", owners);  
        // 設(shè)置聊天室是持久聊天室,即將要被保存下來  
        submitForm.setAnswer("muc#roomconfig_persistentroom", true);  
        // 房間僅對(duì)成員開放  
        submitForm.setAnswer("muc#roomconfig_membersonly", false);  
        // 允許占有者邀請(qǐng)其他人  
        submitForm.setAnswer("muc#roomconfig_allowinvites", true);  
        if(password != null && password.length() != 0) {  
        // 進(jìn)入是否需要密碼  
        submitForm.setAnswer("muc#roomconfig_passwordprotectedroom",  true);  
        // 設(shè)置進(jìn)入密碼  
        submitForm.setAnswer("muc#roomconfig_roomsecret", password);  
     }  
     // 能夠發(fā)現(xiàn)占有者真實(shí) JID 的角色  
     // submitForm.setAnswer("muc#roomconfig_whois", "anyone");  
    // 登錄房間對(duì)話  
    submitForm.setAnswer("muc#roomconfig_enablelogging", true);  
    // 僅允許注冊(cè)的昵稱登錄  
    submitForm.setAnswer("x-muc#roomconfig_reservednick", true);  
    // 允許使用者修改昵稱  
    submitForm.setAnswer("x-muc#roomconfig_canchangenick", false);  
    // 允許用戶注冊(cè)房間  
    submitForm.setAnswer("x-muc#roomconfig_registration", false);  
    // 發(fā)送已完成的表單(有默認(rèn)值)到服務(wù)器來配置聊天室  
    muc.sendConfigurationForm(submitForm);  
    }
} catch (XMPPException | SmackException e) {  
    e.printStackTrace();  
    return null;  
}  
       return muc; 
   }

上面這段創(chuàng)建群聊聊天室設(shè)置表單屬性的那段代碼引用于網(wǎng)上的代碼段卵慰。
2.加入群聊聊天室

/**
  * 加入一個(gè)群聊聊天室
  * @param roomName     聊天室名字
  * @param nickName     用戶在聊天室中的昵稱
  * @param password     聊天室密碼
  * @return
  */
 public MultiUserChat joinChatRoom(String roomName,  String nickName, String password) {  
    if(!isConnected()) {
  throw new NullPointerException("服務(wù)器連接失敗超棺,請(qǐng)先連接服務(wù)器");
    }  
     try {
         // 使用XMPPConnection創(chuàng)建一個(gè)MultiUserChat窗口  
         MultiUserChat muc = MultiUserChatManager.getInstanceFor(connection).
                          getMultiUserChat(roomName + "@conference." + connection.getServiceName());  
         // 聊天室服務(wù)將會(huì)決定要接受的歷史記錄數(shù)量  
         DiscussionHistory history = new DiscussionHistory();  
         history.setMaxChars(0);  
         // history.setSince(new Date());  
         // 用戶加入聊天室  
         muc.join(nickName, password);  
         return muc;  
     } catch (XMPPException | SmackException e) {  
         e.printStackTrace();  
         return null;  
     }  
 }

在實(shí)現(xiàn)加入群聊聊天室的這段代碼中有這么一段代碼:

getMultiUserChat(roomName + "@conference." + connection.getServiceName());

在@與ServiceName中間必須加上conference這個(gè)字符串,我也不知道為什么呵燕,我最開始時(shí)不知道沒有加,然后無論如何都加入失敗件相,后來在網(wǎng)上查資料查了半天再扭,有人說是要加上這個(gè),然后我加上就成功了夜矗,暫時(shí)沒搞明白為什么泛范,先把程序跑通會(huì)用了再研究其他的原因。

3.群聊發(fā)送消息
當(dāng)你創(chuàng)建或是加入群聊聊天室后紊撕,即可獲得群聊對(duì)象MultiUserChat罢荡,通過該對(duì)象即可發(fā)送群聊消息:

multiUserChat.sendMessage(msg);//發(fā)送群聊消息

4.接收群聊消息

//聊天室消息監(jiān)聽
private MessageListener messageListener = new MessageListener() {
    @Override
    public void processMessage(Message message) {
        //與單聊接收處理消息類似,聊天室里所有人(包括發(fā)送人自己)發(fā)送的消息都會(huì)通過此方法進(jìn)行回調(diào)處理
    }
};
//設(shè)置聊天室消息監(jiān)聽
multiUserChat.addMessageListener(messageListener);

群聊接收消息與單聊接收消息還是很像的,只是監(jiān)聽對(duì)象区赵,監(jiān)聽方式稍稍有點(diǎn)區(qū)別惭缰,整個(gè)來說,消息接收還是很簡單的笼才。

3.文件傳輸

1.獲取文件傳輸對(duì)象

/**
  * 獲取發(fā)送文件的發(fā)送器
  * @param jid  一個(gè)完整的jid(如:laohu@192.168.0.108/Smack
  *                         后面的Smack應(yīng)該客戶端類型漱受,不加這個(gè)會(huì)出錯(cuò))
  * @return
  */
 public OutgoingFileTransfer getSendFileTransfer(String jid) {
    if(isConnected()) {
  return FileTransferManager.getInstanceFor(connection).createOutgoingFileTransfer(jid);
    }
    throw new NullPointerException("服務(wù)器連接失敗,請(qǐng)先連接服務(wù)器");
 }

獲取文件傳輸對(duì)象時(shí)的參數(shù)JID記得為文件傳輸JID:解釋請(qǐng)參考我的系列文章之基于openfire+smack開發(fā)Android即時(shí)聊天應(yīng)用[三]-賬號(hào)信息骡送、添加好友昂羡、JID理解等
2.發(fā)送文件

//獲取文件傳輸對(duì)象
OutgoingFileTransfer transfer = getSendFileTransfer(jid);
//發(fā)送文件
transfer.sendFile(File file, String description);
//此處執(zhí)行文件發(fā)送狀態(tài)監(jiān)聽
以上代碼為發(fā)送文件file,參數(shù)description為對(duì)這次文件傳輸?shù)拿枋?

3.文件傳輸(包括文件發(fā)送與接收)過程監(jiān)聽(傳輸開始摔踱、完成虐先、進(jìn)度百分比)

//文件傳輸過程中的狀態(tài)監(jiān)聽分析
if(transfer.getProgress() < 1) {//開始傳輸
//傳輸進(jìn)度,值為0~1
}

while(!transfer.isDone()) {//判斷傳輸是否完成派敷,傳輸取消蛹批、傳輸完成、傳輸發(fā)生錯(cuò)誤都會(huì)返回true
    try {
        Thread.sleep(200);
    } catch (InterruptedException e) {
    e.printStackTrace();
   }
}

if(FileTransfer.Status.complete.equals(transfer.getStatus())) {
    //傳輸完成  
} else if(FileTransfer.Status.cancelled.equals(transfer.getStatus())) {
    //傳輸取消
} else if(FileTransfer.Status.error.equals(transfer.getStatus())) {
    //傳輸錯(cuò)誤
} else if(FileTransfer.Status.refused.equals(transfer.getStatus())) {
    //傳輸拒絕
}

以上代碼需在子線程執(zhí)行膀息,可以在文件傳輸(發(fā)送般眉、接收)開始時(shí)設(shè)置進(jìn)度條,傳輸完成時(shí)去掉進(jìn)度條潜支,同時(shí)可以通過getProgress()方法獲得文件傳輸?shù)木唧w進(jìn)度百分比甸赃。
4.接收文件

/**
     * 添加文件接收的監(jiān)聽
     * @param fileTransferListener
     */
    public void addFileTransferListener(FileTransferListener fileTransferListener) {
        if(isConnected()) {
        FileTransferManager.getInstanceFor(connection).addFileTransferListener(fileTransferListener);
        return;
        }
        throw new NullPointerException("服務(wù)器連接失敗,請(qǐng)先連接服務(wù)器");
    }
addFileTransferListener(new FileTransferListener() {
    @Override
    public void fileTransferRequest(FileTransferRequest request) {
        // Accept it
    IncomingFileTransfer transfer = request.accept();
    try {
        String description = request.getDescription();
        //在目錄fileDir目錄下新建一個(gè)名字為request.getFileName()的文件
            File file = new File(fileDir ,request.getFileName());
            //開始接收文件(將傳輸過來的文件內(nèi)容輸出到file中)
        transfer.recieveFile(file);
        //此處執(zhí)行文件傳輸監(jiān)聽
    } catch (SmackException | IOException e) {
        e.printStackTrace();
    }
    }
});

上面代碼為設(shè)置文件接收監(jiān)聽

4.發(fā)送語音冗酿、圖片消息

我查看了半天的Smack的API埠对,但是沒有找到直接發(fā)送語音、圖片消息的API裁替,我說說我的實(shí)現(xiàn)思路项玛。

  • 其實(shí)圖片、語音都是文件弱判,我們可以把它們當(dāng)做文件發(fā)送給好友襟沮。

  • 在發(fā)送文件的同時(shí),用描述字段進(jìn)行標(biāo)記傳輸過來的是圖片還是語音昌腰。

  • 然后在接收到該文件后通過描述字段進(jìn)行區(qū)分當(dāng)前接收的是圖片文件還是語音文件开伏,然后進(jìn)行區(qū)分展示即可,這樣就可以達(dá)到發(fā)送圖片消息和語音消息遭商。

  • 但是我的這種實(shí)現(xiàn)方式還是有問題的固灵,因?yàn)檫@種方式對(duì)于單聊還是可以實(shí)現(xiàn)的。但是如果是群聊的話劫流,我就必須給每個(gè)人都發(fā)一個(gè)相同的文件巫玻,這樣的話一條語音或圖片消息丛忆,其實(shí)是要發(fā)送N次的,對(duì)于發(fā)送人來說流量就多消耗了N-1倍仍秤,所以這種方式對(duì)于實(shí)現(xiàn)群聊是行不通的熄诡。

對(duì)于群聊發(fā)送語音和圖片消息,我的思路是這樣的:

  • 自己寫一個(gè)上傳文件的服務(wù)徒扶。
  • 發(fā)送語音或圖片消息時(shí)粮彤,將圖片或語音通過上述上傳服務(wù)上傳到服務(wù)器上。
  • 在上傳完語音或圖片后姜骡,再向聊天室里發(fā)送一個(gè)文本消息导坟,發(fā)送內(nèi)容為文件的類似下載地址這樣的信息,同時(shí)還要告訴群成員這個(gè)文件是圖片還是語音圈澈。
  • 群成員接收到這樣的特殊文本消息后去自動(dòng)下載這個(gè)文件然后進(jìn)行展示或是其他處理惫周。
  • 群聊天里發(fā)送圖片或語音消息的這個(gè)實(shí)現(xiàn)方式我沒有驗(yàn)證,但我覺得應(yīng)該是可行的康栈。至于單聊發(fā)送語音或圖片消息的思路我是實(shí)現(xiàn)驗(yàn)證成功了的递递,是可行的。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末啥么,一起剝皮案震驚了整個(gè)濱河市登舞,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌悬荣,老刑警劉巖菠秒,帶你破解...
    沈念sama閱讀 221,576評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異氯迂,居然都是意外死亡践叠,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,515評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門嚼蚀,熙熙樓的掌柜王于貴愁眉苦臉地迎上來禁灼,“玉大人,你說我怎么就攤上這事轿曙∨叮” “怎么了?”我有些...
    開封第一講書人閱讀 168,017評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵导帝,是天一觀的道長守谓。 經(jīng)常有香客問我,道長舟扎,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,626評(píng)論 1 296
  • 正文 為了忘掉前任悴务,我火速辦了婚禮睹限,結(jié)果婚禮上譬猫,老公的妹妹穿的比我還像新娘。我一直安慰自己羡疗,他們只是感情好染服,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,625評(píng)論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著叨恨,像睡著了一般柳刮。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上痒钝,一...
    開封第一講書人閱讀 52,255評(píng)論 1 308
  • 那天秉颗,我揣著相機(jī)與錄音,去河邊找鬼送矩。 笑死蚕甥,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的栋荸。 我是一名探鬼主播菇怀,決...
    沈念sama閱讀 40,825評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼晌块!你這毒婦竟也來了爱沟?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,729評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤匆背,失蹤者是張志新(化名)和其女友劉穎呼伸,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體靠汁,經(jīng)...
    沈念sama閱讀 46,271評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡蜂大,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,363評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了蝶怔。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片奶浦。...
    茶點(diǎn)故事閱讀 40,498評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖踢星,靈堂內(nèi)的尸體忽然破棺而出澳叉,到底是詐尸還是另有隱情,我是刑警寧澤沐悦,帶...
    沈念sama閱讀 36,183評(píng)論 5 350
  • 正文 年R本政府宣布成洗,位于F島的核電站,受9級(jí)特大地震影響藏否,放射性物質(zhì)發(fā)生泄漏瓶殃。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,867評(píng)論 3 333
  • 文/蒙蒙 一副签、第九天 我趴在偏房一處隱蔽的房頂上張望遥椿。 院中可真熱鬧基矮,春花似錦、人聲如沸冠场。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,338評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽碴裙。三九已至钢悲,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間舔株,已是汗流浹背莺琳。 一陣腳步聲響...
    開封第一講書人閱讀 33,458評(píng)論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留督笆,地道東北人芦昔。 一個(gè)月前我還...
    沈念sama閱讀 48,906評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像娃肿,于是被迫代替她去往敵國和親咕缎。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,507評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容

  • 點(diǎn)擊查看原文 Web SDK 開發(fā)手冊(cè) SDK 概述 網(wǎng)易云信 SDK 為 Web 應(yīng)用提供一個(gè)完善的 IM 系統(tǒng)...
    layjoy閱讀 13,785評(píng)論 0 15
  • https://github.com/coderMyy/MYCoreTextLabel 圖文混排 , 實(shí)現(xiàn)圖片文...
    saman0閱讀 2,948評(píng)論 1 19
  • 一料扰、iOS 直播聊天室 Demo 說明 1凭豪、源碼結(jié)構(gòu) 2、AppDelegate 在 AppDelegate 中初...
    ajiao焦閱讀 2,853評(píng)論 2 7
  • 一晒杈、簡歷準(zhǔn)備 1嫂伞、個(gè)人技能 (1)自定義控件、UI設(shè)計(jì)拯钻、常用動(dòng)畫特效 自定義控件 ①為什么要自定義控件帖努? Andr...
    lucas777閱讀 5,217評(píng)論 2 54
  • 要學(xué)習(xí)基于XMPP協(xié)議的IM開發(fā),首先要熟悉XMPP協(xié)議本身粪般。 XMPP協(xié)議的組成主要的XMPP 協(xié)議范本及當(dāng)今應(yīng)...
    RichieQ閱讀 1,893評(píng)論 0 6