IM 即时通讯
Android
产品介绍
简介
产品优势
主要功能
功能介绍
帐号集成与登录
群组功能
聊天室功能
聊天室标签功能
圈组功能
多端登录与互踢策略
质量数据监控台
海外数据中心
IM平滑迁移方案
接口及业务限制
更新日志
IM UIKit 更新日志
NIM SDK 开发版更新日志
NIM SDK 稳定版更新日志
体验 Demo
下载 SDK 与 Demo 源码
快速开始
跑通 IM Demo 源码
实现单聊消息收发(不含 UI)
实现圈组消息收发(不含 UI)
含 UI 集成
什么是 IM UIKit
IM UIKit 功能概览
快速集成 IM UIKit
组件导入
初始化
界面跳转
自定义用户信息
全局配置
会话列表相关
集成会话列表界面
会话列表事件监听
自定义会话列表界面 UI
会话列表 API 概览
会话消息相关
集成会话界面
会话界面事件监听
实现地理位置消息功能(含 UI)
实现自定义消息发送(含 UI)
自定义会话界面 UI
会话消息 API 概览
通讯录相关
集成通讯录界面
自定义通讯录界面 UI
通讯录界面事件监听
通讯录 API 概览
IM UIKit API 概览
不含 UI 集成
集成 SDK
初始化
登录相关
登录 IM
多端登录与互踢
登出 IM
消息相关
消息概述
消息收发
自定义消息收发
消息配置选项
NOS 存储场景
广播消息收发
消息已读回执
消息撤回
消息重发与转发
消息更新
消息过滤
语音消息处理
插入本地消息
历史消息
最近会话
服务端会话服务
用户资料
用户关系
在线状态订阅
系统通知
系统通知概述
内置系统通知管理
内置系统通知未读数
自定义系统通知收发
离线推送与消息提醒
群组功能
群组概述
群组管理
群成员管理
群消息管理
超大群功能
聊天室
圈组功能
圈组概述
登录管理
服务器相关
服务器概述
服务器管理
服务器成员管理
游客功能
服务器未读数管理
频道相关
频道概述
频道管理
频道黑白名单
实时互动频道
频道分组
频道分组黑白名单
频道未读数管理
搜索服务器和频道
身份组相关
身份组概述
身份组应用场景
服务器身份组
频道身份组
用户定制权限
频道分组身份组
自定义权限项
成员权限查询与判定
身份组相关查询
圈组订阅机制
圈组消息相关
图解圈组消息流转
圈组消息收发
圈组消息撤回
圈组消息更新
圈组消息删除
消息正在输入
会话消息回复(Thread)
圈组快捷评论
获取频道最后一条消息
查询历史消息
圈组消息缓存
圈组消息搜索
圈组系统通知相关
圈组系统通知概述
圈组系统通知收发
圈组系统通知更新
圈组离线推送
圈组内容审核
圈组相关抄送
圈组第三方回调
圈组各端接口命名差异
反垃圾
聊天扩展
其他
最佳实践
IM 登录最佳实践
IM 应用隐私合规
聊天室重要消息投递
API 参考
Android SDK API
Android 端状态码
IM 控制台指南
创建应用
注册 IM 账号
升级服务
开通聊天室功能
配置应用客户端标识
常见问题
FAQ
错题集
Android 端推送问题排查
服务协议

消息发送(已不再维护)

更新时间: 2022/09/30 17:03:54

功能概述

SDK 提供一套完善的消息传输管理服务,包括收发消息,存储消息,上传下载附件等。支持发送文本,语音,图片,视频,文件,地理位置等类型消息,同时支持用户发送自定义类型的消息。

SDK 中用于表示消息的结构为 IMMessage,不同消息类型以 MsgTypeEnum 作区分。IMMessage 不支持继承扩展。

消息接收、已读回执、广播消息接收等相关说明请查看消息接收

消息发送

先通过 MessageBuilder 提供的接口创建对应的消息对象,然后调用 MsgServicesendMessage 接口发出。

/**
 * 发送消息。
 * @param msg    待发送的消息体,由{@link MessageBuilder}构造
 * @param resend 如果是发送失败后重发,标记为true,否则填false
 * @return InvocationFuture 可以设置回调函数。消息发送完成后才会调用,如果出错,会有具体的错误代码。
 */
public InvocationFuture<Void> sendMessage(IMMessage msg, boolean resend);

一秒内默认最多调用 sendMessage 接口100次。如需上调上限,请在官网首页通过微信、在线消息或电话等方式咨询商务人员。

文本消息发送

文本消息创建原型

/**
 * 创建一条普通文本消息
 *
 * @param sessionId   聊天对象ID
 * @param sessionType 会话类型
 * @param text        文本消息内容
 * @return IMMessage 生成的消息对象
 */
public static IMMessage createTextMessage(String sessionId, SessionTypeEnum sessionType, String text);
  • 参数说明
参数 说明
sessionId 聊天对象的 ID,如果是单聊,为用户帐号,如果是群聊,为群组 ID
sessionType 聊天类型,SessionTypeEnum.P2P 为单聊类型,SessionTypeEnum.Team 为群聊类型
text 文本消息内容
  • 示例
// 该帐号为示例
String account = "testAccount";
// 以单聊类型为例
SessionTypeEnum sessionType = SessionTypeEnum.P2P;
String text = "this is an example";
// 创建一个文本消息
IMMessage textMessage = MessageBuilder.createTextMessage(account, sessionType, text);
// 发送给对方
NIMClient.getService(MsgService.class).sendMessage(textMessage, false).setCallback(new RequestCallback<Void>() {
                @Override
                public void onSuccess(Void param) {

                }

                @Override
                public void onFailed(int code) {
                    
                }

                @Override
                public void onException(Throwable exception) {

                }
            });

图片消息发送

图片消息创建原型

/**
 * 创建一条图片消息
 *
 * @param sessionId   聊天对象ID
 * @param sessionType 会话类型
 * @param file        图片文件
 * @param displayName 图片文件的显示名,可不同于文件名
 * @param nosTokenSceneKey 文件资源场景
 * @return IMMessage 生成的消息对象
 */
public static IMMessage createImageMessage(String sessionId, SessionTypeEnum sessionType, File file, String displayName);

// 或者:创建一条图片消息并指定图片上传时使用的文件资源场景
public static IMMessage createImageMessage(String sessionId, SessionTypeEnum sessionType, File file, String displayName, String nosTokenSceneKey); 
  • 参数说明
参数 说明
sessionId 聊天对象的 ID,如果是单聊,为用户帐号,如果是群聊,为群组 ID
sessionType 聊天类型,SessionTypeEnum.P2P 为单聊类型,SessionTypeEnum.Team 为群聊类型
file 图片文件对象
displayName 图片文件的显示名,可不同于文件名,可设置为 null
nosTokenSceneKey 图片上传时使用的 nos scene ,默认为NimNosSceneKeyConstant.NIM_DEFAULT_IM,详见文件资源场景章节
  • 示例
// 该帐号为示例,请先注册
String account = "testAccount";
// 以单聊类型为例
SessionTypeEnum sessionType = SessionTypeEnum.P2P;
// 示例图片,需要开发者在相应目录下有图片
File file = new File("/sdcard/test.jpg");
// 创建一个图片消息
IMMessage message = MessageBuilder.createImageMessage(account, sessionType, file, file.getName());
// 或者:创建一个图片消息并指定图片上传时使用的文件资源场景,"nos_scene_key"请替换成开发者已经配置的
IMMessage message = MessageBuilder.createImageMessage(account, sessionType, file, file.getName(),"nos_scene_key");
// 发送给对方
NIMClient.getService(MsgService.class).sendMessage(message, false).setCallback(new RequestCallback<Void>() {
                @Override
                public void onSuccess(Void param) {

                }

                @Override
                public void onFailed(int code) {
                    
                }

                @Override
                public void onException(Throwable exception) {

                }
            });

语音消息发送

音频消息创建原型:

/**
 * 创建一条音频消息
 *
 * @param sessionId   聊天对象ID
 * @param sessionType 会话类型
 * @param file        音频文件对象
 * @param duration    音频文件持续时间,单位是ms
 * @param nosTokenSceneKey    文件资源场景
 * @return IMMessage 生成的消息对象
 */
public static IMMessage createAudioMessage(String sessionId, SessionTypeEnum sessionType, File file, long duration);

//或者:创建一条音频消息并指定音频上传时使用的文件资源场景
public static IMMessage createAudioMessage(String sessionId, SessionTypeEnum sessionType, File file, long duration, String nosTokenSceneKey);
  • 参数说明
参数 说明
sessionId 聊天对象的 ID,如果是单聊,为用户帐号,如果是群聊,为群组 ID
sessionType 聊天类型,SessionTypeEnum.P2P 为单聊类型,SessionTypeEnum.Team 为群聊类型
file 音频文件对象
duration 音频文件持续时间,单位是 ms
nosTokenSceneKey 音频文件上传时使用的 nos scene ,默认为NimNosSceneKeyConstant.NIM_DEFAULT_IM ,详见文件资源场景章节
  • 示例
// 该帐号为示例,请先注册
String account = "testAccount";
// 以单聊类型为例
SessionTypeEnum sessionType = SessionTypeEnum.P2P;
// 示例音频,需要开发者在相应目录下有文件
File audioFile = new File("/sdcard/testAudio.mp3");
// 音频时长,时间为示例
long audiolength = 2000;
// 创建音频消息
IMMessage audioMessage = MessageBuilder.createAudioMessage(account, sessionType, audioFile, audioLength);
// 或者:创建一个音频消息并指定音频上传时使用的文件资源场景,"nos_scene_key"请替换成开发者已经配置的
IMMessage audioMessage = MessageBuilder.createAudioMessage(account, sessionType, audioFile, "nos_scene_key");
// 发送给对方
NIMClient.getService(MsgService.class).sendMessage(audioMessage, false).setCallback(new RequestCallback<Void>() {
                @Override
                public void onSuccess(Void param) {

                }

                @Override
                public void onFailed(int code) {
                    
                }

                @Override
                public void onException(Throwable exception) {

                }
            });

视频消息发送

视频消息创建原型:

/**
 * 创建一条视频消息
 *
 * @param sessionId   聊天对象ID
 * @param sessionType 会话类型
 * @param file        视频文件对象
 * @param duration    视频文件持续时间
 * @param width       视频宽度
 * @param height      视频高度
 * @param displayName 视频文件显示名,可以为空
 * @param nosTokenSceneKey    文件资源场景
 * @return 视频消息
 */
public static IMMessage createVideoMessage(String sessionId, SessionTypeEnum sessionType, File file, long duration, int width, int height, String displayName);

//或者:创建一条视频消息并指定视频上传时使用的文件资源场景
public static IMMessage createVideoMessage(String sessionId, SessionTypeEnum sessionType, File file, long duration, int width, int height, String displayName, String nosTokenSceneKey)
  • 参数说明
参数 说明
sessionId 聊天对象的 ID,如果是单聊,为用户帐号,如果是群聊,为群组 ID
sessionType 聊天类型,SessionTypeEnum.P2P 为单聊类型,SessionTypeEnum.Team 为群聊类型
file 视频文件对象
duration 视频文件持续时间,单位 ms
width 视频宽度
height 视频高度
displayName 视频文件显示名,可为 null
nosTokenSceneKey 视频文件上传时使用的 nos scene ,默认为NimNosSceneKeyConstant.NIM_DEFAULT_IM ,详见文件资源场景章节
  • 示例
// 该帐号为示例,请先注册
String account = "testAccount";
// 以单聊类型为例
SessionTypeEnum sessionType = SessionTypeEnum.P2P;
// 示例视频,需要开发者在相应目录下有文件
File file = new File("/sdcard/testVideo.mp4");
// 获取视频mediaPlayer
MediaPlayer mediaPlayer;
try {
    mediaPlayer = MediaPlayer.create(context, Uri.fromFile(file));
} catch (Exception e) {
    e.printStackTrace();
}
// 视频文件持续时间
long duration = mediaPlayer == null ? 0 : mediaPlayer.getDuration();
// 视频高度
int height = mediaPlayer == null ? 0 : mediaPlayer.getVideoHeight();
// 视频宽度
int width = mediaPlayer == null ? 0 : mediaPlayer.getVideoWidth();
// 创建视频消息
IMMessage message = MessageBuilder.createVideoMessage(account, sessionType, file, duration, width, height, null);
// 或者:创建一个视频消息并指定视频上传时使用的文件资源场景,"nos_scene_key"请替换成开发者已经配置的
IMMessage message = MessageBuilder.createVideoMessage(account, sessionType, file, duration, width, height, null,"nos_scene_key");
// 发送给对方
NIMClient.getService(MsgService.class).sendMessage(message, false).setCallback(new RequestCallback<Void>() {
                @Override
                public void onSuccess(Void param) {

                }

                @Override
                public void onFailed(int code) {
                    
                }

                @Override
                public void onException(Throwable exception) {

                }
            });

文件消息发送

文件消息创建原型:

/**
 * 创建一条文件消息
 *
 * @param sessionId   聊天对象ID
 * @param sessionType 会话类型
 * @param file        文件
 * @param displayName 文件的显示名,可不同于文件名
 * @param nosTokenSceneKey    文件资源场景
 * @return IMMessage 生成的消息对象
 */
public static IMMessage createFileMessage(String sessionId, SessionTypeEnum sessionType, File file, String displayName);

//或者:创建一条文件消息并指定文件上传时使用的文件资源场景
public static IMMessage  createFileMessage(String sessionId, SessionTypeEnum sessionType, File file, String displayName, String nosTokenSceneKey)
  • 参数说明
参数 说明
sessionId 聊天对象的 ID,如果是单聊,为用户帐号,如果是群聊,为群组 ID
sessionType 聊天类型,SessionTypeEnum.P2P 为单聊类型,SessionTypeEnum.Team 为群聊类型
file 文件
displayName 文件的显示名,可不同于文件名
nosTokenSceneKey 文件上传时使用的 nos scene ,默认为NimNosSceneKeyConstant.NIM_DEFAULT_IM ,详见文件资源场景章节
  • 示例
// 该帐号为示例,请先注册
String account = "testAccount";
// 以单聊类型为例
SessionTypeEnum sessionType = SessionTypeEnum.P2P;
// 示例文件,需要开发者在相应目录下有文件
File file = new File("/sdcard/test.txt");
// 创建文件消息
IMMessage message = MessageBuilder.createFileMessage(account, sessionType, file, file.getName());
// 或者:创建一个文件消息并指定文件上传时使用的文件资源场景,"nos_scene_key"请替换成开发者已经配置的
IMMessage message = MessageBuilder.createFileMessage(account, sessionType, file, file.getName(),"nos_scene_key");
// 发送给对方
NIMClient.getService(MsgService.class).sendMessage(message, false).setCallback(new RequestCallback<Void>() {
                @Override
                public void onSuccess(Void param) {

                }

                @Override
                public void onFailed(int code) {
                    
                }

                @Override
                public void onException(Throwable exception) {

                }
            });

位置消息发送

地理位置消息创建原型:

/**
 * 创建一条地理位置信息
 *
 * @param sessionId   聊天对象ID
 * @param sessionType 会话类型
 * @param lat         纬度
 * @param lng         经度
 * @param addr        地理位置描述信息
 * @param nosTokenSceneKey    文件资源场景
 * @return IMMessage 生成的消息对象
 */
public static IMMessage createLocationMessage(String sessionId, SessionTypeEnum sessionType, double lat, double lng, String addr);
  • 参数说明
参数 说明
sessionId 聊天对象的 ID,如果是单聊,为用户帐号,如果是群聊,为群组 ID
sessionType 聊天类型,SessionTypeEnum.P2P 为单聊类型,SessionTypeEnum.Team 为群聊类型
lat 纬度
lng 经度
addr 地理位置描述信息
  • 示例
// 该帐号为示例,请先注册
String account = "testAccount";
// 以单聊类型为例
SessionTypeEnum sessionType = SessionTypeEnum.P2P;
// 纬度
double lat = 30.3;
// 经度
double lng = 120.2;
// 地理位置描述信息
String addr = "杭州";
// 创建地理位置信息
IMMessage message = MessageBuilder.createLocationMessage(account, sessionType, lat, lng, addr);

// 发送给对方
NIMClient.getService(MsgService.class).sendMessage(message, false).setCallback(new RequestCallback<Void>() {
                @Override
                public void onSuccess(Void param) {

                }

                @Override
                public void onFailed(int code) {
                    
                }

                @Override
                public void onException(Throwable exception) {

                }
            });

提示消息发送

提示消息(又叫做 Tip 消息)主要用于会话内的通知提醒,可以看做是自定义消息的简化,有独立的消息类型 MsgTypeEnum.tip 。 区别于自定义消息,Tip 消息暂不支持 setAttachment,如果要使用 Attachment 请使用自定义消息。 Tip 消息使用场景例如:进入会话时出现的欢迎消息,或是会话过程中命中敏感词后的提示消息等场景,当然也可以用自定义消息实现,只是相对复杂一些。

提示消息创建原型:

/**
 * 创建一条提示消息
 *
 * @param sessionId   聊天对象ID
 * @param sessionType 会话类型
 * @return IMMessage 生成的消息对象
 */
public static IMMessage createTipMessage(String sessionId, SessionTypeEnum sessionType);
  • 示例
// 向群里插入一条Tip消息,使得该群能立即出现在最近联系人列表(会话列表)中,满足部分开发者需求。
Map<String, Object> content = new HashMap<>(1);
content.put("content", "成功创建高级群");
// 创建tip消息,teamId需要开发者已经存在的team的teamId
IMMessage msg = MessageBuilder.createTipMessage(teamId, SessionTypeEnum.Team);
msg.setRemoteExtension(content);
// 自定义消息配置选项
CustomMessageConfig config = new CustomMessageConfig();
// 消息不计入未读
config.enableUnreadCount = false;
msg.setConfig(config);
// 消息发送状态设置为success
msg.setStatus(MsgStatusEnum.success);
// 保存消息到本地数据库,但不发送到服务器
NIMClient.getService(MsgService.class).saveMessageToLocal(msg, true).setCallback(new RequestCallback<Void>() {
                @Override
                public void onSuccess(Void param) {

                }

                @Override
                public void onFailed(int code) {
                    
                }

                @Override
                public void onException(Throwable exception) {

                }
            });

自定义消息发送

除了内建消息类型以外,SDK 还支持收发自定义消息类型。SDK 不负责定义和解析自定义消息的具体内容,解释工作由开发者完成。SDK 会将自定义消息存入消息数据库,会和内建消息一并展现在消息记录中。

为了使用更加方便,自定义消息采用附件的方式展示给开发者。体现在 IMMessage 类中,自定义消息的内容会被解析为 MsgAttachment 对象,由于 SDK 并不知道自定义消息的格式,第三方 App 需要注册一个自定义消息解析器。当第三方 App 调用查询查询消息历史的接口,或者是 SDK 收到消息通知第三方 App 时,就能将自定义消息内容转换为一个 MsgAttachment 对象,然后就可以同操作图片消息等带附件消息一样,操作自定义消息了。

创建自定义消息原型

/**
 * 创建一条APP自定义类型消息, 同时提供描述字段,可用于推送以及状态栏消息提醒的展示。
 *
 * @param sessionId   聊天对象ID
 * @param sessionType 会话类型
 * @param content     消息简要描述,可通过IMMessage#getContent()获取,主要用于用户推送展示。
 * @param attachment  消息附件对象
 * @param config      自定义消息配置
 * @param nosTokenSceneKey    文件资源场景
 * @return 自定义消息
 */
public static IMMessage createCustomMessage(String sessionId, SessionTypeEnum sessionType, String content, MsgAttachment attachment, CustomMessageConfig config);

//或者:创建一条APP自定义类型消息并指定文件(图片、音视、视频...)上传(如果有)时使用的 nos scene
public static IMMessage createCustomMessage(String sessionId, SessionTypeEnum sessionType, String content, MsgAttachment attachment, CustomMessageConfig config, String nosTokenSceneKey)

示例:剪刀石头布

在 demo 中,我们提供了一个用自定义消息实现的“剪刀石头布”的游戏,下面以此为例,详细解析其实现步骤。

首先,我们先定义一个自定义消息附件的基类,负责解析你的自定义消息的公用字段,比如类型等。还可以定义一些公共接口,用于一些便利性的调用。

注意: 实现 MsgAttachment 接口的成员都要实现 Serializable。

// 先定义一个自定义消息附件的基类,负责解析你的自定义消息的公用字段,比如类型等等。
public abstract class CustomAttachment implements MsgAttachment {

	// 自定义消息附件的类型,根据该字段区分不同的自定义消息
    protected int type;

    CustomAttachment(int type) {
        this.type = type;
    }

	// 解析附件内容。
    public void fromJson(JSONObject data) {
        if (data != null) {
            parseData(data);
        }
    }

	// 实现 MsgAttachment 的接口,封装公用字段,然后调用子类的封装函数。
    @Override
    public String toJson(boolean send) {
        return CustomAttachParser.packData(type, packData());
    }

    // 子类的解析和封装接口。
    protected abstract void parseData(JSONObject data);
    protected abstract JSONObject packData();
}

然后,继承这个基类,实现“剪刀石头布”的附件类型。注意,成员变量都要实现 Serializable。

public class GuessAttachment extends CustomAttachment {

	// 猜拳类型枚举
    public enum Guess {
        Shitou(1),
        Jiandao(2),
        Bu(3),
        ;
    }

    private Guess value;

    public GuessAttachment() {
        super(CustomAttachmentType.Guess);
        random();
    }

	// 解析猜拳类型具体数据
    @Override
    protected void parseData(JSONObject data) {
        value = Guess.enumOfValue(data.getIntValue("value"));
    }

	// 数据打包
    @Override
    protected JSONObject packData() {
        JSONObject data = new JSONObject();
        data.put("value", value.getValue());
        return data;
    }

    private void random() {
        int value = new Random().nextInt(3) + 1;
        this.value = Guess.enumOfValue(value);
    }
}

第三步,实现自定义消息的附件解析器。

public class CustomAttachParser implements MsgAttachmentParser {

	// 根据解析到的消息类型,确定附件对象类型
    @Override
    public MsgAttachment parse(String json) {
        CustomAttachment attachment = null;
        try {
            JSONObject object = JSON.parseObject(json);
            int type = object.getInteger("type");
            JSONObject data = object.getJSONObject(KEY_DATA);
            switch (type) {
                case CustomAttachmentType.Guess:
                    attachment = new GuessAttachment();
                    break;
                default:
                    attachment = new DefaultCustomAttachment();
                    break;
            }

            if (attachment != null) {
                attachment.fromJson(data);
            }
        } catch (Exception e) {

        }

        return attachment;
    }

	public static String packData(int type, JSONObject data) {
        JSONObject object = new JSONObject();
        object.put(KEY_TYPE, type);
        if (data != null) {
            object.put(KEY_DATA, data);
        }

        return object.toJSONString();
    }
}

最后,将该附件解析器注册到 SDK 中。为了保证生成历史消息时能够正确解析自定义附件,注册一般应放在 Application 的 onCreate 中 的主进程判断语句内完成。

if (NIMUtil.isMainProcess(this)) {
    // 监听的注册,必须在主进程中。
    NIMClient.getService(MsgService.class).registerCustomAttachmentParser(new CustomAttachParser()); 
}

示例:阅后即焚

若需要发送文件类型消息,例如图片等,可以参考阅后即焚的实现。具体实现步骤如下:

第一步,定义一个自定义的附件类型,并继承 FileAttachment。注意,成员变量都要实现 Serializable。

public class SnapChatAttachment extends FileAttachment {

	private static final String KEY_PATH = "path";
    private static final String KEY_SIZE = "size";
    private static final String KEY_MD5 = "md5";
    private static final String KEY_URL = "url";

    public SnapChatAttachment() {
        super();
    }

    public SnapChatAttachment(JSONObject data) {
        load(data);
    }

    @Override
    public String toJson(boolean send) {
        JSONObject data = new JSONObject();
        try {
	        // 重发使用本地路径
	        if (!send && !TextUtils.isEmpty(path)) {
                data.put(KEY_PATH, path);
            }

            if (!TextUtils.isEmpty(md5)) {
                data.put(KEY_MD5, md5);
            }
			// 注意:这段代码一定要写。
			// SDK在调toJson的时候 父类FileAttachemnt的url才有值。
			// 这个值是sdk自动赋值的。
            data.put(KEY_URL, url);
            data.put(KEY_SIZE, size);
        } catch (Exception e) {
            e.printStackTrace();
        }

        return CustomAttachParser.packData(CustomAttachmentType.SnapChat, data);
    }

    private void load(JSONObject data) {
	    path = data.getString(KEY_PATH);
        md5 = data.getString(KEY_MD5);
        url = data.getString(KEY_URL);
        size = data.containsKey(KEY_SIZE) ? data.getLong(KEY_SIZE) : 0;
    }
}

第二步,实现自定义消息的附件解析器。

public class CustomAttachParser implements MsgAttachmentParser {

	// 根据解析到的消息类型,确定附件对象类型
    @Override
    public MsgAttachment parse(String json) {
        CustomAttachment attachment = null;
        try {
            JSONObject object = JSON.parseObject(json);
            int type = object.getInteger("type");
            JSONObject data = object.getJSONObject(KEY_DATA);
            switch (type) {
                case CustomAttachmentType.SnapChat:
                    return new SnapChatAttachment(data);
                default:
                    attachment = new DefaultCustomAttachment();
                    break;
            }

            if (attachment != null) {
                attachment.fromJson(data);
            }
        } catch (Exception e) {

        }

        return attachment;
    }
    ...
}

最后,将该附件解析器注册到 SDK 中。为了保证生成历史消息时能够正确解析自定义附件,注册一般应放在 Application 的 onCreate 中完成。

if (NIMUtil.isMainProcess(this)) {
    // 监听的注册,必须在主进程中。
    NIMClient.getService(MsgService.class).registerCustomAttachmentParser(new CustomAttachParser()); 
}

消息属性设置

发送消息时可以设置消息配置选项 CustomMessageConfig,主要用于设定该消息是否存入云端、是否写入漫游等。

  • 参数说明

自定义消息配置 CustomMessageConfig 属性

/**
 * 该消息是否要保存到服务器
 * 默认为true。
 */
public boolean enableHistory = true;

/**
 * 该消息是否需要漫游。
 * 默认为true
 */
public boolean enableRoaming = true;

/**
 * 当发送方在多个客户端同时登录时,其中一端发送一条消息后,客户端是否需要在收消息的回调抛出该条消息。
 * 默认为true
 */
public boolean enableSelfSync = true;

/**
 * 该消息是否要推送提醒
 * 默认为true
 */
public boolean enablePush = true;

/**
 * 该消息是否需要推送昵称
 * 默认为true
 */
public boolean enablePushNick = true;

/**
 * 该消息是否要计入未读数。
 * 默认为true
 */
public boolean enableUnreadCount = true;

/**
 * 该消息是否触发抄送。
 * 默认为true
 */
public boolean enableRoute = true;

/**
 *
 * 该消息是否要存离线,若设置为false,将不会存入离线库与云端历史消息库。
 * 默认为true
   */
public boolean enablePersist = true;
  • 示例
// 该帐号为示例,请先注册
String account = "testAccount";
// 以单聊类型为例
SessionTypeEnum sessionType = SessionTypeEnum.P2P;
String text = "this is an example";
// 创建一个文本消息
IMMessage textMessage = MessageBuilder.createTextMessage(account, sessionType, text);

// 消息的配置选项
CustomMessageConfig config = new CustomMessageConfig();
// 该消息不保存到服务器
config.enableHistory = false;
// 该消息不漫游
config.enableRoaming = false;
// 该消息不同步
config.enableSelfSync = false;
textMessage.setConfig(config);

// 发送给对方
NIMClient.getService(MsgService.class).sendMessage(textMessage, false).setCallback(new RequestCallback<Void>() {
                @Override
                public void onSuccess(Void param) {

                }

                @Override
                public void onFailed(int code) {
                    
                }

                @Override
                public void onException(Throwable exception) {

                }
            });

文件资源场景

SDK支持对图片、语音、视频与文件等设置对应的存活时间。

  • 预设文件资源场景

在初始化SDK时,可做相应的文件资源场景配置,参考代码:


static SDKOptions getSDKOptions(Context context) {
        SDKOptions options = new SDKOptions();
         //其他配置
        options.mNosTokenSceneConfig = createNosTokenScene();
        return options;
}

public static final String TEST_NOS_SCENE_KEY="test_nos_scene_key";

private static NosTokenSceneConfig createNosTokenScene() {
      NosTokenSceneConfig nosTokenSceneConfig = new NosTokenSceneConfig();
      
      //更新默认场景(NimNosSceneKeyConstant.NIM_DEFAULT_IM)对应的过期时间(天)
      nosTokenSceneConfig.updateDefaultIMSceneExpireTime(1);
      
      //更新默认场景 (NimNosSceneKeyConstant.NIM_DEFAULT_PROFILE) 对应的过期时间(天)
      nosTokenSceneConfig.updateDefaultProfileSceneExpireTime(2);
      
      //设置自定义场景及对应的过期时间(天),0代表永不过期。
      //建议sceneKey常量化,这样使用的时候比较方便,目前支持自定义最多10种场景
      nosTokenSceneConfig.appendCustomScene(TEST_NOS_SCENE_KEY, 4);
      return nosTokenSceneConfig;
}

NimNosSceneKeyConstant主要参数一览:

NimNosSceneKeyConstant 含义
NIM_DEFAULT_IM 私聊、群聊、聊天室发送图片、音频、视频、文件消息等。
NIM_DEFAULT_PROFILE 对应使用云信上传服务上传的用户、群组..资料(eg:头像)等。

值得注意的是:

  • 如果使用了不存在的场景,那么上传将失败(错误码:5)
  • 如果文件过期了,再去下载文件,那么会下载失败(错误码:4)

本地消息插入

当有业务场景需要单纯插入一条消息至本地数据库内,而不发出时,可以使用插入本地消息的方法来实现。

/**
* 插入消息到本地数据库,且通知更新UI界面,但不发送到服务器端。发送一条可设置他人为发送方的本地消息给自己。
* 该接口将消息保存到数据库后,会通知到UI,此时会触发{@link MsgServiceObserve#observeReceiveMessage(Observer, boolean)}通知。
*
* @param msg    待插入的消息对象
* @param fromAccount 发送者ID
* @return InvocationFuture 可以设置回调函数。在消息存入数据库后,就会回调。
*/
InvocationFuture<Void> insertLocalMessage(IMMessage msg, String fromAccount);

/**保存消息到本地数据库,但不发送到服务器端。可设置保存消息的时间 用于第三方APP保存本地提醒一类的消息。 该接口将消息保存到数据库后,如果需要通知到UI,可将notify设置为true,此时会触发{@link MsgServiceObserve.observeReceiveMessage(Observer, boolean)}通知。

* @param msg    待插入的消息对象
* @param notify 是否要通知
* @param time   设置该本地消息的时间
* @return InvocationFuture 可以设置回调函数。在消息存入数据库后,就会回调。
*/
InvocationFuture<java.lang.Void> saveMessageToLocalEx(IMMessage msg,boolean notify,long time);

消息更新

扩展字段更新

SDK支持更新客户端数据库内的消息的客户端扩展字段。

/**
* 更新消息的LocalExtension
* @param  message  待更新的消息
*/
void updateIMMessage(IMMessage message);

状态更新

/**
* 更新消息状态
* @param  message  待更新的消息
*/
void updateIMMessageStatus(IMMessage message);

示例:

msg.setStatus(MsgStatusEnum.success);
NIMClient.getService(MsgService.class).updateIMMessageStatus(msg);

注意:请不要对notification类型消息进行更新。

消息重发

消息发送失败之后,可以重发消息。消息重发和消息发送是同一个接口,只是参数的设置不同而已,当 resend 参数为 true 时,表示重发消息。

消息转发

SDK 支持消息转发功能,但不支持通知消息和音视频通话事件消息的转发,其他消息类型均支持。

首先,通过 MessageBuilder 创建一个待转发的消息,然后通过 MsgService#sendMessage 接口,将消息发送出去。

/**
 * 创建一条待转发的消息
 *
 * @param message     要转发的消息
 * @param sessionId   聊天对象ID
 * @param sessionType 会话类型
 * @return 待转发的消息
 */
public static IMMessage createForwardMessage(IMMessage message, String sessionId, SessionTypeEnum sessionType);
  • 参数说明
参数 说明
message 要转发的消息
sessionId 聊天对象的 ID,如果是单聊,为用户帐号,如果是群聊,为群组 ID
sessionType 聊天类型,SessionTypeEnum.P2P 为单聊类型,SessionTypeEnum.Team 为群聊类型
  • 示例
// 该帐号为示例,请先注册
String account = "testAccount";
// 以单聊类型为例
SessionTypeEnum sessionType = SessionTypeEnum.P2P;
// forwardMessage为待转发的消息, 一般由上下文获得
IMMessage message = MessageBuilder.createForwardMessage(forwardMessage, account, sessionType);
// 发送给对方
NIMClient.getService(MsgService.class).sendMessage(textMessage, false).setCallback(new RequestCallback<Void>() {
                @Override
                public void onSuccess(Void param) {

                }

                @Override
                public void onFailed(int code) {
                    
                }

                @Override
                public void onException(Throwable exception) {

                }
            });

消息撤回

消息撤回

允许用户撤回一定时间内发送过的消息,客户端可允许撤回时长默认2分钟,可在网易云信控制台配置。
8.5.0起,支持撤回自己发送给自己的消息

  • API 原型
/**
 * 消息撤回 , 并设置相应的第三方推送配置(包括IOS平台的推送)与未读数变化,如果想要关闭App内的返回消息提醒,参考{@link NIMClient#toggleRevokeMessageNotification(boolean on)}
 *
 * @param message        待撤回的消息
 * @param customApnsText 第三方透传消息推送文本,不填则不推送
 * @param pushPayload    第三方自定义的推送属性,限制json类型,长度2048
 * @param shouldNotifyBeCount 撤回通知是否更新未读数
 * @param postscript 附言
 * @param attach 扩展字段
 *
 * @return InvocationFuture 可设置回调函数,监听发送结果。
 */
InvocationFuture<Void> revokeMessage(IMMessage message, String customApnsText,
                                     Map<String, Object> pushPayload, boolean shouldNotifyBeCount,
                                     String postscript, String attach);
  • 参数说明
参数 说明
message 待撤回的消息
customApnsText 第三方透传消息推送文本,不填则不推送
pushPayload 第三方自定义的推送属性,限制json类型,长度2048
shouldNotifyBeCount 撤回通知是否更新未读数
postscript 附言
attach 扩展字段
  • 示例
NIMClient.getService(MsgService.class).revokeMessage(message, null, null, true, postscript, attachJson).setCallback(new RequestCallbackWrapper<Void>() {
    @Override
    public void onResult(int code, Void result, Throwable exception) {

    }
});

在撤回消息请求调用成功后, SDK 会先回调给上层成功,再自动将本地的这条消息删除。如果需要在撤回后显示一条本方已撤回的提示,开发者可以自行构造一条提醒消息并调用插入本地消息的方法。

以下情况消息撤回会失败:

  • 消息为空
  • 消息没有发送成功
  • 消息超过撤回时限

针对撤回场景的通知栏内容覆盖需求:如A发消息给B,产生APNs推送,文案内容为“你好“。然后A撤回了这条消息,此时通知栏中的“你好”变为预设的“对方撤回了一条消息”。

一种建议的实现方式是:

  • 在发送消息时,需要通过 NIMMessage - setPushPayload(Map pushPayload)方法 插入key为apns-collapse-id的键值对,value的内容建议使用uuid等字符串,用以唯一标识该消息。
  • 当要撤回这条消息时,在撤回接口传参 customApnsText 中设置覆盖文案,在 pushPayload 中插入与被撤回消息相同的apns-collapse-id键值对。

监听消息撤回

被撤回方会收到消息撤回通知。

  • API 原型
/**
 * 注册/注销消息撤回的观察者
 * @param observer 观察者,参数为被撤回的消息。
 * @param register true为注册,false为注销
 */
public void observeRevokeMessage(Observer<RevokeMsgNotification> observer, boolean register);
  • RevokeMsgNotification 说明
方法 说明
getMessage() 获取撤回消息
getAttach() 获取撤回附件字段
getRevokeAccount() 获取撤回者
getCustomInfo() 获取消息撤回时设置的msg 字段(eg: 通过服务端API撤回)。
getNotificationType() 获取通知类型: 1表示是离线通知,2表示是漫游通知 , 默认 0
getRevokeType() 获取撤回类型:分为点对点双向撤回, 群双向撤回, 超大群双向撤回, 点对点单向撤回和未定义
getCallbackExt() 获取第三方扩展字段
  • 示例
Observer<RevokeMsgNotification> revokeMessageObserver = new Observer<RevokeMsgNotification>() {
    @Override
    public void onEvent(RevokeMsgNotification notification) {
       // 监听到消息撤回的通知,可以在界面做相应的操作
       // 获取撤回消息
       notification.getMessage();
       // 获取撤回者
       notification.getRevokeAccount();
    }
};

NIMClient.getService(MsgServiceObserve.class).observeRevokeMessage(revokeMessageObserver, true);

此外,若启用消息提醒功能,则收到撤回通知时,也会触发产生提醒通知栏。若不需要,可以通过

NIMClient.toggleRevokeMessageNotification(false);

来关闭,详见撤回通知消息提醒

撤回是否展示通知的过滤器设置

设置一个过滤器,根据撤回通知对象判断是否应该展示通知栏。
如果其他逻辑判断为应该上通知栏,则由此过滤器决定是否上通知栏;如果其他逻辑判断为不应该上通知栏,则无论过滤器如何配置,都不会上通知栏

该过滤器可以用于减少上通知栏的撤回消息通知的数量

  • API原型
/**
 * 注册是否在撤回消息时展示通知的过滤器
 */
void registerShouldShowNotificationWhenRevokeFilter(ShowNotificationWhenRevokeFilter filter);
  • ShowNotificationWhenRevokeFilter原型
public interface ShowNotificationWhenRevokeFilter {
    boolean showNotification(RevokeMsgNotification notification);
}
  • 示例
NIMClient.getService(MsgService.class).registerShouldShowNotificationWhenRevokeFilter(notification -> {
    return notification == null || TextUtils.isEmpty(notification.getAttach());
});

文件传输过程管理

监听文件传输进度

SDK提供文件传输管理功能,开发可以通过注册消息附件上传/下载进度观察者来监控文件传输。

/**
 * 注册/注销消息附件上传/下载进度观察者
 *
 * @param observer 观察者, 参数为附件的传输进度
 * @param register true为注册,false为注销
 */
void observeAttachmentProgress(Observer<AttachmentProgress> observer,
                               boolean register);

取消上传消息附件

MsgService 接口提供了取消上传消息附件的方法。

/**
*  取消上传消息附件(图片、视频、文件类型的),如果附件已经上传成功,操作将会失败 。如果成功取消了附件的上传,那么相应的消息会发送失败,对应的消息状态是MsgStatusEnum.fail,附件状态是AttachStatusEnum.cancel。注意:此操作暂时不支持聊天室。
* @param imMessage   要取消上传附件的消息
*/
InvocationFuture<java.lang.Void> cancelUploadAttachment(IMMessage imMessage);

语音消息处理

云信 SDK 提供了高清语音的录制与播放的功能,用于处理语音消息。

播放

云信提供 AudioPlayer 类来支持语音播放的功能。

AudioPlayer 接口说明:

返回值 AudioPlayer 接口 说明
long getCurrentPosition() 获取当前音频播放进度
long getDuration() 获取音频持续时间长度
OnPlayListener getOnPlayListener() 获取 AudioPlayer 的播放进度监听
boolean isPlaying() 查询是否正在播放
void seekTo(int msec) 让播放器跳转到指定位置继续播放
void setDataSource(String audioFile) 设置音频来源
void setOnPlayListener(OnPlayListener listener) 设置播放监听
void start(int audioStreamType) 开始播放
void stop() 停止播放

构造播放实例

  • API 原型
/**
 * 音频播放器构造函数
 * @param context 上下文参数
 * @param audioFile 待播放音频的文件路径
 * @param listener 播放进度监听者
 */
public AudioPlayer(Context context, String audioFile, OnPlayListener listener);
  • 参数说明
参数 说明
context 上下文参数
audioFile 待播放音频的文件路径
listener 播放进度监听者

OnPlayListener 接口说明:

OnPlayListener 接口 说明
onCompletion() 播放完成
onError(String error) 播放过程中出错。参数为出错原因描述
onInterrupt() 中断播放
onPlaying(long curPosition) 播放进度报告,每隔 500ms 会回调一次,告诉当前进度。 参数为当前进度,单位为毫秒,可用于更新 UI
onPrepared() 文件解码完成,准备播放
  • 示例
// 定义一个播放进程回调类
OnPlayListener listener = new OnPlayListener() {

    // 音频转码解码完成,会马上开始播放了
    public void onPrepared() {}

    // 播放结束
    public void onCompletion() {}

    // 播放被中断了
    public void onInterrupt() {}

    // 播放过程中出错。参数为出错原因描述
    public void onError(String error){}

    // 播放进度报告,每隔 500ms 会回调一次,告诉当前进度。 参数为当前进度,单位为毫秒,可用于更新 UI
    public void onPlaying(long curPosition) {}
};

// 构造播放器对象
AudioPlayer player = new AudioPlayer(context, filePath, listener);

播放控制

  • 播放音频
// 开始播放。需要传入一个 audioStreamType 参数,表示是用听筒播放还是扬声器。
// AudioManager.STREAM_VOICE_CALL    表示使用听筒模式
// AudioManager.STREAM_MUSIC         表示使用扬声器模式
player.start(audioStreamType);
  • 指定播放起点
// 如果中途切换播放设备,重新调用 start,传入指定的 audioStreamType 即可。player 会自动停止播放,然后再以新的 streamType 重新开始播放。
// 如果需要从中断的地方继续播放,需要开发者记住已经播放到的位置,然后在 onPrepared 回调中调用 seekTo
player.seekTo(pausedPosition);
  • 停止播放
player.stop();
  • 其他方法
// 获取当前音频播放进度
player.getCurrentPosition();

// 获取音频持续时间长度
player.getDuration();

// 查询是否正在播放
player.isPlaying();

// 设置音频来源
player.setDataSource(audioFile);

录制

SDK 提供 AudioRecorder 来支持语音的录制。

AudioRecorder 接口说明:

返回值 AudioRecorder 接口 说明
void completeRecord(boolean cancel) 完成(结束)录音,根据参数 cancel,做不同的回调。
如果 cancel 为 true,回调 IAudioRecordCallback#onRecordCancel, 为 false,回调 IAudioRecordCallback#onRecordSuccess
void destroyAudioRecorder() 释放资源
int getCurrentRecordMaxAmplitude() 获取当前录音时最大振幅, 40ms 更新一次数据
void handleEndRecord(boolean isSuccess, int duration) 处理录制结束后的操作,回调 IAudioRecordCallback#onRecordSuccess
boolean isRecording() 是否正在录音
void startRecord() 启动(开始)录音,如果成功,会按照顺序回调 IAudioRecordCallback#onRecordReady 和 IAudioRecordCallback#onRecordStart

构造录制实例

  • API 原型
/**
 * 构造函数
 *
 * @param context     上下文
 * @param recordType  录制音频类型(aac/amr)
 * @param maxDuration 最长录音时长,到该长度后,会自动停止录音
 * @param cb          录音过程回调
 */
public AudioRecorder(Context context, RecordType recordType,int maxDuration, IAudioRecordCallback cb);
  • 参数说明
参数 说明
context 上下文
recordType 录制音频类型(aac/amr)
maxDuration 最长录音时长,到该长度后,会自动停止录音
cb 录音过程回调
  • IAudioRecordCallback 接口说明:
IAudioRecordCallback 接口 说明
onRecordCancel() 录音结束, 用户主动取消录音
onRecordFail() 录音结束,出错
onRecordReachedMaxTime(int maxTime) 到达指定的最长录音时间
onRecordReady() 录音器已就绪,提供此接口用于在录音前关闭本地音视频播放(可选)
onRecordStart(File audioFile, RecordType recordType) 开始录音回调
onRecordSuccess(File audioFile, long audioLength, RecordType recordType) 录音结束,成功
  • 示例
// 定义录音过程回调对象
IAudioRecordCallback callback = new IAudioRecordCallback () {

	void onRecordReady() {
		// 初始化完成回调,提供此接口用于在录音前关闭本地音视频播放(可选)
	}

    void onRecordStart(File audioFile, RecordType recordType) {
	    // 开始录音回调
    }

    void onRecordSuccess(File audioFile, long audioLength, RecordType recordType) {
	    // 录音结束,成功
    }

    void onRecordFail() {
	    // 录音结束,出错
    }

    void onRecordCancel() {
	    // 录音结束, 用户主动取消录音
    }

    void onRecordReachedMaxTime(int maxTime) {
	    // 到达指定的最长录音时间
    }
};

// 初始化recorder
AudioRecorder recorder = new AudioRecorder(
	context,
	RecordType.AAC, // 录制音频类型(aac/amr)
	maxDuration, // 最长录音时长,到该长度后,会自动停止录音, 默认120s
	callback // 录音过程回调
);

录制控制

  • 录制音频
// 启动(开始)录音,如果成功,会按照顺序回调onRecordReady和onRecordStart。
recorder.startRecord();
  • 结束/取消录制
// 结束录音, 正常结束,或者取消录音
// cancel为true,代表取消;为false,代表结束录音。
recorder.completeRecord(cancel);
  • 销毁实例
recorder.destroyAudioRecorder();
  • 其他方法

// 是否正在录音
recorder.isRecording();

// 获取当前录音时最大振幅, 40ms更新一次数据。
recorder.getCurrentRecordMaxAmplitude();

// 录制达到最长录音时长时,确认当前录制
recorder.handleEndRecord(true, maxTime) 

语音转文字

要使用语音转文字功能,请联系商务顾问申请开通「语音识别」。如果未开通功能的情况下调用接口,将返回403。

语音转文字的原理:

  • 录制音频文件(目前最大支持60秒)。
  • 上传到云信存储服务器,返回文件url。
  • 通过语音转文字接口传入该url和相关参数,返回转换后的文字。

使用 MsgService 接口中如下方法进行语音转文字:

/**
*  语音转文字并指定上传文件的场景以及是否要强制重新上传文件
* @param  voiceUrl - 语音url。可选项。如果没有,SDK自动做nos上传工作。
* @param  path - 语音path, 用于获得语音采样率。APP必须保证音频已经下载到本地
* @param  duration - 语音时长
* @param  sceneKey - 上传文件时用的nos sceneKey ,默认值:NimNosSceneKeyConstant#NIM_DEFAULT_IM , nos token scene 配置参考文件资源场景章节
* @param  enableForceUploadFile - 如果服务器存在相同的文件,是否强制重新上传文件 ,默认false
* AbortableFuture 调用跟踪。可设置回调函数,可中止下载操作
*/
AbortableFuture<java.lang.String> transVoiceToTextEnableForce(java.lang.String voiceUrl,
                                                              java.lang.String path,
                                                              long duration,
                                                              java.lang.String sceneKey,
                                                              boolean enableForceUploadFile);

示例:

public void voiceToText(IMMessage msg, String sceneKey) {
        AudioAttachment attachment = (AudioAttachment) msg.getAttachment();
        String voiceUrl = attachment.getUrl();
        String path = attachment.getPath();
        refreshStartUI();
        callFuture = NIMClient.getService(MsgService.class).transVoiceToTextEnableForce(voiceUrl, path, attachment.getDuration(), sceneKey, false);
        callFuture.setCallback(new RequestCallback<String>() {
            @Override
            public void onSuccess(String param) {
                voiceTransText.setText(param);
                updateUI();
            }

            @Override
            public void onFailed(int code) {
                LogUtil.e(TAG, "voice to text failed, code=" + code);
                voiceTransText.setText(R.string.trans_voice_failed);
                failIcon.setVisibility(View.VISIBLE);
                updateUI();
            }

            @Override
            public void onException(Throwable exception) {
                LogUtil.e(TAG, "voice to text throw exception, e=" + exception.getMessage());
                voiceTransText.setText("参数错误");
                failIcon.setVisibility(View.VISIBLE);
                updateUI();
            }
        });
        show();
    }
此文档是否对你有帮助?
有帮助
我要吐槽
  • 功能概述
  • 消息发送
  • 文本消息发送
  • 图片消息发送
  • 语音消息发送
  • 视频消息发送
  • 文件消息发送
  • 位置消息发送
  • 提示消息发送
  • 自定义消息发送
  • 示例:剪刀石头布
  • 示例:阅后即焚
  • 消息属性设置
  • 文件资源场景
  • 本地消息插入
  • 消息更新
  • 扩展字段更新
  • 状态更新
  • 消息重发
  • 消息转发
  • 消息撤回
  • 消息撤回
  • 监听消息撤回
  • 撤回是否展示通知的过滤器设置
  • 文件传输过程管理
  • 监听文件传输进度
  • 取消上传消息附件
  • 语音消息处理
  • 播放
  • 构造播放实例
  • 播放控制
  • 录制
  • 构造录制实例
  • 录制控制
  • 语音转文字