『技術分享』-- 使用極光 IM 構建聊天功能

下位子發表於2019-01-20

前言

距離上次極光徵文不知不覺已經過了將近一年的時間,先感謝上次的徵文比賽,通過 《我和 Android 推送的時間簡史》 這篇文章獲獎,這次又厚著臉皮再次參與,因為專案一直很忙,只得利用週末時間準備 demo 素材和寫文章,不好之處,多多見諒。

上一篇文章主要講述了 我跟 極光推送 的關係,以及簡單的描述了其整合和使用,作為三個專案都在使用極光推送的我,對其瞭解也是相當多的,當然坑也踩了不少,不得不再次感謝大俠(極光技術人員)對我的幫助,雖然這一年沒有繼續接觸推送的業務,但是當我遇到困惑依然有問必答,服務態度不容置疑!

在準備參加徵文時就在想應該從哪個角度來寫呢,正好之前跟前同事一起寫了一個開源專案 WeaponApp, 現在已經有 800+ 的 star 了。

image.png

裡面涉及的技術我就不一一闡述了,感興趣的話可以自行看一下,裡面有一個模組由我單獨負責- IM模組,因為已經整合了極光推送,考慮到成本和使用,最終選擇了極光IM,畢竟是以極光推送的大規模、高併發、穩定的推送為技術基礎,並繼承這些特性。那這篇文章就以我整合的經歷和使用做個介紹,快速的實現具有 IM 功能的 APP。

展示

這裡只對 IM 模組功能做簡單的演示,感興趣可以點選 連結 進行下載,如下 gif 圖:

register.gif

ceshi.gif

基本的聊天功能已經實現,其中包括:

  1. 登入、註冊、強踢和退登
  2. 個人資訊檢視和修改
  3. 查詢好友並進行聊天
  4. 群聊
  5. 個人中心展示
  6. 刪除會話和清空聊天記錄

後續會根據需要新增新的功能。

整合

因為前項已經整合了極光推送服務,很多東西已經不需要重複操作,只需要將 JMessage 相關的元件整合到專案中即可,詳情的步驟可直接參考官網

1. 匯入jmessage jar 包 2. 在 AndroidManifest 中新增相應的事件

沒了。。由此可以看出,由推送到 IM 過渡是多麼流暢!

使用

其實在使用的過程,無非是根據自己的業務需求,到 官網 查詢 API 來實現自己想要的功能,那我就根據目前專案中有的功能進行介紹。

註冊、登入和退登

這其實是使用者的資訊管理,極光 IM 統一用 UserInfo 進行管理,內部包含了使用者的大部分資訊:

    protected long userID;
    protected String userName;
    protected String nickname = "";
    protected String avatarMediaID;
    protected String birthday = "";
    protected String signature = "";
    protected String gender = "";
    protected String region = "";
    protected String address = "";
    protected String appkey = null;
    protected String notename = null;
    protected String noteText = null;
    protected int mTime;
    protected Map<String, String> extras = new ConcurrentHashMap();
複製程式碼

1. 註冊

一開始打算自己寫使用者伺服器,事實上卻是由另一位開發者完成了,但是考慮到 IM 的整合,使用者資料的遷移和轉存過程繁瑣,就乾脆直接用極光的使用者介面,其實內部的資料也確實很詳細,還支援自定義欄位,完全滿足日常需求。

JMessageClient.register(userName, password, new JMessageCallBack() {
    @Override
    public void onSuccess() {
        registerSuccess(userName);
    }

    @Override
    public void onFailed(int status, String desc) {
        registerFailed(desc);
    }
});
複製程式碼

註冊需要使用者名稱和密碼,註冊成功後通過 setResult 的方法,將賬戶和密碼傳回登入頁面。

2. 登入

JMessageClient.login(userName, password, new JMessageCallBack() {
    @Override
    public void onSuccess() {
        loginSuccess(userName);
    }

    @Override
    public void onFailed(int status, String desc) {
        loginFailed(desc);
    }
});
複製程式碼

同註冊一樣,登入也需要使用者名稱和密碼進行登入,如果格式有誤會直接觸發 onFailed 回撥,彈出相應的提示。成功後本地便會儲存一個 UserInfo 物件儲存使用者的資訊。

3. 退登

極光支援主動退出賬號的功能,即:

JMessageClient.logout();
複製程式碼

直接清除本地儲存的使用者資訊,同樣他支援多端同時線上:

image.png

如果不開啟開關,另一臺裝置登入,會用 EventBus 傳送 LoginStateChangeEvent,告知開發者改賬號已在另一臺裝置登入,並且會攜帶三種狀態:

case user_password_change:
	forcedExit("賬號密碼被修改");
	break;
case user_logout:
	forcedExit("賬號在另一臺裝置登入");
	break;
case user_deleted:
	forcedExit("賬號被刪除");
	break;
複製程式碼

根據自己的需要進行處理。

資訊檢視和修改

1. 個人

自己的使用者資訊其實是儲存在本地的資料庫中,通過呼叫:

mUserInfo = JMessageClient.getMyInfo();
複製程式碼

獲取使用者資訊,之前提過 UserInfo 裡面包含了使用者的所有資料。與之對應的:

JMessageClient.updateMyInfo(UserInfo.Field.gender, mUserInfo, new JMessageCallBack() {
    @Override
    public void onSuccess() {
       
    }

    @Override
    public void onFailed(int status, String desc) {
        
    }
});
複製程式碼

這個就是修改自己資訊的方法,通過傳入 UserInfo.Field 來區分修改屬性值。

2. 他人

如果需要檢視好友的資訊,可通過 userName 進行請求查詢:

JMessageClient.getUserInfo(userName, new GetUserInfoCallback() {
    @Override
    public void gotResult(int status, String desc, UserInfo userInfo) {
        if (status == 0) {
            getViewModel().setUserInfo(userInfo);
        } else {
            getViewModel().setError(desc);
        }
    }
});
複製程式碼

具體的結果如下:

ceshi.gif

如果是個人資訊,直接可以修改和退登,如果是他人只能檢視並與其進行聊天。

聊天

終於到了核心的聊天功能,其實實現起來並不複雜,極光 IM 已經給了豐富的 API 和使用說明,足夠完成基本的需求。

1. 發訊息

發訊息,前提是需要構建 Message 物件,以基礎文字為例:

final Message message = mConversation.createSendTextMessage(sendContent);
message.setOnSendCompleteCallback(new BasicCallback() {
    @Override
    public void gotResult(int status, String desc) {
        if (status == 0) {
            // 訊息傳送成功
            MobclickAgent.onEvent(getContext().getApplicationContext(), "send_message", sendContent);
            addSendMessage(message);
            ++curCount;
            setSendContent("");
            getView().scrollToPosition(items.size() - 1);
        } else {
            // 訊息傳送失敗
            Toast.makeText(getContext(), desc, Toast.LENGTH_SHORT).show();
        }
    }
});
JMessageClient.sendMessage(message);
複製程式碼

最終通過 JmessageClient.sendMessage(message) 將訊息傳送出去。

2. 接收訊息

他這裡比較簡單粗暴,直接使用 EventBus 進行訊息接收的回撥。

他 jar 裡整合了 EventBus,專案中也有了 EventBus,這一點還是蠻坑的。換想一下,這裡也是為了方便接收,不然會有很多相互應用,各種耦合,不過使用過 EventBus 的小夥伴,應該知道,如果維護不好 EventBus 會導致邏輯非常的混亂,維護和擴充異常困難。

 * 接收訊息事件
 * 目前只支援文字訊息,後面再進行優化
 *
 * @param event 訊息事件
 */
public void onEventMainThread(MessageEvent event) {
    Message message = event.getMessage();
    switch (message.getContentType()) {
        case text:
            // 處理文字訊息
            String userName = ((UserInfo) message.getTargetInfo()).getUserName();
            if (TextUtils.equals(userName, mUserName)) {
                // 當收到的訊息是官方訊息才進行更新UI
                getViewModel().receiveMessage(message);
            }
        default:
            LogUtils.i("office", message.getFromType());
            break;
    }
}
複製程式碼

根據 contentType 區分訊息實體的型別,並做相應的處理。 在需要接收訊息的地方進行事件的註冊和取消註冊。

JMessageClient.registerEventReceiver(this, 200);
JMessageClient.unRegisterEventReceiver(this);
複製程式碼

3. 單聊

這裡引入一個 Conversation 概念,當你與他人聊天必然會建立會話,那會話的訊息和聊天的物件都會存在這個會話中,那單聊則通過傳入 userName 進行聯絡,由此可見 userName 的唯一性和重要性。

因為剛進去需要獲取歷史資訊,所以通過 conversion 獲取所有的訊息,並展示在介面上。

mConversation = Conversation.createSingleConversation(userName);
JMessageClient.getUserInfo(userName, this);
if (mConversation == null) {
    getView().finish();
}
// 獲取本地所有的訊息
msgCount = mConversation.getAllMessage().size();
List<Message> messagesFromNewest = mConversation.getMessagesFromNewest(curCount, LIM_COUNT);
curCount = messagesFromNewest.size();
// 第一條訊息是正序的,需要反轉一下
Collections.reverse(messagesFromNewest);
for (Message message : messagesFromNewest) {
    MessageDirect direct = message.getDirect();
    if (direct == MessageDirect.send) {
        addSendMessage(message);
    } else {
        addReceiverMessage(message);
    }
}
複製程式碼

4. 群聊

群聊與單聊有點類似,不過建立會話的前提引數不是 userName, 而是 groupId 群的唯一標識 ID。

mConversation = Conversation.createGroupConversation(groupId);
if (mConversation == null) {
    getView().finish();
    return;
}
List<Message> messagesFromNewest = mConversation.getMessagesFromNewest(curCount, LIM_COUNT);
curCount = messagesFromNewest.size();
Collections.reverse(messagesFromNewest);
for (Message message : messagesFromNewest) {
    MessageDirect direct = message.getDirect();
    if (direct == MessageDirect.send) {
        addSendMessage(message);
    } else {
        addReceiverMessage(message);
    }
}
複製程式碼

具體的程式碼很相似,只是建立的過程不一樣,不再贅述。

總結

前段時間王欣、位元組跳動等都推出社交軟體,不過在微信平臺被封殺,這點還是蠻狠的,不過另一方面反映出社交 聊天在各個行業應用的廣泛,無論是金融、教育、銷售等軟體都需要一個 IM 作為使用者與使用者、使用者和平臺之間的溝通橋樑,因此作為開發者,還是要多多學習一下 IM 相關的知識。當然自己能獨立完成最好,如果沒有經歷或者暫時能力不夠,又或者公司急切需要整合 IM 功能,建議你可以考慮極光 IM 服務,其推送服務做得還是蠻不錯的,而且還在不斷的維護迭代中,有時間不妨嘗試一波吧!

WeaponApp APK下載

本文為極光徵文參賽文章

相關文章