Cocos2d-x开发集成

网易云通信服务概要

网易云通信以提供客户端SDK和服务端OPEN API 的形式提供即时通讯云服务。开发者只需想好APP 的创意及UI 展现方案,根据APP 业务需求选择网易云通信的相应功能接入即可。Cocos2d-x SDK 支持Android、Windows(x86)、iOS平台。

注: Unreal Engine 支持使用c++进行开发,该SDK同样可以集成到Unreal Engine开发的游戏中。

开发准备

SDK内容

libs 目录根据平台存放云信动态库,nim 目录下是云信SDK对外提供的接口头文件。云信SDK提供 c 和 c++ 接口文件,c接口文件位于nim/c目录中,c++接口文件位于nim/cpp目录中,在cocos-2dx游戏中推荐使用c++ 接口,开发效率更高。接口根据功能模块存放在不同的目录中:

nim: 即时通信相关接口

nim_audio :语音功能相关接口

nim_chatroom: 聊天室相关接口

快速接入SDK

  1. 下载并导入SDK

    • 下载网易云信cocos-2dx SDK压缩包,目前仅支持c/c++。

    • 创建Cocos2d-x 工程

      使用 cocos 命令创建工程 cocos new -p <package name\> -l cpp <project name\>,创建工程目录。 如:

      cocos new -p org.cocos2dx.nim_cocos2d -l cpp nim_cocos2d

    • 导入SDK

      将云信SDK压缩包解压后拷贝到创建的cocos项目内。

  2. Android 集成

    • 使用 Android Studio 打开创建的 cocos 项目中 proj.android-studio 目录

    • 导入jar包

      在运行SDK目录中libs/Android 目录下包含两个jar包:nim_sdk.jar 和 nim_audio.jar。nim_sdk.jar 是云信SDK必须的,需要确保正确引入项目中,nim_audio.jar 在使用语音功能时会用到。

    • 权限配置

      云信SDK需要使用以下权限:

        <!-- Enable internet for app!!!. -->
        <uses-permission android:name="android.permission.INTERNET" />
        <!-- Getting the state of internet for app!!!. -->
        <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
        <!-- write or read user data file for app!!!. -->
        <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
        <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
        <uses-permission android:name="android.permission.READ_LOGS" />
        <uses-permission android:name="android.permission.READ_PHONE_STATE" />
        <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
        <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
        <uses-permission android:name="android.permission.RECORD_AUDIO" />
        <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/>

      需要在AndroidManifest.xml文件中确保已经配置了这些权限。

    • 编译链接

      修改 Android Studio工程目录中的Android.mk文件,将云信SDK引入工程,假设SDK目录为nim_cocos2d-x_sdk。

      加入预编译动态库:

        #import nim c library
        include $(CLEAR_VARS)
        include $(MY_ROOT_PATH)/nim_cocos2d-x_sdk/libs/Android/Android.mk

      编译云信SDK c++ 代码:

        # _NIM_CPP_LIBRARY_BEGIN
      
        include $(CLEAR_VARS)
        include $(MY_ROOT_PATH)/nim_cocos2d-x_sdk/nim/cpp/third_party/jsoncpp/Android.mk
      
        include $(CLEAR_VARS)
        include $(MY_ROOT_PATH)/nim_cocos2d-x_sdk/nim/cpp/nim/Android.mk
      
        include $(CLEAR_VARS)
        include $(MY_ROOT_PATH)/nim_cocos2d-x_sdk/nim/cpp/nim_chatroom/Android.mk
      
        include $(CLEAR_VARS)
        include $(MY_ROOT_PATH)/nim_cocos2d-x_sdk/nim/cpp/nim_audio/Android.mk
      
        # _NIM_CPP_LIBRARY_END

      链接:

        LOCAL_STATIC_LIBRARIES += jsoncpp
        LOCAL_SHARED_LIBRARIES += nim
        LOCAL_SHARED_LIBRARIES += nim_chatroom
        LOCAL_SHARED_LIBRARIES += nim_audio
        LOCAL_SHARED_LIBRARIES += fjni_wrapper
        LOCAL_STATIC_LIBRARIES += nim_cpp nim_chatroom_cpp nim_audio_cpp

      以上代码中包含了即时通信、聊天室和语音模块,用户可以根据需要移除不需要的SDK功能。在开发时还需要设置LOCAL_C_INCLUDES 加入正确的头文件包含路径。

    • Proguard混淆配置

      请务必不要混淆网易云信SDK,如果工程设置了代码混淆,在工程的Proguard混淆文件proguard-rules.pro中加入以下配置:

    • 添加初始化调用:

      修改 AppActivity.java 文件,在使用网易云信SDK之前,需要调用nim_sdk.jar包里的NIMSDK.init初始化接口进行必要的初始化工作,如下:

  3. Windows 集成

    • 引入云信SDK c++ 项目

      在cocos工程中添加nim\cpp目录中各功能模块的的项目文件,并在cocos主项目中添加对这些项目的引用。

    • 配置正确的头文件包含目录

    • 在程序运行时需要将SDK目录libs\Windows\x86下的dll文件拷贝到程序执行目录,可以根据功能模块选择需要的dll,不需要的模块可以不拷贝。

  4. iOS 集成

    SDK 只支持 Xcode7.x,不支持 Xcode6.x ,支持 iOS 7.0 以上版本。

    • 集成 SDK

      1) 将压缩包解压后,将nim_cocos2d-x_sdk目录拷贝到相应Cocos2d-x工程目录,找到~/nim_cocos2d-x_sdk/libs/iOS,拖到工程的 FrameWorks下,同时添加下系统库:libz.tbd(或libz.dylib)分别将~/nim_cocos2d_sdk/nim/cpp各子文件夹(nim、nim_audio、nim_chatroom)下找到需要的Xcode project,并将其拖入到Cocos Workspace中。若不需要音频和聊天室相关的功能,则不必添加其相应project.如图

      在cocos工程中链接相应的library。

      2) 设置相应的Header Search Paths,如图:

      3) 设置相应的Library Search Paths,如图:

      编译通过则集成工作就完成了。

接口说明

初始化SDK

nim::SDKConfig config;
//组装SDK能力参数(必填)
config.database_encrypt_key_ = "Netease";     //string(db key必填,目前只支持最多32个字符的加密密钥!建议使用32个字符)
config.sdk_log_level_ = ;                     //bool 选填,是否需要预下载附件(图片和语音),SDK默认预下载,如果有对带宽流量有较高要求的请关闭该选项,改为上层开发者按需下载附件文件
config.preload_image_quality_ = ;             //int 预下载图片质量,选填,范围0-100
config.preload_image_resize_ = ;             //string 预下载图片基于长宽做内缩略,选填,比如宽100高50,则赋值为100x50,中间为字母小写x
config.sync_session_ack_ = true;             //bool 选填,消息已读未读是否多端同步,默认true
config.login_max_retry_times_ = ;             //int 登录重试最大次数,如需设置建议设置大于3次,默认填0,SDK默认设置次数

//组装SDK独立部署的服务器配置(选填)
config.use_private_server_ = true;
config.rsa_public_key_module_ = "http://xxx";
config.default_nos_download_address_.push_back("http://xxx.xxx.xxx.xxx:xxxx");
config.lbs_address_ = "http://xxx";
config.nos_lbs_address_ = "http://xxx";
config.default_link_address_.push_back("http://xxx.xxx.xxx.xxx:xxxx");
config.default_nos_upload_address_.push_back("http://xxx.xxx.xxx.xxx:xxxx");
config.default_nos_access_address_.push_back("http://xxx.xxx.xxx.xxx:xxxx");

bool ret = nim::Client::Init("45c6af3c98409b18a84451215d0bdd6e", "Netease", "", config); // 载入网易云通信sdk,初始化安装目录和用户目录,第一个参数是appkey(此处填写的是测试示例)

在程序退出前,调用接口nim::Client::Cleanup() 进行NIM SDK 的清理工作,对于清理工作的注意事项请查看后续的"登出/退出和清理SDK"章节。

登录与登出

登录集成必读

首次登录

SDK 登录后会同步群信息,离线消息,漫游消息,系统通知等数据,所以首次登录前需要 提前注册所需的全局广播类通知的回调 (具体说明请参阅API 文档),以下以注册最近会话变更通知回调为例:

void OnSessionChangeCallback(nim::NIMResCode rescode, const nim::SessionData& data, int total_unread_counts)
{
    if (rescode != nim::kNIMResSuccess)
    {
        return;
    }

    switch (data.command_)
    {
    case nim::kNIMSessionCommandAdd:
    case nim::kNIMSessionCommandUpdate:
    case nim::kNIMSessionCommandMsgDeleted:
    break;
    ···
    }
}

void foo()
{
    //注册全局会话列表变更通知函数
    nim::Session::RegChangeCb(&OnSessionChangeCallback);
}

登录

void OnLoginCallback(const nim::LoginRes& login_res, const void* user_data)
{
    if (login_res.res_code_ == nim::kNIMResSuccess)
    {
        if (login_res.login_step_ == nim::kNIMLoginStepLogin)
        {
            ···
        }
    }
    else
    {
        ···
    }
}

void foo()
{
    nim::Client::Login(app_key, "app acount", "token", &OnLoginCallback);
}

自动重新登录

SDK 在网络连接断开后,会监听网络状况,如果网络状况好转,那么SDK 会进行重新登录,登录结果可以在注册的重新登录回调函数中处理。

注意 自动重新登录的机制可能会失效:

如果重新登录回调函数返回的的错误号既不是kNIMResSuccess,也不是网络错误相关错误号(kNIMResTimeoutError或者kNIMResConnectionError),那么说明自动重新登录的机制已经失效,需要开发者退回到登录界面进行手动重新登录。

手动重新登录

SDK 在重新登录失败后,可以由用户手动调用重新登录接口nim::Client::Relogin()

登出/退出和清理SDK

执行接口nim::Client::Logout进行登出或退出,登出或退出的过程因为涉及到和服务器的交互以及需要将缓存中的数据持久化到本地,因此根据实际情况回调通知会有延迟。清理SDK必须在完全退出(收到退出回调)后执行。

void LoginCallback::OnLogoutCallback(nim::NIMResCode res_code)
{
    //如果是退出程序
    nim::Client::Cleanup();
    ...

    //如果是登出到登录界面,这里不能执行清理SDK工作,否则会报错
    ...
}

void foo()
{
    //登出到登录界面(nim::kNIMLogoutChangeAccout) 或者 退出程序(nim::kNIMLogoutAppExit)
    nim::Client::Logout(nim::kNIMLogoutAppExit, &OnLogoutCallback);
}

断网重连通知

通过接口nim::Client::RegDisconnectCb来注册连接断开回调函数,断开连接后根据实际需要做后续处理(比如提示用户)。

被踢出通知

通过接口nim::Client::RegKickoutCb来注册被踢出回调函数,当收到此通知时,一般是退出程序然后回到登录窗口,回调函数参数中详细说明了被踢出的原因。

注意:聊天室被踢出通知不是通过这个接口监听的,具体请看下文 聊天室开发说明

多端登录

通过调用nim::Client::RegMultispotLoginCb来注册多端登录通知的回调函数,当用户在某个客户端登录时,其他没有被踢掉的端会触发这个回调函数,并携带当前时间登录的设备列表的数据。

网易云通信内置踢人策略为:移动端(Android,iOS)互踢,桌面端(PC,Web)互踢,移动端和桌面端共存。

如果当前的互踢策略无法满足业务需求的话,可以联系我们取消内置互踢,根据多端登录的回调和当前的设备列表,判断本设备是否需要被踢出。如果需要踢出,直接调用登出接口并在界面上给出相关提示即可。

注意:聊天室多端登录相关的通知不是通过这个接口监听的,具体请看下文 聊天室开发说明

网易云通信 ID

网易云通信的每个用户由网易云通信ID唯一标识。网易云通信ID是由用户帐号和token成功同步到网易云通信服务器后自动分配的,可以通过 NIMMessage 的 from 字段获取消息发送方的网易云通信 ID。

基础消息功能

消息功能概述

SDK支持文本、图片、音频、视频、通知消息、提醒消息、文件消息和自定义消息等多种种类型消息。

发送消息

发送消息状态通过nim::Talk::RegSendMsgCb注册的全局回调获取。

以发送图片消息的为例:

void SessionBox::SendImage( const std::wstring &src )
{
    nim::IMMessage msg;
    PackageMsg(msg);
    msg.type_ = nim::kNIMMessageTypeImage;

    std::wstring filename = nbase::UTF8ToUTF16(msg.client_msg_id_);
    std::wstring dest = GetUserImagePath() + filename;
    GenerateUploadImage(src, dest);
    msg.local_res_path_ = nbase::UTF16ToUTF8(dest);

    nim::IMImage img;
    img.md5_ = GetFileMD5(dest);
    img.size_ = (long)nbase::GetFileSize(dest);

    Gdiplus::Image image(dest.c_str());
    if (image.GetLastStatus() != Gdiplus::Ok)
    {
      assert(0);
    }
    else
    {
      img.width_ = image.GetWidth();
      img.height_ = image.GetHeight();
    }

    msg.attach_ = img.ToJsonString();

    std::string json_msg = nim::Talk::CreateImageMessage(msg.receiver_accid_, msg.session_type_, msg.client_msg_id_, img, msg.local_res_path_, msg.timetag_);
    nim::Talk::SendMsg(json_msg);
}

可以通过nim:Talk::CreateXXXMessage接口创建消息,省去手动拼接json字符串的麻烦;如果要停止正在发送中的消息(目前只支持发送文件消息时的终止),可以调用nim::Talk::StopSendMsg接口实现。

此外,通过类MessageSetting提供了以下消息属性设置:

消息多端推送开关

void OnMultiportPushConfigChange(int rescode, bool switch_on)
{
    if (rescode == nim::kNIMResSuccess)
    {
        ···
    }
}

void OnInit()
{
    //登录前注册的全局回调获取当前多端推送开关状态
    nim::Client::RegSyncMultiportPushConfigCb(&OnMultiportPushConfigChange);
}

void SetMultiportConfig(bool switch_on)
{
    //当PC/Web端在线时,可以配置消息是否推送给移动端
    nim::Client::SetMultiportPushConfigAsync(switch_on, &OnMultiportPushConfigChange);
}

void GetMultiportConfig()
{
    //主动获取当前多端推送开关状态
    nim::Client::GetMultiportPushConfigAsync(&OnMultiportPushConfigChange);
}

接收消息

通过nim::Talk::RegReceiveCb提前注册好接收消息的回调函数。在接收到消息时,如果是图片、语音消息SDK 会自动下载,然后通过nim::NOS::RegDownloadCb注册的NOS回调函数通知。如果下载失败,则调用接口nim::NOS::FetchMedia重新下载。

void OnReceiveMsgCallback(const nim::IMMessage& message)
{
    std::string id = GetSessionId(message);

    if (message.feature_ == nim::kNIMMessageFeatureDefault)
    {
        ···
    }
    else if (message.feature_ == nim::kNIMMessageFeatureSyncMsg || message.feature_ == nim::kNIMMessageFeatureRoamMsg)
    {
        ···
    }
    else if (message.feature_ == nim::kNIMMessageFeatureCustomizedMsg)
    {
        ···
    }
}

void foo()
{
    nim::Talk::RegReceiveCb(&OnReceiveMsgCallback);
}

转发消息

用户通过转发消息接口获取消息结构Json字符串,然后需要调用发送消息接口。

void foo(nim::IMMessage msg)
{
    std::string send_msg = nim::Talk::CreateRetweetMessage(msg.ToJsonString(false), QString::GetGUID(), nim::kNIMSessionTypeP2P, session_id, msg.msg_setting_, 1000 * nbase::Time::Now().ToTimeT());
    nim::Talk::SendMsg(send_msg);
}

撤回消息

用户通过nim::Talk::SendMsg发的消息可以通过该接口执行撤回操作(不支持聊天室消息),撤回操作一般有时限限制(该限制为全局的APP设置),超过限制返回508。

开发者通过注册撤回消息通知回调,接收其他端的撤回消息的通知,收到通知后SDK会标记主动在消息历史中标记该条消息为删除状态,同时开发者根据自身需求,删除界面上显示的消息,甚至插入一条提示。

void OnReceiveRecallMsgCallback(nim::NIMResCode code, const std::list<nim::RecallMsgNotify>& message)
{
    for (auto notify : message)
    {
        if (code == nim::kNIMResSuccess)
        {
            ···
        }
    }
}

void OnInit()
{
    nim::Talk::RegRecallMsgsCallback(&OnReceiveRecallMsgCallback);
}

void foo(const nim::IMMessage& msg)
{
    nim::Talk::RecallMsg(msg, "test notify when recall", &OnReceiveRecallMsgCallback);
}

最近会话

最近会话记录了与用户最近有过会话的联系人信息,包括联系人帐号、联系人类型、最近一条消息的时间、消息状态、消息缩略和未读条数等信息。

void OnQuerySessionListCallback(int unread_count, const nim::SessionDataList& session_list)
{
    ···
}

void foo()
{
    nim::Session::QueryAllRecentSessionAsync(&OnQuerySessionListCallback);
}

在收发消息的同时,SDK会更新对应聊天对象的最近联系人资料。当有消息收发时,SDK 会发出最近联系人更新通知,使用nim::Session::RegChangeCb注册通知回调函数。

SDK 也提供了删除单个和删除全部最近联系人的接口。需要注意:删除会话项时本地和服务器会同步删除!

自定义消息

除了内建消息类型外,SDK 也支持收发自定义消息类型。和透传消息一样,SDK 也不会解析自定义消息的具体内容,解释工作由开发者完成。

消息已读回执

在会话界面中调用发送已读回执的接口并传入最后一条消息,即表示这之前的消息都已读,对端将收到此回执。

在发送端将需要发送的消息作为参数传入,而接收端可以通过解析result中的msg_timetag来得知发送端当前已读时间戳。此功能仅在 P2P 消息中有效。重复发送和通过无效消息构造的已读回执都将被 SDK 忽略。

多媒体消息

语音消息录制和播放

权限配置

  需要配置 AndroidManifest.xml,添加权限如下:

    <uses-permission android:name="android.permission.RECORD_AUDIO" /> 
    <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/> 

另外开发者在android系统下需要调用 System.loadLibrary("nim_audio")加载库文件。
若录音影响游戏音效之类的问题,需要每次调用sdk录音或者播放,结束以后,需要开发者调用OC接口将 audio session的category设置一下

    [AVAudioSession sharedInstance] setCategory:xxxxxxxx]
    /*Use this category for background sounds such as rain,car engine noise,etc.Mixes with other music.*/
    AVF_EXPORT NSString *const AVAudioSessionCategoryAmbient ;
    /*Use this category for background sounds.other music will stop playing.*/
    AVF_EXPORT NSString *const AVAudioSessionCategorySoloAmbient;

接口说明

语音消息录制和播放等接口封装在Audio类中,相关的C++接口代码在cpp/nim_audio/api子目录下,具体接口说明请参考api文档说明。

语音转文字

将接收到的语音消息转换为文本内容。

void foo(nim::IMMessage &msg_data)
{
    nim::IMAudio audio;
    nim::Talk::ParseAudioMessageAttach(msg_data, audio);
    nim::AudioInfo audio_info;
    audio_info.samplerate_ = "16000";
    audio_info.url_ = audio.url_;
    audio_info.duration_ = audio.duration_;
    audio_info.mime_type_ = audio.file_extension_;

    nim::Tool::GetAudioTextAsync(audio_info, ToWeakCallback([this](int rescode, const std::string& text) {
        if (rescode == nim::kNIMResSuccess) {
            ···
        }
        else {
            ···
        }
    }));
}

群组功能

群组功能概述

网易云通信SDK提供了普通群(kNIMTeamTypeNormal),以及高级群(kNIMTeamTypeAdvanced)两种形式的群聊功能。高级群拥有更多的权限操作,两种群聊形式在共有操作上保持了接口一致。在群组中,当前会话的ID就是群组的ID。

群聊消息

群聊消息收发和管理与双人聊天完全相同,仅在消息类型(kNIMMsgKeyToType)上做了区分。

获取群组

SDK 在程序启动时会对本地群信息进行同步,所以只需要调用本地缓存接口获取群就可以了。SDK 提供了批量获取自己的群接口、以及根据单个群 ID 查询的接口。同样SDK 也提供了远程获取群信息的接口。

创建群组

网易云通信群组分为两类:普通群和高级群,两种群组的消息功能都是相同的,区别在于管理功能。

普通群所有人都可以拉人入群,除群主外,其他人都不能踢人。

固定群则拥有完善的成员权限体系及管理功能。创建群的接口相同,传入不同的类型参数即可。

void OnTeamEventCallback(const nim::TeamEvent& result)
{
    ...
}

void foo()
{
    std::list<std::string> id_list;
    id_list.push_back("test1");
    id_list.push_back("test2");

    nim::TeamInfo tinfo;
    tinfo.SetName("test");
    tinfo.SetType(nim::kNIMTeamTypeNormal);
    nim::Team::CreateTeamAsync(tinfo, id_list, "", &OnTeamEventCallback);
}

加入群组

用户可以通过被动接受邀请和主动加入两种方式进入群组。

请求完成后,如果是普通群,被邀请者将直接入群;如果是高级群,网易云通信服务器会下发一条系统消息到目标用户,目标用户可以选择同意或者拒绝入群。

void OnTeamEventCallback(const nim::TeamEvent& result)
{
    ···
}

void foo()
{
    const std::list<std::string> friend_list;
    friend_list.push_back("test1");
    friend_list.push_back("test2");
    nim::Team::InviteAsync("123445", friend_list, "", &OnTeamEventCallback);
}

踢人出群

普通群仅拥有者可以踢人,高级群拥有者和管理员可以踢人,且管理员不能踢拥有者和其他管理员。

void TeamEventCb(const nim::TeamEvent& team_event)
{
    ···
}

void foo()
{
    std::list<std::string> uids_list;
    uids_list.push_back("test_user");
    nim::Team::RejectJoinApplyAsync("12345", uids_list, &TeamEventCb);
}

主动退群

除拥有者外,其他用户均可以主动退群:

void TeamEventCb(const nim::TeamEvent& team_event)
{
    ···
}

void foo()
{
    nim::Team::LeaveAsync("12345", &TeamEventCb);
}

编辑群组资料

普通群所有人均可以修改群名,高级群仅拥有者和管理员可修改群名及其他群资料。

void OnUpdateBroadCb(const nim::TeamEvent& team_event)
{
    if (team_event.res_code_ == 200)
    {
        ···
    }
}

void foo()
{
    Json::Value broad;
    broad["title"] = "title";
    broad["content"] = "内容";
    broad["creator"] = "test_user";

    Json::Value broads;
    broads.append(broad);

    Json::FastWriter writer;
    nim::TeamInfo param;
    param.SetAnnouncement(writer.write(broads));
    param.SetTeamID("tid_");

    nim::Team::UpdateTeamInfoAsync("tid_", param, &OnUpdateBroadCb);
}

管理群组权限

高级群群主可以对群进行权限管理,权限管理包括:

群组成员

解散群

群主可以调用接口解散所拥有的群:

void OnTeamEventCallback(const nim::TeamEvent& result)
{
    ···
}

foo()
{
    nim::Team::DismissAsync("tid_", &OnTeamEventCallback);
}

群组通知

用户在创建群或者进入群成功之后,任何关于群的变动(群组资料变动,群成员变动等),网易云通信服务器都会下发一条群通知消息。APP 可以通过注册全局回调函数接受群组通知。

void OnTeamEventCallback(const nim::TeamEvent& result)
{
    ···
}

foo()
{
    nim::Team::RegTeamEventCb(&OnTeamEventCallback);
}

群成员禁言

历史记录

本地记录

导入导出

云端记录

系统通知

除消息通道外,SDK 还提供系统通知这种通道用于消息之外的通知分发。目前有两种类型:内置系统通知和自定义系统通知。所有的系统通知(包括内置系统通知和自定义系统通知)都是通过nim::SystemMsg::RegSysmsgCb注册的回调函数接收。为了保证整个程序逻辑的一致性,APP 需要针对不同类型的系统通知进行相应的操作。

内置系统通知

这是由SDK 预定义的通知类型,目前仅支持几种群操作的通知,如被邀请入群,SDK 负责这些通知的持久化。此外,SDK 提供了以下接口来获取和维护内置系统通知记录:

自定义系统通知

除了内置系统通知外,SDK 也额外提供了自定义系统给开发者,方便开发者进行业务逻辑的通知。这个通知既可以由客户端发起也可以由开发者服务器发起。

客户端发起的自定义通知,该类型通知格式由开发者自定义(kNIMSysMsgKeyAttach 里),SDK 仅负责发送、接收,支持在线或离线发送,且支持点对点通知和群通知,不做任何解析,也不会存储,因此也不会在聊天记录中体现,可以使用的场景例如发送正在输入的状态等。代码示例如下:

void foo()
{
    Json::Value json;
    Json::FastWriter writer;
    json["id"] = "1";

    nim::SysMessage msg;
    msg.receiver_accid_ = ;    //接收者id
    msg.sender_accid_ = ;     //自己id
    msg.client_msg_id_ = QString::GetGUID();    //本地定义的消息id
    msg.attach_ = writer.write(json);            //通知附件内容
    msg.type_ = nim::kNIMSysMsgTypeCustomP2PMsg; //通知类型

    nim::SystemMsg::SendCustomNotificationMsg(msg.ToJsonString());
}

客户端发起的自定义通知的回执结果通过APP 预先通过nim::SystemMsg::RegSysmsgCb注册的回调通知开发者。

自定义系统通知还提供了属性设置如下:

SDK 并不负责自定义通知的持久化,APP 需要根据自己的业务逻辑按需进行解析和持久化的工作。

用户名片

概述

SDK 提供了用户帐号资料管理。以下几个接口仅当选择网易云通信托管用户资料时有效,如果开发者不希望网易云通信获取自己的用户数据,则需自行维护用户资料。

用户信息变更会通过nim::User::RegUserNameCardChangedCb注册的全用户信息变更通知回调告知APP。

获取本地用户信息

void OnGetUserCard(const std::list<nim::UserNameCard> &json_result)
{
    for (auto& info : json_result)
    {
        ···
    }
}

void foo()
{
    std::list<std::string> account_list;
    account_list.push_back("test1");
    account_list.push_back("test2");
    nim::User::GetUserNameCard(account_list, &OnGetUserCard);
}

获取服务器用户信息

单次查询限制150人。

void OnGetUserCard(const std::list<nim::UserNameCard> &json_result)
{
    for (auto& info : json_result)
    {
        ···
    }
}

void foo()
{
    std::list<std::string> account_list;
    account_list.push_back("test1");
    account_list.push_back("test2");
    nim::User::GetUserNameCardOnline(account_list, &OnGetUserCard);
}

编辑用户资料

void OnUpdateMyInfo(nim::NIMResCode res) 
{
    if (res == nim::kNIMResSuccess)
    {

    }
}

void foo()
{
    nim::UserNameCard info;
    info.SetName("new_name");
    nim::User::UpdateMyUserNameCard(info, &OnUpdateMyInfo);
}

用户关系托管

SDK 提供了用户好友关系管理,以及对用户会话的消息设置。在网易云通信中,不是好友也允许聊天。好友关系如果不托管给网易云通信,开发者需要自己在应用服务器维护。

添加/被添加,请求/被请求,删除/被删除的通知以及多端同步等通知通过nim::Friend::RegChangeCb注册的好友数据变更通知回调告知APP。

好友关系

黑名单

网易云通信中,黑名单和用户关系是互相独立的,即修改用户关系不会影响黑名单关系,同时,修改黑名单也不会对用户关系进行操作。

黑名单列表有本地缓存,缓存会在手动/自动登录后与服务器自动进行同步更新。通过nim::User::RegSpecialRelationshipChangedCb注册用户关系变更通知回调获取当前数据变化。

消息提醒

易云通信中,可以单独设置是否开启某个用户的消息提醒,即对某个用户静音。静音关系和用户关系是互相独立的,修改用户关系不会影响静音关系,同时,修改静音关系也不会对用户关系进行操作。

静音名单列表有本地缓存,缓存会在手动/自动登录后与服务器自动进行同步更新。通过nim::User::RegSpecialRelationshipChangedCb注册用户关系变更通知回调获取当前数据变化。

NOS云存储服务

下载

下载资源,回调函数包括了下载结果以及下载进度,示例代码如下:

void DownloadResourceCallback(nim::NIMResCode res_code, const std::string& file_path, const std::string& call_id, const std::string& res_id)
{

}

void DownloadResourceProgressCallback(__int64 downloaded_size, __int64 file_size)
{

}

void foo(const IMMessage& msg)
{
    nim::NOS::FetchMedia(msg, &DownloadResourceCallback, &DownloadResourceProgressCallback);
}

上传

上传资源,回调函数包括了上传结果以及上传进度,示例代码如下:

void OnUploadCallback(int res_code, const std::string& url)
{

}

void foo()
{
    nim::NOS::UploadResource("D://test_file.txt", &OnUploadCallback);
}

聊天室

集成聊天室必读

聊天室功能概述

聊天室特点:

在程序启动时,调用nim_chatroom::ChatRoom::Init初始化聊天室SDK;在程序退出前,调用nim_chatroom::ChatRoom::Cleanup清理聊天室SDK。

进入聊天室

进入聊天室前,需要通过调用IM模块接口获取进入聊天室的权限,注意:这里调用的是IMSDK模块的接口

void OnChatRoomRequestEnterCallback(int error_code, const std::string& result)
{
}

void foo()
{
    //获取聊天室登录信息
    nim::PluginIn::ChatRoomRequestEnterAsync(room_id, &OnChatRoomRequestEnterCallback);
}

获取权限成功后会得到权限信息(回调函数第二个参数result),接下去再调用进入聊天室的接口。

void foo()
{
    ChatRoomEnterInfo info;
    ChatRoom::Enter(room_id_, room_enter_token, info);
}

通过ChatRoom::RegEnterCb注册回调函数获取进入聊天室的结果通知。

离开聊天室(被踢)

void foo()
{
    ChatRoom::Exit(room_id);
}

被踢出聊天室时,开发者可以通过接口ChatRoom::RegExitCb注册全局通知回调函数获取离开聊天室的原因。

除了被多端、主播和管理员踢出以外,聊天室被关闭或者被解散也会收到离开聊天室的通知。

聊天室连接情况通知

通过接口ChatRoom::RegLinkConditionCb来当前连接情况变更回调函数,开发者可以通过回调函数获取当前连接情况。

获取/更新聊天室信息

进入聊天室的回调函数中会有聊天室的信息,此外还能通过单独的接口获取聊天室信息。SDK 本地不缓存聊天室信息,只提供向服务器查询的接口,因此接口调用遵循服务器接口使用频率限制。

void OnGetChatRoomInfoCallback(__int64 room_id, int error_code, const ChatRoomInfo& info)
{
    if (error_code != nim::kNIMResSuccess || room_id != room_id_)
    {
        return;
    }
}

void foo()
{
    ChatRoom::GetInfoAsync(room_id, &OnGetChatRoomInfoCallback);
}

更新聊天室信息(需要管理员权限),目前只支持更新kNIMChatRoomInfoKeyName, kNIMChatRoomInfoKeyAnnouncement, kNIMChatRoomInfoKeyBroadcastUrl, kNIMChatRoomInfoKeyExt四个字段。

void UpdateRoomCallback(int64_t room_id, int error_code)
{

}

void foo()
{
    ChatRoomInfo info;
    info.announcement_ = ; //聊天室公告
    info.name_ = ; //聊天室名称
    ChatRoom::UpdateRoomInfoAsync(room_id, info, true, "广播通知", &UpdateRoomCallback);
}

更新自己的信息

目前只支持更新kNIMChatRoomMemberInfoKeyNick, kNIMChatRoomMemberInfoKeyAvatar, kNIMChatRoomMemberInfoKeyExt三个字段。

void UpdateMyRoleCallback(int64_t room_id, int error_code)
{

}

void foo()
{
    ChatRoomMemberInfo info;
    info.nick_ = ; //聊天室内的昵称字段
    info.avatar_ = ; //聊天室内的头像
    info.ext_ = ;  //开发者扩展字段

    Json::Value values;
    values[nim_chatroom::kNIMChatRoomUpdateMyRoleExtNeedSave] = ; //bool 是否持久化
    Json::FastWriter fw;
    std::string extension = fw.write(values);

    ChatRoom::UpdateMyRoomRoleAsync(room_id, info, true, "广播通知", &UpdateMyRoleCallback, extension);
}

聊天室消息收发

聊天室SDK 支持文本、图片、音频、视频、通知消息、提醒消息、文件消息和自定义消息等多种种类型消息。

查询消息历史

SDK 本地不缓存任何消息历史,只提供向服务器查询消息历史的接口,因此接口调用遵循服务器接口使用频率限制。

void GetMsgHistoryCallback(__int64 room_id, int error_code, const std::list<ChatRoomMessage>& msgs)
{
    if (error_code != nim::kNIMResSuccess)
        return;
}

void foo()
{
    ChatRoomGetMsgHistoryParameters history_param;
    history_param.limit_ = 10;
    history_param.start_timetag_ = 0;
    ChatRoom::GetMessageHistoryOnlineAsync(room_id, history_param, &GetMsgHistoryCallback);
}

获取聊天室在线成员

SDK 本地不缓存聊天室成员信息,只提供向服务器查询的接口,因此接口调用遵循服务器接口使用频率限制。

SDK 提供了两种获取聊天室在线成员信息的接口:

踢出在线成员

void KickMemberCallback(__int64 room_id, int error_code)
{
    if (error_code != nim::kNIMResSuccess)
        return;
}

void foo()
{
    ChatRoom::KickMemberAsync(room_id, user_account, "", &KickMemberCallback);
}

聊天室权限设置

设置管理员、设置普通成员、设置黑名单、禁言通过调用同一个接口ChatRoom::SetMemberAttributeOnlineAsync实现。

聊天室队列服务