NIM Demo PC导读

前言

这篇教程主要介绍NIM Demo的nim工程的内容,Demo的作用主要是展现sdk的功能,方便开发者快速熟悉使用sdk,但Demo的代码不一定是最佳的。Demo使用Visual Studio 2013 Update5开发(必须使用Update5版本)。

Demo源码中目录结构中的C++封装层文件夹名字尾部携带_vs2010的为vs2010创建的工程项目,对应的不携带_vs2010的为vs2013创建的工程项目,即Demo目前加载的工程,该工程依赖Demo工程。

Demo的主要功能由Demo工程本身以及UI组件工程共同完成,Demo工程和UI组件工程具有相同的目录结构。UI组件位于tool_kits\ui_component\ui_kit目录,UI组件的相关文档详见:云信UI组件

Demo源码版本历史

Demo源码版本历史

界面开发资料

网易云信Duilib使用说明

代码托管地址

网易云信Duilib使用说明

控件属性

控件属性

界面布局介绍

网易云信 DuiLib 布局功能指南

会话多窗口实现介绍

从3.0.0版本开始,Demo对会话窗口的实现进行了升级,方便开发者快速开发类似QQ,微信等多窗口模式。

会话窗口多窗口合并及分开功能及升级说明

CEF开发指南

从3.4.0版本开始,Demo增加了Cef控件,方便开发者开发Web相关的功能模块。

CEF开发指南

Duillib高分屏(高DPI)支持

从3.5.0版本开始,Duilib增加了对高分屏的支持,方便在用户设置了DPI后保持软件界面的清晰效果。

Duillib高分屏(高DPI)支持

目录结构

打包说明

开发者在打包自己的应用时,应确保将以下云信SDK相关文件打包进去。

其他的文件及目录是应用程序相关的,开发者根据自己程序的使用情况选择是否打包。

功能点指引

SDK C++封装层

因为SDK所有接口都是C接口,为了方便使用C++的同学使用,我们提供了nim_cpp_sdk静态库。静态库位于libs\nim_sdk_desktop\nim_cpp_sdk目录,它将C接口SDK封装为C++代码,demo和UI组件都直接使用nim_cpp_sdk静态库的C++封装层代码。开发者可以直接在解决方案中导入nim_cpp_sdk工程。

封装层提供的包装文件如下:

除了nim_cpp_sdk静态库,另外还提供了负责语音和http传输的dll的C++封装类:

SDK 初始化

SDK的初始化在main.cpp中InitNim方法进行。

示例:

//初始化nim sdk
std::string app_key="xxxxx";
nim::SDKConfig config;
//sdk能力参数(必填)
config.database_encrypt_key_ = "Netease"; //string(db key必填,目前只支持最多32个字符的加密密钥!建议使用32个字符)
bool ret = nim::Client::Init(app_key, "Netease", "", config); // 载入云信sdk,初始化安装目录和用户目录
assert(ret);

//初始化语音和http
nim_http::Init(); 
std::string res_audio_path = nbase::UTF16ToUTF8(app_data_audio_path);    // app_data_audio_path为语音资源所在用户目录
ret = nim_audio::Audio::Init(res_audio_path);
assert(ret);

界面开发

云信PC demo以及UI组件的界面开发都依赖云信DuiLib库,关于云信DuiLib库的使用方法和注意事项,请参考:云信Duilib

登录

登录相关界面代码在gui/login目录下,登录相关的控制逻辑已经封装到了UI组件的,逻辑代码在callback\login\login_callback.cpp文件中。登录界面可以直接调用UI组件的登录函数,示例如下:

std::string username = "123456789"std::string password = "123456789";
nim_ui::LoginManager::GetInstance()->DoLogin(username, password);

登录窗体的开发注意事项请看:云信UI组件。如果不使用UI组件提供的登录逻辑,可以直接调用C++封装层的登录函数,示例如下。请注意demo的登录密码是做了md5加密的,如果客户把app key替换成自己应用的app key,请记得去掉密码的md5加密。

std::string app_key = "45c6af3c98409b18a84451215d0bdd6e";//app key 申请获得
std::string pass_md5 = QString::GetMd5(password);    //MD5加密(用户自己的app请不要加密)
nim::Client::Login(app_key, user, pass_md5, OnLoginCallback, nullptr);

主窗体功能

Demo主窗体界面直接使用UI组件中开发好的最近会话列表组件、好友列表组件和群组列表组件。UI组件的相关文档详见:云信UI组件。在demo的主窗体类MainForm的初始化函数InitWindow中集成了最近会话列表组件、好友列表组件和群组列表组件。示例如下:

((OptionBox*) FindControl(L"btn_session_list"))->Selected(true, true);
ui::ListBox* session_list = (ListBox*)FindControl(L"session_list");
nim_ui::SessionListManager::GetInstance()->AttachListBox(session_list);
ui::TreeView* friend_list = (TreeView*) FindControl(L"friend_list");
nim_ui::ContactsListManager::GetInstance()->AttachFriendListBox(friend_list);
ui::TreeView* group_list = (TreeView*) FindControl(L"group_list");
nim_ui::ContactsListManager::GetInstance()->AttachGroupListBox(group_list);

nim_ui::ContactsListManager::GetInstance()->InvokeGetAllUserInfo();
nim_ui::SessionListManager::GetInstance()->InvokeLoadSessionList();
nim_ui::SessionListManager::GetInstance()->QueryUnreadCount();

会话

会话相关的界面代码在UI组件的gui/session目录下,逻辑代码在module/session目录下。Demo直接使用UI组件中开发好的会话窗体组件,也可以调用C++封装层的会话相关函数直接收发消息。

发送消息

SDK 支持文本、图片、音频、视频、地理位置、通知消息、提醒消息、文件消息和自定义消息等多种种类型消息,Demo目前展示了其中三种消息的发送,包括文本、图片和文件;同时支持用户自定义消息类型,用户可根据自己需要使用。发送消息的需要把数据组装成json、调用SDK接口发送。

发送文本消息示例:

Json::Value json;
json[nim::kNIMMsgKeyToType] = nim::kNIMSessionTypeP2P; //会话类型,好友是"kNIMSessionTypeP2P",群组是"kNIMSessionTypeTeam"
json[nim::kNIMMsgKeyType] = nim::kNIMMessageTypeText; //消息类型
json[nim::kNIMMsgKeyBody] = text; //消息内容

json[nim::kNIMMsgKeyToAccount] = receiver; //消息接收者账号
json[nim::kNIMMsgKeyTime] = 1000 * nbase::Time::Now().ToTimeT();; // 消息发送时间(毫秒)
json[nim::kNIMMsgKeyClientMsgid] = QString::GetGUID(); //消息id,一般使用guid
json[nim::kNIMMsgKeyLocalLogStatus] = nim::kNIMMsgLogStatusSending; //消息状态
nim::Talk::SendMsg(json.toStyledString());

发送图片消息示例:

std::wstring utf16_image_local_path = nbase::UTF8ToUTF16(image_local_path);
Json::Value json;
json[nim::kNIMMsgKeyToType] = nim::kNIMSessionTypeP2P;                //会话类型,好友是"kNIMSessionTypeP2P",群组是"kNIMSessionTypeTeam"
json[nim::kNIMMsgKeyType] = nim::kNIMMessageTypeImage;                //消息类型

//图片本地路径
json[nim::kNIMMsgKeyLocalFilePath] = image_local_path;                //文件本地路径
//图片详细信息
Json::Value image_key;
std::string md5 = GetFileMD5(image_local_path);
image_key[nim::kNIMImgMsgKeyMd5] = md5;                                //文件MD5
long sz = nbase::GetFileSize(utf16_image_local_path);
image_key[nim::kNIMImgMsgKeySize] = sz;                                //文件大小

Gdiplus::Image image(utf16_image_local_path.c_str());
if (image.GetLastStatus() == Gdiplus::Ok)
{
    image_key[nim::kNIMImgMsgKeyWidth] = image.GetWidth();            //宽度
    image_key[nim::kNIMImgMsgKeyHeight] = image.GetHeight();        //高度
}
json[nim::kNIMMsgKeyAttach] = image_key.toStyledString();

json[nim::kNIMMsgKeyToAccount] = receiver;                        //消息接收者账号
json[nim::kNIMMsgKeyTime] = 1000 * nbase::Time::Now().ToTimeT();;   //消息发送时间(毫秒)
json[nim::kNIMMsgKeyClientMsgid] = QString::GetGUID();                //消息id,一般使用guid
json[nim::kNIMMsgKeyLocalLogStatus] = nim::kNIMMsgLogStatusSending; //消息状态

nim::Talk::SendMsg(json.toStyledString());

接收消息

接收消息的需要在程序刚启动时,提前注册好接收消息的回调函数。回调函数的参数是一个json字符串,首先我们要解析json,json字段跟发送消息相近,只需要根据这些字段解析就可以了。解析完之后,一般我们会需要把解析到的文本、图片等消息在会话窗口中进行展示。

示例:

void TalkCallback::OnReceiveMsgCallback( const std::string& json_str)
{
    QLOG_PRO(L"OnReceiveMsgCallback: {0}") << json_str;

    Json::Value value;
    Json::Reader reader;
    if (reader.parse(json_str, value))
    {
        int code = value[nim::kNIMMsgKeyLocalRescode].asInt();
        int feature = value[nim::kNIMMsgKeyLocalMsgFeature].asInt();

        Json::Value json = value[nim::kNIMMsgKeyLocalReceiveMsgContent];
        json[nim::kNIMMsgKeyLocalRescode] = code;
        json[nim::kNIMMsgKeyLocalMsgFeature] = feature;

        MsgData msg;
        JsonToMsg(json, msg);
        std::string id = GetSessionId(msg);

        //会话窗口
        if (msg.feature == nim::kNIMMessageFeatureDefault)
        {
            if (msg.msg_type == nim::kNIMMessageTypeNotification)
            {
                SessionForm* session = SessionManager::GetInstance()->Find(id);
                if (session)
                {
                    session->AddNewMsg(msg, false);
                }
            }
            else
            {
                SessionManager::GetInstance()->AddNewMsg(msg);
            }
        }
        else if (msg.feature == nim::kNIMMessageFeatureSyncMsg || msg.feature == nim::kNIMMessageFeatureRoamMsg)
        {
            SessionForm* session = SessionManager::GetInstance()->Find(id);
            if (session)
            {
                session->AddNewMsg(msg, false);
            }
        }
        else if (msg.feature == nim::kNIMMessageFeatureCustomizedMsg)
        {
            SessionForm* session = SessionManager::GetInstance()->Find(id);
            if (session)
            {
                session->AddNewMsg(msg, false);
            }
        }
    }
    else
    {
        QLOG_ERR(L"parse receive msg fail: {0}") << json_str;
    }
}

聊天室代码导读

聊天室代码导读