跳转到主要内容
消息管理器负责消息的增删改查、新消息监听、刷新消息监听、消息入库、发送消息回执监听、监听同步某个聊天数据等。

发送消息

基础发送方法

/**
 * 发送消息
 * @param textContent 消息正文
 * @param channelID 投递的频道ID
 * @param channelType 投递的频道类型(个人频道,群频道,客服频道等等)
 */
WKIM.getInstance().getMsgManager().sendMessage(textContent, channelID, channelType);
SDK 内置频道类型可通过 WKChannelType 查看

文本消息

// 定义文本消息
WKTextContent textContent = new WKTextContent("你好,悟空");
// 发送消息
WKIM.getInstance().getMsgManager().send(textContent, channel);

图片消息

// 定义图片消息
WKImageContent imageContent = new WKImageContent(localPath);
// 发送消息
WKIM.getInstance().getMsgManager().send(imageContent, channel);
在构建图片消息正文时,无需传递图片的高宽。SDK 会自动获取图片高宽

完整发送示例

public class ChatActivity extends AppCompatActivity {
    
    private String channelID;
    private byte channelType;
    
    // 发送文本消息
    private void sendTextMessage(String content) {
        WKTextContent textContent = new WKTextContent(content);
        WKIM.getInstance().getMsgManager().send(textContent, channelID, channelType);
    }
    
    // 发送图片消息
    private void sendImageMessage(String imagePath) {
        WKImageContent imageContent = new WKImageContent(imagePath);
        WKIM.getInstance().getMsgManager().send(imageContent, channelID, channelType);
    }
    
    // 发送语音消息
    private void sendVoiceMessage(String voicePath, int duration) {
        WKVoiceContent voiceContent = new WKVoiceContent(voicePath, duration);
        WKIM.getInstance().getMsgManager().send(voiceContent, channelID, channelType);
    }
    
    // 发送位置消息
    private void sendLocationMessage(double latitude, double longitude, String address) {
        WKLocationContent locationContent = new WKLocationContent(latitude, longitude, address);
        WKIM.getInstance().getMsgManager().send(locationContent, channelID, channelType);
    }
}

自定义消息

参考自定义消息: 自定义消息

消息入库监听

在发送消息时,SDK 将消息保存在本地数据库后就会触发入库回调。此时消息并未进行发送,可在此监听中将消息展示在 UI 上。
WKIM.getInstance().getMsgManager().addOnSendMsgCallback("key", new ISendMsgCallBackListener() {
    @Override
    public void onInsertMsg(WKMsg wkMsg) {
        // 可以在这里将保存在数据库的消息`wkMsg`展示在UI上
        runOnUiThread(() -> {
            addMessageToUI(wkMsg);
        });
    }
});
关于事件是否传入唯一 key 说明请查看事件监听

收到新消息监听

// 添加监听
WKIM.getInstance().getMsgManager().addOnNewMsgListener("key", new INewMsgListener() {
    @Override
    public void newMsg(List<WKMsg> list) {
        // list:接收到的消息
        runOnUiThread(() -> {
            handleNewMessages(list);
        });
    }
});

// 退出页面时移除监听
WKIM.getInstance().getMsgManager().removeNewMsgListener("key");
如果在聊天页面内收到新消息时需判断该消息是否属于当前会话,可通过消息对象 WKMsgchannelIDchannelType 判断

新消息处理示例

public class ChatActivity extends AppCompatActivity {
    
    private List<WKMsg> messageList = new ArrayList<>();
    private MessageAdapter messageAdapter;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        // 添加新消息监听
        WKIM.getInstance().getMsgManager().addOnNewMsgListener("ChatActivity", new INewMsgListener() {
            @Override
            public void newMsg(List<WKMsg> list) {
                handleNewMessages(list);
            }
        });
    }
    
    private void handleNewMessages(List<WKMsg> newMessages) {
        for (WKMsg msg : newMessages) {
            // 判断是否是当前会话的消息
            if (msg.channelID.equals(this.channelID) && msg.channelType == this.channelType) {
                runOnUiThread(() -> {
                    messageList.add(msg);
                    messageAdapter.notifyItemInserted(messageList.size() - 1);
                    
                    // 滚动到最新消息
                    recyclerView.scrollToPosition(messageList.size() - 1);
                    
                    // 标记消息为已读
                    markMessageAsRead(msg);
                });
            }
        }
    }
    
    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 移除监听器
        WKIM.getInstance().getMsgManager().removeNewMsgListener("ChatActivity");
    }
}

刷新消息监听

在 SDK 更新过消息时,如:消息发送状态、有人点赞消息、消息已读回执、消息撤回、消息被编辑等等,SDK 都将回调以下事件。UI 可通过消息对象 WKMsgclientMsgNO 来判断具体是哪条消息发生了更改。
// 添加刷新监听
WKIM.getInstance().getMsgManager().addOnRefreshMsgListener("key", new IRefreshMsg() {
    @Override
    public void onRefresh(WKMsg wkMsg, boolean isEnd) {
        // wkMsg:刷新的消息对象
        // isEnd:为了避免频繁刷新UI导致卡顿,当isEnd为true时再刷新UI
        if (isEnd) {
            runOnUiThread(() -> {
                refreshMessageInUI(wkMsg);
            });
        }
    }
});

// 退出页面时移除刷新监听
WKIM.getInstance().getMsgManager().removeRefreshMsgListener("key");

消息刷新处理示例

private void refreshMessageInUI(WKMsg updatedMsg) {
    // 根据clientMsgNO找到对应的消息并更新
    for (int i = 0; i < messageList.size(); i++) {
        WKMsg msg = messageList.get(i);
        if (msg.clientMsgNO.equals(updatedMsg.clientMsgNO)) {
            messageList.set(i, updatedMsg);
            messageAdapter.notifyItemChanged(i);
            break;
        }
    }
}

查看历史消息

/**
 * 查询或同步某个频道消息
 *
 * @param channelId                频道ID
 * @param channelType              频道类型
 * @param oldestOrderSeq           最后一次消息大orderSeq 第一次进入聊天传入0
 * @param contain                  是否包含 oldestOrderSeq 这条消息
 * @param dropDown                 是否下拉
 * @param aroundMsgOrderSeq        查询此消息附近消息 如 aroundMsgOrderSeq=20 返回数据则是 [16,17,19,20,21,22,23,24,25]
 * @param limit                    每次获取数量
 * @param iGetOrSyncHistoryMsgBack 请求返回
 */
WKIM.getInstance().getMsgManager().getOrSyncHistoryMessages(
    channelId, 
    channelType, 
    oldestOrderSeq, 
    contain, 
    dropDown, 
    limit, 
    aroundMsgOrderSeq, 
    new IGetOrSyncHistoryMsgBack() {
        @Override
        public void onSyncing() {
            // 正在同步中 按需显示loading
        }

        @Override
        public void onResult(List<WKMsg> list) {
            // 展示消息
        }
    }
);
获取历史消息并不是同步方法,因为有可能存在非连续性时会往服务器同步数据

历史消息加载示例

public class ChatActivity extends AppCompatActivity {
    
    private boolean isLoadingHistory = false;
    private long oldestOrderSeq = 0;
    
    // 加载历史消息
    private void loadHistoryMessages() {
        if (isLoadingHistory) return;
        
        isLoadingHistory = true;
        showLoadingIndicator();
        
        WKIM.getInstance().getMsgManager().getOrSyncHistoryMessages(
            channelID,
            channelType,
            oldestOrderSeq,
            false,  // 不包含oldestOrderSeq这条消息
            true,   // 下拉加载
            20,     // 每次加载20条
            0,      // 不查询周围消息
            new IGetOrSyncHistoryMsgBack() {
                @Override
                public void onSyncing() {
                    // 正在同步中
                    runOnUiThread(() -> showSyncingIndicator());
                }

                @Override
                public void onResult(List<WKMsg> list) {
                    runOnUiThread(() -> {
                        hideLoadingIndicator();
                        isLoadingHistory = false;
                        
                        if (list != null && !list.isEmpty()) {
                            // 将历史消息插入到列表开头
                            messageList.addAll(0, list);
                            messageAdapter.notifyItemRangeInserted(0, list.size());
                            
                            // 更新最旧消息的orderSeq
                            oldestOrderSeq = list.get(0).orderSeq;
                            
                            // 保持滚动位置
                            recyclerView.scrollToPosition(list.size());
                        }
                    });
                }
            }
        );
    }
    
    // 下拉刷新触发
    private void onPullToRefresh() {
        loadHistoryMessages();
    }
}

离线消息接收

需要实现同步频道消息数据源: 频道消息数据源 因为 WuKongIM 是支持消息永久存储,所以会产生海量的离线消息。对此我们采用了按需拉取的机制,如 10 个会话一个会话 10 万条消息,WuKongIM 不会把这个 10×10 万=100 万条消息都拉取到本地。而是采用拉取这 10 个会话的信息和对应的最新 20 条消息,也就是实际只拉取了 200 条消息相对 100 万条消息来说大大提高了离线拉取速度。用户点进对应的会话才会去按需拉取这个会话的消息。这些机制 SDK 内部都已做好了封装,使用者其实不需要关心。使用者只需要关心最近会话的变化和监听获取数据的回调即可。

数据结构说明

消息类核心属性

public class WKMsg {
    // 服务器消息ID(全局唯一,无序)
    public String messageID;
    // 本地唯一ID
    public String clientMsgNO;
    // 服务器时间 (10位时间戳)
    public long timestamp;
    // 消息来源发送者
    public String fromUID;
    // 聊天频道ID
    public String channelID;
    // 聊天频道类型
    public byte channelType;
    // 消息正文
    public WKMessageContent baseContentMsgModel;
    // 消息头
    public WKMsgHeader header;
    // 本地扩展字段
    public HashMap localExtraMap;
    // 远程扩展
    public WKMsgExtra remoteExtra;
    // ...
}

消息正文核心属性

public class WKMessageContent {
    // 消息内容类型
    public int type;
    // 消息中的@提醒信息
    public WKMentionInfo mentionInfo;
    // 消息回复对象
    public WKReply reply;
    
    // 编码消息 上层需实现该方法并编码
    public JSONObject encodeMsg() {
        return new JSONObject();
    }
    
    // 解码消息 上层需实现该方法并解码
    public WKMessageContent decodeMsg(JSONObject jsonObject) {
        return this;
    }
    // ...
}

消息属性说明

属性类型说明
messageIDString服务器消息ID(全局唯一,无序)
clientMsgNOString本地唯一ID,用于消息去重和更新
timestamplong服务器时间(10位时间戳)
fromUIDString消息发送者UID
channelIDString聊天频道ID
channelTypebyte聊天频道类型
baseContentMsgModelWKMessageContent消息正文内容
headerWKMsgHeader消息头信息
localExtraMapHashMap本地扩展字段
remoteExtraWKMsgExtra远程扩展信息

最佳实践

1. 消息列表性能优化

// 使用RecyclerView的ViewHolder模式
public class MessageAdapter extends RecyclerView.Adapter<MessageAdapter.ViewHolder> {
    
    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        WKMsg msg = messageList.get(position);
        
        // 避免频繁的类型判断
        if (holder.getItemViewType() == msg.baseContentMsgModel.type) {
            holder.bindMessage(msg);
        }
    }
    
    @Override
    public int getItemViewType(int position) {
        return messageList.get(position).baseContentMsgModel.type;
    }
}

2. 消息状态管理

private void updateMessageStatus(WKMsg msg) {
    switch (msg.status) {
        case WKSendMsgResult.send_success:
            // 发送成功
            showSuccessStatus(msg);
            break;
        case WKSendMsgResult.send_fail:
            // 发送失败
            showFailStatus(msg);
            break;
        case WKSendMsgResult.send_ing:
            // 发送中
            showSendingStatus(msg);
            break;
    }
}

3. 内存管理

@Override
protected void onDestroy() {
    super.onDestroy();
    
    // 移除所有监听器
    WKIM.getInstance().getMsgManager().removeNewMsgListener("ChatActivity");
    WKIM.getInstance().getMsgManager().removeRefreshMsgListener("ChatActivity");
    WKIM.getInstance().getMsgManager().removeOnSendMsgCallback("ChatActivity");
    
    // 清理消息列表
    if (messageList != null) {
        messageList.clear();
    }
}

下一步