介绍如何在鸿蒙(HarmonyOS)应用中集成互动消息SDK,实现评论、弹幕、点赞等直播互动能力。
前提条件
客户端集成前,请确保已完成服务端集成,并提供客户端访问的获取鉴权Token的接口。详细操作,请参见服务端集成。
环境要求
在开始集前,请确保开发环境满足以下要求:
已联系华为商务人员,签署合作计划,开通相关权限,获取 DevEco Studio 5.0.3.900 Release 或以上版本。
获取配套 文档中心的 HarmonyOS NEXT SDK 或以上版本。
获取配套 API Version 12的 HarmonyOS NEXT 5.0.0.102 操作系统或以上版本,支持音视频的鸿蒙设备,且已开启“允许调试”选项。
如果需要使用真机调试,请参考 鸿蒙官网文档 进行配置。
鸿蒙设备已经连接到 Internet。
已 注册华为开发者账号 并完成实名认证。
集成SDK
ohpm自动集成。
在鸿蒙主工程 entry 目录中的 oh-package.json5 中添加 SDK 依赖,然后点击 "Sync Now"。
{
"dependencies": {
"@aliyun_video_cloud/alivc-im-sdk": "最新版本x.y.z"
}
}具体最新版本x.y.z可前往版本更新说明查询。
添加 SDK 依赖权限。
权限名称 | 权限说明 | 使用目的 |
ohos.permission.INTERNET | 使用网络 | 连接通道、收发消息需要网络连接 |
使用SDK
SDK使用需遵循如下操作顺序:
初始化
登录
相关操作
登出
反初始化
其他
其中相关操作包含群组操作和消息操作,详细说明如下:
注意事项
各ID需遵循以下规则:
appid :最长64位,仅限于A~Z,a~z, 0~9及“-”, 不能包含其他字符。
userid:最长64位,仅限于A~Z,a~z, 0~9及“-”, 不能包含其他字符。
groupid:最长64位,仅限于A~Z,a~z, 0~9及“-”, 不能包含其他字符。
初始化
在使用SDK前需要进行初始化,可以在相关业务模块的主入口设置。
// 创建SDK配置
let config = new ImSdkConfig("your_app_id", "your_app_sign");
config.logLevel = ImLogLevel.DEBUG;
// 初始化SDK
let ret = AliVCIMEngine.getInstance().init(this.getUIContext().getHostContext()!, config);
if (ret == 0) {
// 初始化成功,添加监听器
addListeners();
}监听长连接状态
初始化成功后,添加监听。
// 添加SDK状态监听器:可以按需监听对应的回调
let engineListener: AliVCIMEngineListener = new AliVCIMEngineListener()
.setOnLinkStateEventListener((event: ImLinkStateEvent): void => {
// 连接状态变化
})
.setOnExitListener((exitCode: number): void => {
// 登录退出回调:1主动退出 2被踢出 3超时等其他原因 4在其他端上登录
})
.setOnTokenExpiredListener((getTokenCallback: AliVCIMTokenCallback): void => {
// Token过期处理
})
.setOnReconnectSuccessListener((groupStatus: ImGroupInfo[]): void => {
// 重连成功回调:断连期间群组信息可能发送变化
});
AliVCIMEngine.getInstance().addEngineListener(engineListener);登录
生成登录token。
private getTokenAuth(userId: string, role: string): ImAuth {
const appId: string = 'your_app_id';
const appKey: string = 'your_app_key';
const nonce: string = "your_nonce";
const tokenDuration: number = 24*60*60; // 1天有效期;跟服务端生成的Token的过期时长一致;
const timestamp: number = Math.floor(Date.now() / 1000) + tokenDuration;
const pendingShaStr: string = appId + appKey + userId + nonce + timestamp + role;
const appToken: string = CryptoJS.SHA256(pendingShaStr).toString(CryptoJS.enc.Hex);
let auth = new ImAuth(appToken, timestamp);
auth.role = role;
auth.nonce = nonce;
return auth;
}token相关信息说明可前往服务端集成查看,务必传入相关的参数信息,否则会登录失败。
其中role参数,有两种角色:
管理员(值:admin)
admin角色,可以创建/关闭群组,如果不需要可以设置为空。其中,非管理员的所有操作,管理员均能执行。
非管理员(值:非admin即可)
不可执行需要管理员权限的操作,若执行会返回失败。
登录并等待结果
let user = new ImUser(userId);
let auth = this.getTokenAuth(userId, "your_role");
let loginReq = new ImLoginReq(user, auth);
AliVCIMEngine.getInstance().login(loginReq)
.then(() => {
// 登录成功
})
.catch((error: ImError) => {
// 登录失败
});登出
AliVCIMEngine.getInstance().logout()
.then(() => {
// 登出成功
})
.catch((error: ImError) => {
// 登出失败
});反初始化
移除监听器。
AliVCIMEngine.getInstance().removeSdkListener(engineListener);
AliVCIMEngine.getInstance().getGroupManager()?.removeGroupListener(groupListener);
AliVCIMEngine.getInstance().getMessageManager()?.removeMessageListener(messageListener);反初始化SDK。
AliVCIMEngine.getInstance().unInit();消息服务
非管理员操作
监听消息
// 添加消息监听器
let messageListener: AliVCIMMessageListener = new AliVCIMMessageListener()
.setOnReceiveGroupMessageListener((groupId: string, message: ImGroupMessage) => {
// 收到群组消息
})
.setOnDeleteMessageListener((groupId: string, messageId: string): void => {
// 群组消息撤回通知
})
.setOnReceiveC2cMessageListener( (message: ImMessage) => {
// 收到C2C消息
});
AliVCIMEngine.getInstance().getMessageManager()?.addMessageListener(messageListener);发送单聊消息
let content = new ImMessageContent(this._messageType/*自定义消息类型*/, data/*消息内容*/);
content.setMessageLevel(ImMessageLevel.NORMAL); // 设置消息等级,默认为NORMAL,使用详情参见【消息分级限流】说明
let sendMessageOption = new ImSendMessageOption();
sendMessageOption.enableAudit(false); // 设置是否开启安全审核,默认不开启
let req = new ImSendMessageToUserReq(receiverId/*目标用户ID*/, content, sendMessageOption);
// 需确保对方在线,否则会返回错误码424,此时建议待对方上线后再重发
AliVCIMEngine.getInstance().getMessageManager()?.sendC2cMessage(req)
.then((rsp: ImSendMessageToUserRsp) => {
this.showToast(`私聊成功:消息ID=${rsp.messageId}`);
})
.catch((error: ImError) => {
this.showToast(`私聊失败:errorCode=${error.errorCode}, errorMsg=${error.errorMsg}`);
});发送群聊消息
let content = new ImMessageContent(this._messageType/*自定义消息类型*/, data/*消息内容*/);
content.setMessageLevel(ImMessageLevel.NORMAL); // 设置消息等级,默认为NORMAL,使用详情参见【消息分级限流】说明
let sendMessageOption = new ImSendGroupMessageOption();
sendMessageOption.enableAudit(false); // 设置是否开启安全审核,默认不开启;
sendMessageOption.enableStorage(false); // 设置是否存储消息,默认不存储;若开启,发送的消息会存储在数据库,「查询历史消息」或者「查询消息列表」时会返回该消息;
sendMessageOption.enableCache(false); // 设置是否缓存消息,默认不缓存;若开启,发送的消息会缓存在内存中(注意:只缓存最近50条),「查询最近消息列表」时会返回该消息;
sendMessageOption.enableMuteCheck(true); // 设置是否开启禁言检测,默认开启;若不开启,则发送的消息不受禁言的限制;
sendMessageOption.setRepeatCount(1); // 设置消息计数增长值,默认为1,主要用于聚合同类型消息,例如点赞消息场景,即可以聚合n次点赞后再返送1条消息,repeatCount设置为n
let req = new ImSendMessageToGroupReq(groupId/*目标群组ID*/, content, sendMessageOption);
// 需确保已经加入群成功(即在AliVCIMGroupInterface.joinGroup回调成功之后),再发送群组消息,否则会返回错误码425
AliVCIMEngine.getInstance().getMessageManager()?.sendGroupMessage(req)
.then((rsp: ImSendMessageToGroupRsp) => {
this.showToast(`发送消息成功:消息ID=${rsp.messageId}`);
})
.catch((error: ImError) => {
this.showToast(`发送消息失败:errorCode=${error.errorCode}, errorMsg=${error.errorMsg}`);
});查询最近消息列表
let req = new ImListRecentMessageReq(groupId/*目标群组ID*/);
AliVCIMEngine.getInstance().getMessageManager()?.listRecentMessage(req)
.then((rsp: ImListRecentMessageRsp) => {
this.showToast(`查询最近消息列表成功: 共${rsp.messageList.length}条`);
this.logInfo('消息列表:' + JSON.stringify(rsp, null, 2));
})
.catch((error: ImError) => {
this.showToast(`查询最近消息列表失败:errorCode=${error.errorCode}, errorMsg=${error.errorMsg}`);
});查询历史消息
let req = new ImListHistoryMessageReq(this._groupId/*目标群组ID*/, 88888/*目标消息类型*/);
req.nextPageToken = 231231; // 不传时表示第一页,遍历时服务端会返回下一页Token,客户端获取下一页时应带上
req.sortType = ImSortType.ASC; // 返回消息的顺序;默认为时间递增
req.pageSize = 10; // 每次请求返回的最大消息数; 范围:10~30
req.beginTime = 0; // 按时间范围遍历,开始时间,不传时表示最早时间,单位:秒
req.endTime = 0; // 按时间范围遍历,结束时间,不传时表示最晚时间,单位:秒
AliVCIMEngine.getInstance().getMessageManager()?.listHistoryMessage(req)
.then((rsp: ImListHistoryMessageRsp) => {
this.showToast(`查询历史消息成功:共${rsp.messageList.length}`);
this.logInfo('消息列表:' + JSON.stringify(rsp, null, 2));
this._lastMsgPageToken = rsp.nextPageToken;
})
.catch((error: ImError) => {
this.showToast(`查询历史消息失败:errorCode=${error.errorCode}, errorMsg=${error.errorMsg}`);
});撤回消息
let req = new ImDeleteMessageReq(groupId/*目标群组ID*/, messageId/*目标消息ID*/);
AliVCIMEngine.getInstance().getMessageManager()?.deleteMessage(req)
.then(() => {
this.showToast(`撤回消息成功`);
})
.catch((error: ImError) => {
this.showToast(`撤回消息失败:errorCode=${error.errorCode}, errorMsg=${error.errorMsg}`);
});管理员操作
查询消息列表
//该接口(仅限群主/群管理员操作)
let req = new ImListMessageReq(this._groupId/*目标群组ID*/, 88888/*目标消息类型*/);
req.nextPageToken = 231231; // 不传时表示第一页,遍历时服务端会返回下一页Token,客户端获取下一页时应带上
req.sortType = ImSortType.ASC; // 返回消息的顺序;默认为时间递增
req.pageSize = 10; // 每次请求返回的最大消息数; 范围:10~30
req.beginTime = 0; // 按时间范围遍历,开始时间,不传时表示最早时间,单位:秒
req.endTime = 0; // 按时间范围遍历,结束时间,不传时表示最晚时间,单位:秒
AliVCIMEngine.getInstance().getMessageManager()?.listMessage(req)
.then((rsp: ImListMessageRsp) => {
this.showToast(`查询消息列表成功: 共${rsp.messageList.length}条`);
this.logInfo('消息列表:' + JSON.stringify(rsp, null, 2));
this._lastMsgPageToken = rsp.nextPageToken;
})
.catch((error: ImError) => {
this.showToast(`查询消息列表失败:errorCode=${error.errorCode}, errorMsg=${error.errorMsg}`);
});群组服务
非管理员操作
监听群组状态
// 添加群组监听器
let groupListener: AliVCIMGroupListener = new AliVCIMGroupListener()
.setOnMemberChangeListener((changeInfo: ImGroupMemberChangeInfo) => {
// 群成员变化通知
})
.setOnGroupExitListener((groupId: string, reason: number) => {
// 退出群组通知: 1群被解散 2被踢出来
})
.setOnMuteChangeListener((muteStatus: ImGroupMuteStatus) => {
// 群组禁言状态变化通知
})
.setOnInfoChangeListener((infoStatus: ImGroupInfoStatus) => {
// 群组信息变化通知
});
AliVCIMEngine.getInstance().getGroupManager()?.addGroupListener(groupListener);加入群组
let req = new ImJoinGroupReq(groupId/*目标群组ID*/);
AliVCIMEngine.getInstance().getGroupManager()?.joinGroup(req)
.then((rsp: ImJoinGroupRsp) => {
this._groupId = groupId;
this.showToast('加入群组成功');
this.logInfo('群组信息:' + JSON.stringify(rsp, null, 2));
})
.catch((error: ImError) => {
this.showToast(`加入群组失败:errorCode=${error.errorCode}, errorMsg=${error.errorMsg}`);
this.exitCurrentPage();
});离开群组
let req = new ImLeaveGroupReq(groupId/*目标群组ID*/);
AliVCIMEngine.getInstance().getGroupManager()?.leaveGroup(req)
.then(() => {
this.showToast('退出群组成功');
})
.catch((error: ImError) => {
this.showToast(`退出群组失败:errorCode=${error.errorCode}, errorMsg=${error.errorMsg}`);
});查询群组信息
let req = new ImQueryGroupReq(groupId/*目标群组ID*/);
AliVCIMEngine.getInstance().getGroupManager()?.queryGroup(req)
.then((rsp: ImQueryGroupRsp) => {
this.showToast(`查询群组成功: ${rsp.groupInfo.groupId}`);
this.logInfo('群组信息:' + JSON.stringify(rsp.groupInfo, null, 2));
})
.catch((error: ImError) => {
this.showToast(`查询群组失败:errorCode=${error.errorCode}, errorMsg=${error.errorMsg}`);
});查看最近群组成员
如果当前群组为超级大群,则不支持查询用户列表,即该接口不可用。
let req = new ImListRecentGroupUserReq(groupId/*目标群组ID*/);
AliVCIMEngine.getInstance().getGroupManager()?.listRecentGroupUser(req)
.then((rsp: ImListRecentGroupUserRsp) => {
this.showToast(`列出最近群组用户成功: 共${rsp.total}人`);
this.logInfo('用户列表:' + JSON.stringify(rsp, null, 2));
})
.catch((error: ImError) => {
this.showToast(`列出最近群组用户失败:errorCode=${error.errorCode}, errorMsg=${error.errorMsg}`);
});管理员操作
创建群组
您最多可以创建5,000个群组(已关闭的群组不计入此额度)。为了确保资源的有效利用,请务必及时关闭不再使用的群组。
let req = new ImCreateGroupReq(groupId/*目标群组ID*/);
req.groupName = "测试群组"; // 群组名称
req.groupMeta = "用于测试场景"; // 自定义拓展信息
AliVCIMEngine.getInstance().getGroupManager()?.createGroup(req)
.then((rsp: ImCreateGroupRsp) => {
this.showToast(`创建群组成功: groupId=${rsp.groupId}, alreadyExist=${rsp.alreadyExist}`);
})
.catch((error: ImError) => {
this.showToast(`创建群组失败:errorCode=${error.errorCode}, errorMsg=${error.errorMsg}`);
});删除群组
let req = new ImCloseGroupReq(groupId/*目标群组ID*/);
AliVCIMEngine.getInstance().getGroupManager()?.closeGroup(req)
.then(() => {
this.showToast('关闭群组成功');
})
.catch((error: ImError) => {
this.showToast(`关闭群组失败:errorCode=${error.errorCode}, errorMsg=${error.errorMsg}`);
});修改群组
let req = new ImModifyGroupReq(this._groupId/*目标群组ID*/);
req.forceUpdateGroupMeta = false; // 为true表示强制刷新groupMeta信息,若groupMeta为空则表示清空;为false,则只有groupMeta不空才更新groupMeta信息
req.groupMeta = "";
req.forceUpdateAdmins = false; // 为true表示强制刷新admins信息,若admins为空则表示清空;为false,则只有admins不空才更新admins信息
req.admins = [];
AliVCIMEngine.getInstance().getGroupManager()?.modifyGroup(req)
.then(() => {
this.showToast('修改群组成功');
})
.catch((error: ImError) => {
this.showToast(`修改群组失败:errorCode=${error.errorCode}, errorMsg=${error.errorMsg}`);
});查询群组成员
let req = new ImListGroupUserReq(this._groupId/*群组ID*/);
req.sortType = ImSortType.ASC; // 排序方式:ASC-先加入优先,DESC-后加入优先
req.nextPageToken = 0; // 默认表示第一页;遍历时服务端会返回下一页的Token,获取取下一页时应带上
req.pageSize = 30; // 每次请求返回的最大人数;最大不超过50
AliVCIMEngine.getInstance().getGroupManager()?.listGroupUser(req)
.then((rsp: ImListGroupUserRsp) => {
this.showToast(`列出群组用户成功: 共${rsp.userList.length}人`);
this.logInfo('用户列表:' + JSON.stringify(rsp, null, 2));
this._lastUserPageToken = rsp.nextPageToken;
})
.catch((error: ImError) => {
this.showToast(`列出群组用户失败:errorCode=${error.errorCode}, errorMsg=${error.errorMsg}`);
});全体禁言
let req = new ImMuteAllReq(groupId/*目标群组ID*/);
AliVCIMEngine.getInstance().getGroupManager()?.muteAll(req)
.then(() => {
this.showToast('全员禁言成功');
})
.catch((error: ImError) => {
this.showToast(`全员禁言失败:errorCode=${error.errorCode}, errorMsg=${error.errorMsg}`);
});取消禁言
let req = new ImCancelMuteAllReq(groupId/*目标群组ID*/);
AliVCIMEngine.getInstance().getGroupManager()?.cancelMuteAll(req)
.then(() => {
this.showToast('取消全员禁言成功');
})
.catch((error: ImError) => {
this.showToast(`取消全员禁言失败:errorCode=${error.errorCode}, errorMsg=${error.errorMsg}`);
});禁言指定用户
let req = new ImMuteUserReq(groupId/*目标群组ID*/, [userId]/*目标禁言的用户ID列表*/);
AliVCIMEngine.getInstance().getGroupManager()?.muteUser(req)
.then(() => {
this.showToast('禁言用户成功');
})
.catch((error: ImError) => {
this.showToast(`禁言用户失败:errorCode=${error.errorCode}, errorMsg=${error.errorMsg}`);
});取消用户禁言
let req = new ImCancelMuteUserReq(groupId/*目标群组ID*/, [userId]/*目标取消禁言的用户ID列表*/);
AliVCIMEngine.getInstance().getGroupManager()?.cancelMuteUser(req)
.then(() => {
this.showToast('取消禁言用户成功');
})
.catch((error: ImError) => {
this.showToast(`取消禁言用户失败:errorCode=${error.errorCode}, errorMsg=${error.errorMsg}`);
});查询禁言用户
let req = new ImListMuteUsersReq(groupId/*目标群组ID*/);
AliVCIMEngine.getInstance().getGroupManager()?.listMuteUsers(req)
.then((rsp: ImListMuteUsersRsp) => {
this.showToast(`列出禁言用户成功: 全员禁言=${rsp.muteAll}`);
this.logInfo('禁言用户列表:' + JSON.stringify(rsp, null, 2));
})
.catch((error: ImError) => {
this.showToast(`列出禁言用户失败:errorCode=${error.errorCode}, errorMsg=${error.errorMsg}`);
});其他
辅助API接口,用于一些业务场景的判断和处理。
// 判断SDK是否已初始化,业务侧可用此接口判断SDK是否已经实例化
AliVCIMEngine.getInstance().isInit();
/**
* 获取当前的登录状态
* 1、只有登出状态「LOGOUT」才能执行登录操作
* 2、只有登录状态「LOGIN」才能执行入群等操作
*/
AliVCIMEngine.getInstance().getCurrentState();