环信_WebIM

环信_WebIM

2
评论

基于环信sdk在uni-app框架中快速开发一款多平台社交Demo SDK uni_app 环信

beyond 发表了文章 • 556 次浏览 • 2020-05-11 11:34 • 来自相关话题

说在前面:此款 demo 是基于 环信sdk 开发的一款具有单聊、群聊、聊天室、音视频等功能的应用。在此之前我们已经开发完 Vue、react(web端)、微信小程序。这三个热门领域的版本,如有需要源码可以后台留言索取。





 
安装开发工具

我们选用微信小程序来用做示例(如果选择百度、支付宝安装对应开发者工具即可)、

微信开发者工具建议还是安装最新版的。uni-app的开发也必须安装HBuilderX工具,这个是捆绑的,没得选择。要用uni-app,你必须得装!

工具安装:

微信开发者工具

HBuilderX

项目demo介绍:






项目demo启动预览:





 
快速集成环信 sdk:

1、复制整个utils文件






如果你想具体了解主要配置文件 请看这个链接:

http://docs-im.easemob.com/im/web/intro/start

2、如何使用环信的appkey ,可以在环信 console 后台注册一个 账号申请appkey ,可以参考这里 ,获取到  appkey 以后添加到配置文件中 ,如下图所示:






以上两个重要的配置准备完成之后就可以进行一系列的操作了(收发消息、好友申请、进群入群通知等)

在uni-app中 使用环信 sdk 实现添加、删除好友:

1、在全局 App.vue 文件 钩子函数 onLaunch() 中监听各种事件 (好友申请、收到各类消息等)如图:






发送好友请求:






在onPresence(message)事件中接收到好友消息申请:






同意好友请求:






拒绝好友请求:






实现收发消息:

1、给好友发送消息:






2、接收到消息:

在onTextMessage(message)事件中接收到好友消息,然后做消息上屏处理(具体消息上屏逻辑可看demo中代码示例):





以上展示的仅仅为基本业务场景,更多的业务逻辑详情请看demo示例。api具体详情可以查看 环信sdk 文档

最后结语:基于uni-app这个框架可实现多平台, 虽然目前一期集成环信sdk的版本仅支持微信小程序版本,但二期我们将加入头条、支付宝等小程序,敬请期待。PS:对于安卓、ios移动端,我们建议使用针对移动端开发的sdk版本。

基于uni-app的开发其中也趟了不少坑,在这里就不多赘述了。回归到框架的选型来讲,选用uni-app开发小程序,可同时并行多端小程序,这点是真香,一次开发多端发布。至于审核嘛~ 时快时慢。 查看全部
说在前面:此款 demo 是基于 环信sdk 开发的一款具有单聊、群聊、聊天室、音视频等功能的应用。在此之前我们已经开发完 Vue、react(web端)、微信小程序。这三个热门领域的版本,如有需要源码可以后台留言索取。

1.jpg

 
安装开发工具

我们选用微信小程序来用做示例(如果选择百度、支付宝安装对应开发者工具即可)、

微信开发者工具建议还是安装最新版的。uni-app的开发也必须安装HBuilderX工具,这个是捆绑的,没得选择。要用uni-app,你必须得装!

工具安装:

微信开发者工具

HBuilderX

项目demo介绍:

2.jpg


项目demo启动预览:

3.jpg

 
快速集成环信 sdk:

1、复制整个utils文件

4.jpg


如果你想具体了解主要配置文件 请看这个链接:

http://docs-im.easemob.com/im/web/intro/start

2、如何使用环信的appkey ,可以在环信 console 后台注册一个 账号申请appkey ,可以参考这里 ,获取到  appkey 以后添加到配置文件中 ,如下图所示:

5.jpg


以上两个重要的配置准备完成之后就可以进行一系列的操作了(收发消息、好友申请、进群入群通知等)

在uni-app中 使用环信 sdk 实现添加、删除好友:

1、在全局 App.vue 文件 钩子函数 onLaunch() 中监听各种事件 (好友申请、收到各类消息等)如图:

6.jpg


发送好友请求:

7.jpg


在onPresence(message)事件中接收到好友消息申请:

8.jpg


同意好友请求:

9.jpg


拒绝好友请求:

10.jpg


实现收发消息:

1、给好友发送消息:

11.jpg


2、接收到消息:

在onTextMessage(message)事件中接收到好友消息,然后做消息上屏处理(具体消息上屏逻辑可看demo中代码示例):

12.jpg

以上展示的仅仅为基本业务场景,更多的业务逻辑详情请看demo示例。api具体详情可以查看 环信sdk 文档

最后结语:基于uni-app这个框架可实现多平台, 虽然目前一期集成环信sdk的版本仅支持微信小程序版本,但二期我们将加入头条、支付宝等小程序,敬请期待。PS:对于安卓、ios移动端,我们建议使用针对移动端开发的sdk版本。

基于uni-app的开发其中也趟了不少坑,在这里就不多赘述了。回归到框架的选型来讲,选用uni-app开发小程序,可同时并行多端小程序,这点是真香,一次开发多端发布。至于审核嘛~ 时快时慢。
1
评论

手把手教程:4小时开发一个视频会议APP【附开源代码】 环信 开源 视频会议

fat1 发表了文章 • 1054 次浏览 • 2020-04-17 00:29 • 来自相关话题

今年是不平凡的一年,没错,就是因为疫情,因为疫情原因 ,大家只能呆着家里,严重影响了我们正常的学习 生活 工作,在这种情况下,只能在家办公,这时候大家就会想到线上视频会议,目前很多互联网公司有这个产品,比较出名的就比如 腾讯会议 钉钉 zoom等,用这些是很方便,但是如果能开发自己的视频会议,那会不会更好或者是更有成就感,下面简单介绍这个这个项目,和大概的开发过程。本项目基于环信音视频云来完成,实现的主要功能有:
  创建会议、删除会议、获取指定会议室详情、加入会议室、退出会议室等关于会议的管理 ;  获取会议室参会人名列表、踢人,设置观众为主播,设置主播为观众等关于会议室的人员管理;  共享桌面(web端);
 三个端的实现:Android,iOS,Web

上面这些功能在项目中都已经实现。还有水印 ,变声等高级功能在环信音视频SDK的接口内部都已经封装好,本项目没有实现 ,大家可以自行去实现。有关多人音视频功能更详细的介绍大家可以参考:这儿。多人音视频实现的实现主要有以下一些场景:社交交友,远程心理咨询、远程医疗、一对一在线教育、远程视频辅助等。咳咳 ,接下来就是纯干货了,给大家介绍我是如何一步步开发出一个完整的多人音视频app。
 
项目截图
 
首先给大家展示下项目运行的效果图,会议界面 主窗口是一个大的 RelativeLayout ,最下面的那一排排小窗口是的实现方法是HorizontalScrollView加上一个开源的组件 com.jaouan.compoundlayout.RadioLayoutGroup 实现的,点击下面的小窗口后,可以 把小窗口的视频流显示在大屏上,具体是调用 updateRemoteSurfaceView(String streamId, EMCallSurfaceView remoteView)来更新SurfaceView,具体的细节大家可以看看代码里面的实现 最后会公布代码开源地址。
















准备工作
    大家得下载安装Android Studio,配置好Android 开发环境,怎么详细配置我就在这不再细说了 网上有很多的教程,大家自己可以找找看,然后大家可以看看环信多人音视频会议的主要功能和一些基本概念介绍。
集成 
  1. 首先大家会想问怎么调用环信的SDK ,大家可以使用 远程依赖SDK包,建议大家用最新版本的远程依赖:
     com.hyphenate:hyphenate-sdk:3.6.6 ,依赖包可以放在 build.gradle里面的 dependencies 选项下面,如下图所示





2.其次怎么使用环信的appkey ,可以在环信音视频云后台注册一个 账号申请appkey ,可以参考这里 ,获取到  appkey 以后添加到AndroidManifest.xml中 ,如下图所示:






3.经过以上两个重要的前期配置准备 ,接下来我们就可以开始进行代码开发了,首先我们先创建一个项目的DemoApplication类和      DemoHelper类,DemoApplication 类和DemoHelper类都是一个单例类 ,DemoApplication 主要功能就是进行DemoHelper 的初始化,而DemoHelper里面主要是主要有一些option 配置和EMClient 进行初始化,代码如下所示:public void init(Context context) {
EMOptions options = initChatOptions(context);
EMClient.getInstance().init(context, options);
PreferenceManager.init(context);
}
  DemoHelper还有一个重要的功能就是设置  EMConferenceListener 进行会议监听,有关 EMConferenceListener的类的详细介绍 ,通过这个监听可以再加入会议的时候获取到已经在会议中的流和主播信息,分别是通过其中以下两个回调获取:@Override
public void onMemberJoined(EMConferenceMember member){

}

@Override
public void onStreamAdded(EMConferenceStream stream){

}
4.DemoApplication类完成以后,接下来就是怎么去登陆 环信IM 账号和 创建加入会议房间了,首次安装的时候都没有账号,我们使用的办法是自动注册一个账号 在本地进行保存,然后进行登录 ,注册 登录详细接口请看 这儿,  注册 登录的调用大概如下所示: 
 try {
        //注册一个环信ID
        EMClient.getInstance().createAccount(username, password);
            
        //注册成功进行登录
        PreferenceManager.getInstance().setCurrentUserName(username);
        PreferenceManager.getInstance().setCurrentuserPassword(password);
        login();
    } catch (final HyphenateException e) {
       runOnUiThread(new Runnable() {
               public void run() {
                  int errorCode=e.getErrorCode();
                   if(errorCode==EMError.NETWORK_ERROR){
                    Toast.makeText(getApplicationContext(), getResources().getString(R.string.network_anomalies), Toast.LENGTH_SHORT).show();
      }
   }   
}
 
  public void login() {
 
        //登录已经注册成功的环信ID
        EMClient.getInstance().login(username, password, new EMCallBack() {
            @Override
            public void onSuccess() {
                Log.d(TAG, "login: onSuccess");
                //登录成功进入会议房间
                joinRoom();
            }
            @Override
            public void onProgress(int progress, String status) {
                Log.d(TAG, "login: onProgress");
            }
            @Override
            public void onError(final int code, final String message) {
                Log.d(TAG, "login: onError: " + code);
                runOnUiThread(new Runnable() {
                    public void run() {
                        Toast.makeText(getApplicationContext(), getString(R.string.Login_failed) + message,Toast.LENGTH_SHORT).show();
                    }
                });
            }
        });
    }
登录完成以后,我们可以根据房间名创建并加入房间,主要代码大概如下: EMClient.getInstance().conferenceManager().joinRoom(currentRoomname, currentPassword, conferenceRole,roomConfig, new EMValueCallBack<EMConference>(){
@Override
public void onSuccess(EMConference value) {
EMLog.i(TAG, "join conference success");
Intent intent = new Intent(MainActivity.this, ConferenceActivity.class);
startActivity(intent);
finish();
}
@Override
public void onError(final int error, final String errorMsg) {
EMLog.e(TAG, "join conference failed error " + error + ", msg " + errorMsg);
runOnUiThread(new Runnable() {
@Override
public void run() {
setBtnEnable(true);
if(error == CALL_TALKER_ISFULL) {
takerFullDialogDisplay();
}else{
Toast.makeText(getApplicationContext(), "Join conference failed " + error + " " + errorMsg, Toast.LENGTH_SHORT).show();
}
}
});
}
});
EMClient.getInstance().conferenceManager().joinRoom() API可以根据房间名创建指定会议,当以该房间名命名的会议不存在时候,会直接创建,当会议已经创建好 可以根据正确的房间名和密码加入房间 ,到这一步为止,我们已经成功的创建 并加入会议。
5.加入会议以后我们进入到会议界面,展示从DemoHelper类 EMConferenceListener 中的 onStreamAdded 回调 和 onMemberJoined 获取到的流和主播列表 ,在ConferenceActivity 中实现 EMConferenceListener ,然后直接把 ConferenceActivity 注册监听,用以下方法  EMClient.getInstance().conferenceManager().addConferenceListener(this); 这样就可实现 EMConferenceListener 事件的处理,比如
 主播 进出房间 :
public void onMemberJoined(final EMConferenceMember member);
public void onMemberExited(final EMConferenceMember member);

增加流 移除流:
public void onStreamAdded(final EMConferenceStream stream)
 public void onStreamRemoved(final EMConferenceStream stream)

管理员变更: 
public void onAdminAdded(String streamId) ;  
public void onAdminRemoved(String streamId)

角色变更  用户被踢  谁在说话等各种回调,可以处理各种业务逻辑 ,详细的请参考 项目中的实现 ,最后会附上项目的开源地址。

6 进入会议房间以后如果用户角色为主播可以进行发布视频流 ,观众只能订阅视频流 不能发布视频流 ,可以调用SDK的publish接口发布流,该接口用到了EMStreamParam参数,你可以自由配置,比如是否上传视频,是否上传音频,使用前置或后置摄像头,视频码率,显示视频页面等等,具体实现可以参考 中发布 订阅视频流的内容, 关于以上的代码逻辑如以 如以下: //发布视频流
normalParam = new EMStreamParam();
normalParam.setStreamType(EMConferenceStream.StreamType.NORMAL);
normalParam.setVideoOff(true);
normalParam.setAudioOff(true);

EMClient.getInstance().conferenceManager().publish(normalParam, new EMValueCallBack<String>() {
@Override
public void onSuccess(String value) {
conference.setPubStreamId(value, EMConferenceStream.StreamType.NORMAL);
addOrUpdateStreamList("local-stream", value);

PhoneStateManager.get(ConferenceActivity.this).addStateCallback(phoneStateCallback);
}

@Override
public void onError(int error, String errorMsg) {
EMLog.i(TAG, "publish failed: error=" + error + ", msg=" + errorMsg);
}
});
//订阅其他主播的视频流
private void subscribe(EMConferenceStream stream, EMCallSurfaceView surfaceView) {
EMClient.getInstance().conferenceManager().subscribe(stream, surfaceView, new EMValueCallBack<String>() {
@Override
public void onSuccess(String value) {
}

@Override
public void onError(int error, String errorMsg) {

}
});
}
7.有关上麦 下麦 的逻辑处理,观众可以请求上麦成为主播,主播可以下麦成为观众,上麦 下麦 是利用 EMConferenceAttribute进行处理 ,EMConferenceAttribute  是一个事件广播,广播事件是一个key-value格式,key-value 可以由开发者进行自行定义,增添事件以后 ,服务器会把事件进行广播。会议中成员会收到 onAttributesUpdated回调。例如本项目中的会议上麦 下麦 代码如下所示://上麦申请

EMClient.getInstance().conferenceManager().setConferenceAttribute(
EMClient.getInstance().getCurrentUser(),
"request_tobe_speaker",
new EMValueCallBack<Void>() {
@Override
public void onSuccess(Void value) {
EMLog.i(TAG, "request_tobe_speaker scuessed");

}

@Override
public void onError(int error, String errorMsg) {
EMLog.i(TAG, "request_tobe_speaker failed: error=" + error

}
});

//下麦申请
EMClient.getInstance().conferenceManager().setConferenceAttribute(EMClient.getInstance().getCurrentUser()
, "request_tobe_audience", new EMValueCallBack<Void>() {
@Override
public void onSuccess(Void value) {
EMLog.i(TAG, "request_tobe_audience scuessed");
}

@Override
public void onError(int error, String errorMsg) {
EMLog.i(TAG, "request_tobe_audience failed: error=" + error + ", msg=" + errorMsg);
}
});
 上麦 下麦 请求发出以后 只能由主持人去处理,处理在 EMConferenceListener  的回调 onAttributesUpdated(EMConferenceAttribute attributes) 去处理 ,收到回调以后 解析attributes 然后进行处理请求,处理的过程代码大概如下: EMClient.getInstance().conferenceManager().grantRole(conference.getConferenceId()
, new EMConferenceMember(memName, null, null,null)
, EMConferenceManager.EMConferenceRole.Talker, new EMValueCallBack<String>() {
@Override
public void onSuccess(String value) {
EMLog.i(TAG, " requestTalkerDisplay request_tobe_speaker changeRole success, result: " + value);
dialog.dismiss();
}
@Override
public void onError(int error, String errorMsg) {
EMLog.i(TAG, " requestTalkerDisplay request_tobe_speaker changeRole failed, error: " + error + " - " + errorMsg);

}
});
下麦也是和上麦一样是利用 EMConferenceAttribute进行处理。

9.有关退出会议 销毁会议 普通主播  观众只能退出会议 ,主持人还可以 销毁会议 正在进行中的会议可以进行销毁,退出会议 销毁会议 具体代码如下: EMClient.getInstance().conferenceManager().exitConference(new EMValueCallBack() {
@Override
public void onSuccess(Object value) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(getApplicationContext(), "您已成功退出当前会议!", Toast.LENGTH_SHORT).show();
}
});
}

@Override
public void onError(int error, String errorMsg) {
EMLog.i(TAG, "exit conference failed " + error + ", " + errorMsg);
}
});

EMClient.getInstance().conferenceManager().destroyConference(new EMValueCallBack() {
@Override
public void onSuccess(Object value) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(getApplicationContext(), "您已成功销毁当前会议!", Toast.LENGTH_SHORT).show();
}
});
EMLog.i(TAG, "finish ConferenceActivity");
finish();
}

尾语
至此 整个 多人音视频会议开发的详细步骤 已经完成 ,虽然比较麻烦 但是每个步骤都很清晰 ,有不太清楚的欢迎大家积极讨论, 附上本项目的github地址:https://github.com/easemob/videocall-android  欢迎大家积极参与 ,谢谢支持。
Demo下载 二维码如下 欢迎大家体验(iOS版和web版下载地址请见:https://www.easemob.com/download/rtc)






  查看全部
今年是不平凡的一年,没错,就是因为疫情,因为疫情原因 ,大家只能呆着家里,严重影响了我们正常的学习 生活 工作,在这种情况下,只能在家办公,这时候大家就会想到线上视频会议,目前很多互联网公司有这个产品,比较出名的就比如 腾讯会议 钉钉 zoom等,用这些是很方便,但是如果能开发自己的视频会议,那会不会更好或者是更有成就感,下面简单介绍这个这个项目,和大概的开发过程。本项目基于环信音视频云来完成,实现的主要功能有:
  •   创建会议、删除会议、获取指定会议室详情、加入会议室、退出会议室等关于会议的管理 ;
  •   获取会议室参会人名列表、踢人,设置观众为主播,设置主播为观众等关于会议室的人员管理;
  •   共享桌面(web端);

 三个端的实现:Android,iOS,Web

上面这些功能在项目中都已经实现。还有水印 ,变声等高级功能在环信音视频SDK的接口内部都已经封装好,本项目没有实现 ,大家可以自行去实现。有关多人音视频功能更详细的介绍大家可以参考:这儿。多人音视频实现的实现主要有以下一些场景:社交交友,远程心理咨询、远程医疗、一对一在线教育、远程视频辅助等。咳咳 ,接下来就是纯干货了,给大家介绍我是如何一步步开发出一个完整的多人音视频app。
 
项目截图
 
首先给大家展示下项目运行的效果图,会议界面 主窗口是一个大的 RelativeLayout ,最下面的那一排排小窗口是的实现方法是HorizontalScrollView加上一个开源的组件 com.jaouan.compoundlayout.RadioLayoutGroup 实现的,点击下面的小窗口后,可以 把小窗口的视频流显示在大屏上,具体是调用 updateRemoteSurfaceView(String streamId, EMCallSurfaceView remoteView)来更新SurfaceView,具体的细节大家可以看看代码里面的实现 最后会公布代码开源地址。

20200410140627956.jpg


20200410140855852.jpg


20200410141055394.jpg


准备工作
    大家得下载安装Android Studio,配置好Android 开发环境,怎么详细配置我就在这不再细说了 网上有很多的教程,大家自己可以找找看,然后大家可以看看环信多人音视频会议的主要功能和一些基本概念介绍。
集成 
  1. 首先大家会想问怎么调用环信的SDK ,大家可以使用 远程依赖SDK包,建议大家用最新版本的远程依赖:
     com.hyphenate:hyphenate-sdk:3.6.6 ,依赖包可以放在 build.gradle里面的 dependencies 选项下面,如下图所示
2020040917363212.png


2.其次怎么使用环信的appkey ,可以在环信音视频云后台注册一个 账号申请appkey ,可以参考这里 ,获取到  appkey 以后添加到AndroidManifest.xml中 ,如下图所示:

20200409213959916.png


3.经过以上两个重要的前期配置准备 ,接下来我们就可以开始进行代码开发了,首先我们先创建一个项目的DemoApplication类和      DemoHelper类,DemoApplication 类和DemoHelper类都是一个单例类 ,DemoApplication 主要功能就是进行DemoHelper 的初始化,而DemoHelper里面主要是主要有一些option 配置和EMClient 进行初始化,代码如下所示:
public void init(Context context) {
EMOptions options = initChatOptions(context);
EMClient.getInstance().init(context, options);
PreferenceManager.init(context);
}

  DemoHelper还有一个重要的功能就是设置  EMConferenceListener 进行会议监听,有关 EMConferenceListener的类的详细介绍 ,通过这个监听可以再加入会议的时候获取到已经在会议中的流和主播信息,分别是通过其中以下两个回调获取:
@Override 
public void onMemberJoined(EMConferenceMember member){

}

@Override
public void onStreamAdded(EMConferenceStream stream){

}

4.DemoApplication类完成以后,接下来就是怎么去登陆 环信IM 账号和 创建加入会议房间了,首次安装的时候都没有账号,我们使用的办法是自动注册一个账号 在本地进行保存,然后进行登录 ,注册 登录详细接口请看 这儿,  注册 登录的调用大概如下所示: 
 try {
        //注册一个环信ID
        EMClient.getInstance().createAccount(username, password);
            
        //注册成功进行登录
        PreferenceManager.getInstance().setCurrentUserName(username);
        PreferenceManager.getInstance().setCurrentuserPassword(password);
        login();
    } catch (final HyphenateException e) {
       runOnUiThread(new Runnable() {
               public void run() {
                  int errorCode=e.getErrorCode();
                   if(errorCode==EMError.NETWORK_ERROR){
                    Toast.makeText(getApplicationContext(), getResources().getString(R.string.network_anomalies), Toast.LENGTH_SHORT).show();
      }
   }   
}
 
  public void login() {
 
        //登录已经注册成功的环信ID
        EMClient.getInstance().login(username, password, new EMCallBack() {
            @Override
            public void onSuccess() {
                Log.d(TAG, "login: onSuccess");
                //登录成功进入会议房间
                joinRoom();
            }
            @Override
            public void onProgress(int progress, String status) {
                Log.d(TAG, "login: onProgress");
            }
            @Override
            public void onError(final int code, final String message) {
                Log.d(TAG, "login: onError: " + code);
                runOnUiThread(new Runnable() {
                    public void run() {
                        Toast.makeText(getApplicationContext(), getString(R.string.Login_failed) + message,Toast.LENGTH_SHORT).show();
                    }
                });
            }
        });
    }
登录完成以后,我们可以根据房间名创建并加入房间,主要代码大概如下:
 EMClient.getInstance().conferenceManager().joinRoom(currentRoomname, currentPassword, conferenceRole,roomConfig, new EMValueCallBack<EMConference>(){
@Override
public void onSuccess(EMConference value) {
EMLog.i(TAG, "join conference success");
Intent intent = new Intent(MainActivity.this, ConferenceActivity.class);
startActivity(intent);
finish();
}
@Override
public void onError(final int error, final String errorMsg) {
EMLog.e(TAG, "join conference failed error " + error + ", msg " + errorMsg);
runOnUiThread(new Runnable() {
@Override
public void run() {
setBtnEnable(true);
if(error == CALL_TALKER_ISFULL) {
takerFullDialogDisplay();
}else{
Toast.makeText(getApplicationContext(), "Join conference failed " + error + " " + errorMsg, Toast.LENGTH_SHORT).show();
}
}
});
}
});

EMClient.getInstance().conferenceManager().joinRoom() API可以根据房间名创建指定会议,当以该房间名命名的会议不存在时候,会直接创建,当会议已经创建好 可以根据正确的房间名和密码加入房间 ,到这一步为止,我们已经成功的创建 并加入会议。
5.加入会议以后我们进入到会议界面,展示从DemoHelper类 EMConferenceListener 中的 onStreamAdded 回调 和 onMemberJoined 获取到的流和主播列表 ,在ConferenceActivity 中实现 EMConferenceListener ,然后直接把 ConferenceActivity 注册监听,用以下方法  EMClient.getInstance().conferenceManager().addConferenceListener(this); 这样就可实现 EMConferenceListener 事件的处理,比如
 主播 进出房间 :
public void onMemberJoined(final EMConferenceMember member);
public void onMemberExited(final EMConferenceMember member);

增加流 移除流:
public void onStreamAdded(final EMConferenceStream stream)
 public void onStreamRemoved(final EMConferenceStream stream)

管理员变更: 
public void onAdminAdded(String streamId) ;  
public void onAdminRemoved(String streamId)

角色变更  用户被踢  谁在说话等各种回调,可以处理各种业务逻辑 ,详细的请参考 项目中的实现 ,最后会附上项目的开源地址。

6 进入会议房间以后如果用户角色为主播可以进行发布视频流 ,观众只能订阅视频流 不能发布视频流 ,可以调用SDK的publish接口发布流,该接口用到了EMStreamParam参数,你可以自由配置,比如是否上传视频,是否上传音频,使用前置或后置摄像头,视频码率,显示视频页面等等,具体实现可以参考 中发布 订阅视频流的内容, 关于以上的代码逻辑如以 如以下:
 //发布视频流
normalParam = new EMStreamParam();
normalParam.setStreamType(EMConferenceStream.StreamType.NORMAL);
normalParam.setVideoOff(true);
normalParam.setAudioOff(true);

EMClient.getInstance().conferenceManager().publish(normalParam, new EMValueCallBack<String>() {
@Override
public void onSuccess(String value) {
conference.setPubStreamId(value, EMConferenceStream.StreamType.NORMAL);
addOrUpdateStreamList("local-stream", value);

PhoneStateManager.get(ConferenceActivity.this).addStateCallback(phoneStateCallback);
}

@Override
public void onError(int error, String errorMsg) {
EMLog.i(TAG, "publish failed: error=" + error + ", msg=" + errorMsg);
}
});
//订阅其他主播的视频流
private void subscribe(EMConferenceStream stream, EMCallSurfaceView surfaceView) {
EMClient.getInstance().conferenceManager().subscribe(stream, surfaceView, new EMValueCallBack<String>() {
@Override
public void onSuccess(String value) {
}

@Override
public void onError(int error, String errorMsg) {

}
});
}

7.有关上麦 下麦 的逻辑处理,观众可以请求上麦成为主播,主播可以下麦成为观众,上麦 下麦 是利用 EMConferenceAttribute进行处理 ,EMConferenceAttribute  是一个事件广播,广播事件是一个key-value格式,key-value 可以由开发者进行自行定义,增添事件以后 ,服务器会把事件进行广播。会议中成员会收到 onAttributesUpdated回调。例如本项目中的会议上麦 下麦 代码如下所示:
//上麦申请  

EMClient.getInstance().conferenceManager().setConferenceAttribute(
EMClient.getInstance().getCurrentUser(),
"request_tobe_speaker",
new EMValueCallBack<Void>() {
@Override
public void onSuccess(Void value) {
EMLog.i(TAG, "request_tobe_speaker scuessed");

}

@Override
public void onError(int error, String errorMsg) {
EMLog.i(TAG, "request_tobe_speaker failed: error=" + error

}
});

//下麦申请
EMClient.getInstance().conferenceManager().setConferenceAttribute(EMClient.getInstance().getCurrentUser()
, "request_tobe_audience", new EMValueCallBack<Void>() {
@Override
public void onSuccess(Void value) {
EMLog.i(TAG, "request_tobe_audience scuessed");
}

@Override
public void onError(int error, String errorMsg) {
EMLog.i(TAG, "request_tobe_audience failed: error=" + error + ", msg=" + errorMsg);
}
});

 上麦 下麦 请求发出以后 只能由主持人去处理,处理在 EMConferenceListener  的回调 onAttributesUpdated(EMConferenceAttribute attributes) 去处理 ,收到回调以后 解析attributes 然后进行处理请求,处理的过程代码大概如下:
   EMClient.getInstance().conferenceManager().grantRole(conference.getConferenceId()
, new EMConferenceMember(memName, null, null,null)
, EMConferenceManager.EMConferenceRole.Talker, new EMValueCallBack<String>() {
@Override
public void onSuccess(String value) {
EMLog.i(TAG, " requestTalkerDisplay request_tobe_speaker changeRole success, result: " + value);
dialog.dismiss();
}
@Override
public void onError(int error, String errorMsg) {
EMLog.i(TAG, " requestTalkerDisplay request_tobe_speaker changeRole failed, error: " + error + " - " + errorMsg);

}
});

下麦也是和上麦一样是利用 EMConferenceAttribute进行处理。

9.有关退出会议 销毁会议 普通主播  观众只能退出会议 ,主持人还可以 销毁会议 正在进行中的会议可以进行销毁,退出会议 销毁会议 具体代码如下:
 EMClient.getInstance().conferenceManager().exitConference(new EMValueCallBack() {
@Override
public void onSuccess(Object value) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(getApplicationContext(), "您已成功退出当前会议!", Toast.LENGTH_SHORT).show();
}
});
}

@Override
public void onError(int error, String errorMsg) {
EMLog.i(TAG, "exit conference failed " + error + ", " + errorMsg);
}
});

EMClient.getInstance().conferenceManager().destroyConference(new EMValueCallBack() {
@Override
public void onSuccess(Object value) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(getApplicationContext(), "您已成功销毁当前会议!", Toast.LENGTH_SHORT).show();
}
});
EMLog.i(TAG, "finish ConferenceActivity");
finish();
}

尾语
至此 整个 多人音视频会议开发的详细步骤 已经完成 ,虽然比较麻烦 但是每个步骤都很清晰 ,有不太清楚的欢迎大家积极讨论, 附上本项目的github地址:https://github.com/easemob/videocall-android  欢迎大家积极参与 ,谢谢支持。
Demo下载 二维码如下 欢迎大家体验(iOS版和web版下载地址请见:https://www.easemob.com/download/rtc)


20200410142228530.png

 
2
回复

环信发送语音问题! 环信_WebIM集成问题 webIm H5 环信_WebIM 环信_RestAPI

lizg 回复了问题 • 3 人关注 • 79 次浏览 • 2020-06-06 18:30 • 来自相关话题

11
最佳

webim 得到音频blob地址,页面刷新后就不能播放了 环信_WebIM webIm H5

vtchg 回复了问题 • 2 人关注 • 762 次浏览 • 2020-06-05 10:13 • 来自相关话题

1
最佳

请问 h5 端有直播间吗 环信_WebIM集成问题 环信_WebIM使用问题 环信_WebIM

lizg 回复了问题 • 2 人关注 • 127 次浏览 • 2020-06-02 10:12 • 来自相关话题

2
回复

音频消息,如何转化为mp3地址 webIm H5 环信_RestAPI 环信_WebIM 环信_WebIM使用问题

vtchg 回复了问题 • 2 人关注 • 196 次浏览 • 2020-05-31 15:59 • 来自相关话题

1
评论

微信小程序sdk支持1v1语言通话吗? 环信_WebIM

无为 发表了文章 • 102 次浏览 • 2020-05-28 11:22 • 来自相关话题

微信小程序sdk支持1v1语言通话吗?
微信小程序sdk支持1v1语言通话吗?
2
回复

uni-app 是否支持实时音视频 环信_WebIM

lizg 回复了问题 • 2 人关注 • 1972 次浏览 • 2020-05-27 15:10 • 来自相关话题

1
回复
2
回复

Web 端怎么获取历史记录 环信_WebIM

lizg 回复了问题 • 3 人关注 • 158 次浏览 • 2020-05-26 18:44 • 来自相关话题

1
回复

环信上传图片信息返回的share-secret为null 环信_WebIM

carlwang 回复了问题 • 2 人关注 • 215 次浏览 • 2020-05-20 17:04 • 来自相关话题

1
回复

请教支持在微信公众号H5页面中集成1vs1音视频通讯功能吗? 环信_WebIM 环信移动客服

lizg 回复了问题 • 2 人关注 • 380 次浏览 • 2020-05-18 19:20 • 来自相关话题

1
回复

(毕业真实版本)《加州大学伯克利分校毕业证|UCB毕业一样证书 环信_WebIM

lizg 回复了问题 • 2 人关注 • 396 次浏览 • 2020-05-18 19:19 • 来自相关话题

2
回复

webim没有聊天界面吗 环信_WebIM

lizg 回复了问题 • 2 人关注 • 2196 次浏览 • 2020-05-18 17:56 • 来自相关话题

1
回复

我们开发的小伙伴最近在用flutter开发,想整合环信调试,是否支持呢? 环信_WebIM

andy2015 回复了问题 • 1 人关注 • 2915 次浏览 • 2020-05-15 10:35 • 来自相关话题

1
回复

小程序端 如何修改头像 环信_WebIM

lizg 回复了问题 • 2 人关注 • 2241 次浏览 • 2020-05-11 10:38 • 来自相关话题

1
回复

webim发送文本消息ext传头像和昵称怎么书写,放在那好个地方呢 环信_WebIM

lizg 回复了问题 • 2 人关注 • 876 次浏览 • 2020-04-20 18:24 • 来自相关话题

2
回复

微信小程序sdk支持1v1语言通话吗? 环信_WebIM 微信小程序

迷之de小孩灬 回复了问题 • 2 人关注 • 1504 次浏览 • 2020-04-09 12:11 • 来自相关话题

1
回复

easemob-websdk 3.0.7 没有setPresence方法啊 环信_WebIM

lizg 回复了问题 • 3 人关注 • 903 次浏览 • 2020-03-11 07:05 • 来自相关话题

3
回复

webIM vue如何获取所有未读消息数量?有相应的示例代码么 环信_WebIM

lizg 回复了问题 • 4 人关注 • 1068 次浏览 • 2020-03-11 03:05 • 来自相关话题

1
回复

webIM群组发送消息 onError type=503是什么意思 没权限吗 环信_WebIM

lizg 回复了问题 • 2 人关注 • 597 次浏览 • 2020-03-09 18:49 • 来自相关话题

1
回复

websdk ext字段number格式转换为long 环信_WebIM

lizg 回复了问题 • 2 人关注 • 740 次浏览 • 2020-02-26 12:15 • 来自相关话题

1
回复

多登陆被踢不返回login在返回多加了个东西求解答 小程序api 被踢 环信_RestAPI 环信_WebIM

lizg 回复了问题 • 2 人关注 • 787 次浏览 • 2020-02-11 17:34 • 来自相关话题

1
回复

sdk文件放到现有小程序里,报错:Identifier 'DOMParser' has already been declared 环信_WebIM

lizg 回复了问题 • 2 人关注 • 1124 次浏览 • 2020-01-09 17:37 • 来自相关话题

1
回复

自定义消息,webIM发送的boolean值,在android这边取到一直是false 环信_Android 环信_WebIM

lizg 回复了问题 • 2 人关注 • 969 次浏览 • 2020-01-02 17:45 • 来自相关话题

3
最佳

小程序中发现一个问题isAutoLogin为true不手动setPresence接收不到离线消息 小程序 环信_WebIM

lizg 回复了问题 • 2 人关注 • 1762 次浏览 • 2019-12-23 17:29 • 来自相关话题

1
回复

怎么导出环信当天所有聊天记录 环信_RestAPI 环信_管理后台 环信_WebIM 环信_Android 环信_iOS

lizg 回复了问题 • 2 人关注 • 1329 次浏览 • 2019-12-23 17:27 • 来自相关话题

条新动态, 点击查看
可以,环信不是好友也可以聊天,只有知道对方id就行
可以,环信不是好友也可以聊天,只有知道对方id就行
首先登陆你的网站,然后在登陆环信,这个可以同步登陆的,登陆网址之后再登陆你为用户注册的环信id,不让用户感知就行
首先登陆你的网站,然后在登陆环信,这个可以同步登陆的,登陆网址之后再登陆你为用户注册的环信id,不让用户感知就行
beyond

beyond 回答了问题 • 2016-09-08 16:19 • 2 个回复 不感兴趣

web im 注册 返回错误 type:27 求解 版本1.1.2

赞同来自:

type 27错误是WEBIM_CONNCTION_APPKEY_NOT_ASSIGN_ERROR
关于webim api错误码详细对照可以在这里看到 http://docs.easemob.com/im/450errorcode/40weberrorcode... 显示全部 »
type 27错误是WEBIM_CONNCTION_APPKEY_NOT_ASSIGN_ERROR
关于webim api错误码详细对照可以在这里看到 http://docs.easemob.com/im/450errorcode/40weberrorcode
 
现在来看这个错误,WEBIM_CONNCTION_APPKEY_NOT_ASSIGN_ERROR在 sdk定义的是appkey错误或者没写。
 
根据用户名/密码/昵称注册环信 Web IM:  var options = {
username: "username",
password: "password",
nickname: "nickname",
appKey: "orgName#appName",
success: function () { },
error: function () { },
apiUrl: WebIM.config.apiURL
};

WebIM.utils.registerUser(options);


你少写了一个appkey!
 
如果我 回复帮到了你,欢迎赞赏!
naturelan

naturelan 回答了问题 • 2016-10-23 12:12 • 4 个回复 不感兴趣

webIM接收不到消息

赞同来自:

问题描述: {"type":31,"data":{"line":925,"column":41,
如果是这个错误的话,是因为websdk-1.1.2.js里面找不到WebI... 显示全部 »
问题描述: {"type":31,"data":{"line":925,"column":41,
如果是这个错误的话,是因为websdk-1.1.2.js里面找不到WebIM.Emoji, 就是WebIM.Emoji == 'undefined',然后调用函数emojibody.isemoji失败。
 
解决方法:
way1: 根据demo.js里面写,demo里面定义了WebIM.Emoji (表情包)
way2: 修改websdk-1.1.2.js里面的line925,在调用emojibody.isemoji前,判断一下WebIM.Emoji是不是undefined。如果你没有定义 WebIM.Emoji, 就直接用原来line925下面的else里面的函数。
 
/Users/easemob/Downloads/CF9D3C93-D897-4441-9141-31652DC91878.png
/Users/easemob/Downloads/CF9D3C93-D897-4441-9141-31652DC91878.png
PUT /{org_name}/{app_name}/users/{username}  修改用户昵称
通过上面的接口修改  notifier_name 和 device_token ~~~
这样实现了环信内用户跟当前设备的绑定,然后就通了,用户a(andro... 显示全部 »
PUT /{org_name}/{app_name}/users/{username}  修改用户昵称
通过上面的接口修改  notifier_name 和 device_token ~~~
这样实现了环信内用户跟当前设备的绑定,然后就通了,用户a(android客户端)发送消息给用户b(ios客户端),b能够收到离线通知,离线通知用https://github.com/zo0r/react-native-push-notification这个实现
至此该问题解决了~~~
lizg

lizg 回答了问题 • 2017-03-28 11:44 • 5 个回复 不感兴趣

web im视频聊天

赞同来自:

web sdk 实时视频,需要支持https + webkit 浏览器
web sdk 实时视频,需要支持https + webkit 浏览器
现在的逻辑就是登录成功跳转聊天主页面
https://webim.easemob.com/   这是demo地址
 
具体实现逻辑,请参考
demo/javascript/src/chat/chat.js
onOpened: function () {
... 显示全部 »
现在的逻辑就是登录成功跳转聊天主页面
https://webim.easemob.com/   这是demo地址
 
具体实现逻辑,请参考
demo/javascript/src/chat/chat.js
onOpened: function () {
me.props.update({
signIn: false,
signUp: false,
chat: true,
loadingStatus: 'hide'
});
// blacklist and it's callback call updateRoster
me.getBlacklist();
me.getGroup();
me.getChatroom();
Demo.conn.errorType = -1;
},
 
xianghenggang

xianghenggang 回答了问题 • 2017-05-08 14:36 • 1 个回复 不感兴趣

android环信怎么和微信小程序聊天

赞同来自:

可以实现,但是目前的问题就是语音那里有点问题
可以实现,但是目前的问题就是语音那里有点问题
您好,需要开通多端同步才可以,您可以在官网右下角联系商务同事
您好,需要开通多端同步才可以,您可以在官网右下角联系商务同事
木云落

木云落 回答了问题 • 2017-05-09 12:32 • 1 个回复 不感兴趣

我用的reactnative,apns离线推送如何做

赞同来自:

目前环信reactnative 还无法实现推送 
目前环信reactnative 还无法实现推送 
可以参考react-native的集成方案,随便改一改就出来了
7659
可以参考react-native的集成方案,随便改一改就出来了
7659
看一下你在初始化connection对象的时候,有没有设置apiUrl

7866
 
看一下你在初始化connection对象的时候,有没有设置apiUrl

7866
 
空山

空山 回答了问题 • 2017-08-19 00:08 • 2 个回复 不感兴趣

环信 Web IM 获取好友列表失败

赞同来自:

自己排查源码找到原因了, 折腾了一天累觉不爱, 强迫症害人不浅
环信的conn实例的登录状态 states 更改, 不是在登录方法 open 的回调函数中执行, 而是在 全局的监听函数 conn.listen({ onOpened(){}}) 中执行, 并且重... 显示全部 »
自己排查源码找到原因了, 折腾了一天累觉不爱, 强迫症害人不浅
环信的conn实例的登录状态 states 更改, 不是在登录方法 open 的回调函数中执行, 而是在 全局的监听函数 conn.listen({ onOpened(){}}) 中执行, 并且重点是, onOpened 事件并不是在 open(success)钩子后同步执行,而是有一个延迟很大的异步,  因此如果包装 conn.open({success(){}}) 为promise, 则会出现各种奇怪错误, 报错为未登录, 比如 使用了 strophe 库的 getRouset, 但是 使用 ajax 的方法 getGroup 又不受影响
字符串匹配,匹配到后去表情库做对比,一样就取文件出来展示
 
字符串匹配,匹配到后去表情库做对比,一样就取文件出来展示
 
kssion

kssion 回答了问题 • 2017-11-21 18:09 • 1 个回复 不感兴趣

环信关于小程序成功发送消息后的不执行success

赞同来自:

问题在于文件 utils/sdk/src/connection.js
js的类型判断有问题
找到 connection.js 第 1495 行左右[code]_msgHash[id].msg.success instanceof Function &&... 显示全部 »
问题在于文件 utils/sdk/src/connection.js
js的类型判断有问题
找到 connection.js 第 1495 行左右[code]_msgHash[id].msg.success instanceof Function && _msgHash .msg.success(id, mid); 修改为 [code]var success = _msgHash .msg.success;
if (typeof success == 'function') {
success(id, mid);
}
CallDonger

CallDonger 回答了问题 • 2017-11-15 15:32 • 3 个回复 不感兴趣

用vue发送消息,报源码的$msg is not defined

赞同来自:

你好,我也碰到了这个问题,请问你解决了吗?
你好,我也碰到了这个问题,请问你解决了吗?
beyond

beyond 回答了问题 • 2018-07-23 10:54 • 2 个回复 不感兴趣

环信 回调 type = 8是什么意思,求大佬解答

赞同来自:

8 WEBIM_CONNCTION_SERVER_ERROR 多端登录,被踢下线
 
更多webim错误码请查看http://docs.easemob.com/im/450errorcode/40weberrorcode
8 WEBIM_CONNCTION_SERVER_ERROR 多端登录,被踢下线
 
更多webim错误码请查看http://docs.easemob.com/im/450errorcode/40weberrorcode
这个如果没有做其他操作,是直接onOpened后,就被踢了, 那么就看下sdk的 版本,如果使用的是 1.4.11    直接更新sdk版本就可以解决了
这个如果没有做其他操作,是直接onOpened后,就被踢了, 那么就看下sdk的 版本,如果使用的是 1.4.11    直接更新sdk版本就可以解决了
beyond

beyond 回答了问题 • 2018-12-29 11:00 • 2 个回复 不感兴趣

如何获取好友昵称

赞同来自:

环信没有存昵称头像等信息,需要自己服务存,用扩展信息传过来。
环信没有存昵称头像等信息,需要自己服务存,用扩展信息传过来。
发送消息不执行success,拿不到服务器返回的 serverId 就是失败
已读未读可以设置的,onReadMessage 回调,还有 demo 中app.js 有ackMessage 方法 ,发送已读回执
发送消息不执行success,拿不到服务器返回的 serverId 就是失败
已读未读可以设置的,onReadMessage 回调,还有 demo 中app.js 有ackMessage 方法 ,发送已读回执
断网重来
切换到微信,或者切换到其他的app
然后再返回到小程序就会进行重连
断网重来
切换到微信,或者切换到其他的app
然后再返回到小程序就会进行重连
这个要接收消息必须设置 setPresence    小程序sdk 需要
这个要接收消息必须设置 setPresence    小程序sdk 需要
真的是比较尴尬
测试app发给web 的消息,直接用地址就不行,必须转成mp3 格式
每条audio 都要转成mp3 ,可以直接用环信提供的方法处理
真的是比较尴尬
测试app发给web 的消息,直接用地址就不行,必须转成mp3 格式
每条audio 都要转成mp3 ,可以直接用环信提供的方法处理
lizg

lizg 回答了问题 • 2020-06-02 10:12 • 1 个回复 不感兴趣

请问 h5 端有直播间吗

赞同来自:

就是用多人会议实现,看一下新创建会议的接口
可以设置主播和观众
然后使用环信聊天室做交流实现
就是用多人会议实现,看一下新创建会议的接口
可以设置主播和观众
然后使用环信聊天室做交流实现
2
评论

基于环信sdk在uni-app框架中快速开发一款多平台社交Demo SDK uni_app 环信

beyond 发表了文章 • 556 次浏览 • 2020-05-11 11:34 • 来自相关话题

说在前面:此款 demo 是基于 环信sdk 开发的一款具有单聊、群聊、聊天室、音视频等功能的应用。在此之前我们已经开发完 Vue、react(web端)、微信小程序。这三个热门领域的版本,如有需要源码可以后台留言索取。





 
安装开发工具

我们选用微信小程序来用做示例(如果选择百度、支付宝安装对应开发者工具即可)、

微信开发者工具建议还是安装最新版的。uni-app的开发也必须安装HBuilderX工具,这个是捆绑的,没得选择。要用uni-app,你必须得装!

工具安装:

微信开发者工具

HBuilderX

项目demo介绍:






项目demo启动预览:





 
快速集成环信 sdk:

1、复制整个utils文件






如果你想具体了解主要配置文件 请看这个链接:

http://docs-im.easemob.com/im/web/intro/start

2、如何使用环信的appkey ,可以在环信 console 后台注册一个 账号申请appkey ,可以参考这里 ,获取到  appkey 以后添加到配置文件中 ,如下图所示:






以上两个重要的配置准备完成之后就可以进行一系列的操作了(收发消息、好友申请、进群入群通知等)

在uni-app中 使用环信 sdk 实现添加、删除好友:

1、在全局 App.vue 文件 钩子函数 onLaunch() 中监听各种事件 (好友申请、收到各类消息等)如图:






发送好友请求:






在onPresence(message)事件中接收到好友消息申请:






同意好友请求:






拒绝好友请求:






实现收发消息:

1、给好友发送消息:






2、接收到消息:

在onTextMessage(message)事件中接收到好友消息,然后做消息上屏处理(具体消息上屏逻辑可看demo中代码示例):





以上展示的仅仅为基本业务场景,更多的业务逻辑详情请看demo示例。api具体详情可以查看 环信sdk 文档

最后结语:基于uni-app这个框架可实现多平台, 虽然目前一期集成环信sdk的版本仅支持微信小程序版本,但二期我们将加入头条、支付宝等小程序,敬请期待。PS:对于安卓、ios移动端,我们建议使用针对移动端开发的sdk版本。

基于uni-app的开发其中也趟了不少坑,在这里就不多赘述了。回归到框架的选型来讲,选用uni-app开发小程序,可同时并行多端小程序,这点是真香,一次开发多端发布。至于审核嘛~ 时快时慢。 查看全部
说在前面:此款 demo 是基于 环信sdk 开发的一款具有单聊、群聊、聊天室、音视频等功能的应用。在此之前我们已经开发完 Vue、react(web端)、微信小程序。这三个热门领域的版本,如有需要源码可以后台留言索取。

1.jpg

 
安装开发工具

我们选用微信小程序来用做示例(如果选择百度、支付宝安装对应开发者工具即可)、

微信开发者工具建议还是安装最新版的。uni-app的开发也必须安装HBuilderX工具,这个是捆绑的,没得选择。要用uni-app,你必须得装!

工具安装:

微信开发者工具

HBuilderX

项目demo介绍:

2.jpg


项目demo启动预览:

3.jpg

 
快速集成环信 sdk:

1、复制整个utils文件

4.jpg


如果你想具体了解主要配置文件 请看这个链接:

http://docs-im.easemob.com/im/web/intro/start

2、如何使用环信的appkey ,可以在环信 console 后台注册一个 账号申请appkey ,可以参考这里 ,获取到  appkey 以后添加到配置文件中 ,如下图所示:

5.jpg


以上两个重要的配置准备完成之后就可以进行一系列的操作了(收发消息、好友申请、进群入群通知等)

在uni-app中 使用环信 sdk 实现添加、删除好友:

1、在全局 App.vue 文件 钩子函数 onLaunch() 中监听各种事件 (好友申请、收到各类消息等)如图:

6.jpg


发送好友请求:

7.jpg


在onPresence(message)事件中接收到好友消息申请:

8.jpg


同意好友请求:

9.jpg


拒绝好友请求:

10.jpg


实现收发消息:

1、给好友发送消息:

11.jpg


2、接收到消息:

在onTextMessage(message)事件中接收到好友消息,然后做消息上屏处理(具体消息上屏逻辑可看demo中代码示例):

12.jpg

以上展示的仅仅为基本业务场景,更多的业务逻辑详情请看demo示例。api具体详情可以查看 环信sdk 文档

最后结语:基于uni-app这个框架可实现多平台, 虽然目前一期集成环信sdk的版本仅支持微信小程序版本,但二期我们将加入头条、支付宝等小程序,敬请期待。PS:对于安卓、ios移动端,我们建议使用针对移动端开发的sdk版本。

基于uni-app的开发其中也趟了不少坑,在这里就不多赘述了。回归到框架的选型来讲,选用uni-app开发小程序,可同时并行多端小程序,这点是真香,一次开发多端发布。至于审核嘛~ 时快时慢。
1
评论

手把手教程:4小时开发一个视频会议APP【附开源代码】 环信 开源 视频会议

fat1 发表了文章 • 1054 次浏览 • 2020-04-17 00:29 • 来自相关话题

今年是不平凡的一年,没错,就是因为疫情,因为疫情原因 ,大家只能呆着家里,严重影响了我们正常的学习 生活 工作,在这种情况下,只能在家办公,这时候大家就会想到线上视频会议,目前很多互联网公司有这个产品,比较出名的就比如 腾讯会议 钉钉 zoom等,用这些是很方便,但是如果能开发自己的视频会议,那会不会更好或者是更有成就感,下面简单介绍这个这个项目,和大概的开发过程。本项目基于环信音视频云来完成,实现的主要功能有:
  创建会议、删除会议、获取指定会议室详情、加入会议室、退出会议室等关于会议的管理 ;  获取会议室参会人名列表、踢人,设置观众为主播,设置主播为观众等关于会议室的人员管理;  共享桌面(web端);
 三个端的实现:Android,iOS,Web

上面这些功能在项目中都已经实现。还有水印 ,变声等高级功能在环信音视频SDK的接口内部都已经封装好,本项目没有实现 ,大家可以自行去实现。有关多人音视频功能更详细的介绍大家可以参考:这儿。多人音视频实现的实现主要有以下一些场景:社交交友,远程心理咨询、远程医疗、一对一在线教育、远程视频辅助等。咳咳 ,接下来就是纯干货了,给大家介绍我是如何一步步开发出一个完整的多人音视频app。
 
项目截图
 
首先给大家展示下项目运行的效果图,会议界面 主窗口是一个大的 RelativeLayout ,最下面的那一排排小窗口是的实现方法是HorizontalScrollView加上一个开源的组件 com.jaouan.compoundlayout.RadioLayoutGroup 实现的,点击下面的小窗口后,可以 把小窗口的视频流显示在大屏上,具体是调用 updateRemoteSurfaceView(String streamId, EMCallSurfaceView remoteView)来更新SurfaceView,具体的细节大家可以看看代码里面的实现 最后会公布代码开源地址。
















准备工作
    大家得下载安装Android Studio,配置好Android 开发环境,怎么详细配置我就在这不再细说了 网上有很多的教程,大家自己可以找找看,然后大家可以看看环信多人音视频会议的主要功能和一些基本概念介绍。
集成 
  1. 首先大家会想问怎么调用环信的SDK ,大家可以使用 远程依赖SDK包,建议大家用最新版本的远程依赖:
     com.hyphenate:hyphenate-sdk:3.6.6 ,依赖包可以放在 build.gradle里面的 dependencies 选项下面,如下图所示





2.其次怎么使用环信的appkey ,可以在环信音视频云后台注册一个 账号申请appkey ,可以参考这里 ,获取到  appkey 以后添加到AndroidManifest.xml中 ,如下图所示:






3.经过以上两个重要的前期配置准备 ,接下来我们就可以开始进行代码开发了,首先我们先创建一个项目的DemoApplication类和      DemoHelper类,DemoApplication 类和DemoHelper类都是一个单例类 ,DemoApplication 主要功能就是进行DemoHelper 的初始化,而DemoHelper里面主要是主要有一些option 配置和EMClient 进行初始化,代码如下所示:public void init(Context context) {
EMOptions options = initChatOptions(context);
EMClient.getInstance().init(context, options);
PreferenceManager.init(context);
}
  DemoHelper还有一个重要的功能就是设置  EMConferenceListener 进行会议监听,有关 EMConferenceListener的类的详细介绍 ,通过这个监听可以再加入会议的时候获取到已经在会议中的流和主播信息,分别是通过其中以下两个回调获取:@Override
public void onMemberJoined(EMConferenceMember member){

}

@Override
public void onStreamAdded(EMConferenceStream stream){

}
4.DemoApplication类完成以后,接下来就是怎么去登陆 环信IM 账号和 创建加入会议房间了,首次安装的时候都没有账号,我们使用的办法是自动注册一个账号 在本地进行保存,然后进行登录 ,注册 登录详细接口请看 这儿,  注册 登录的调用大概如下所示: 
 try {
        //注册一个环信ID
        EMClient.getInstance().createAccount(username, password);
            
        //注册成功进行登录
        PreferenceManager.getInstance().setCurrentUserName(username);
        PreferenceManager.getInstance().setCurrentuserPassword(password);
        login();
    } catch (final HyphenateException e) {
       runOnUiThread(new Runnable() {
               public void run() {
                  int errorCode=e.getErrorCode();
                   if(errorCode==EMError.NETWORK_ERROR){
                    Toast.makeText(getApplicationContext(), getResources().getString(R.string.network_anomalies), Toast.LENGTH_SHORT).show();
      }
   }   
}
 
  public void login() {
 
        //登录已经注册成功的环信ID
        EMClient.getInstance().login(username, password, new EMCallBack() {
            @Override
            public void onSuccess() {
                Log.d(TAG, "login: onSuccess");
                //登录成功进入会议房间
                joinRoom();
            }
            @Override
            public void onProgress(int progress, String status) {
                Log.d(TAG, "login: onProgress");
            }
            @Override
            public void onError(final int code, final String message) {
                Log.d(TAG, "login: onError: " + code);
                runOnUiThread(new Runnable() {
                    public void run() {
                        Toast.makeText(getApplicationContext(), getString(R.string.Login_failed) + message,Toast.LENGTH_SHORT).show();
                    }
                });
            }
        });
    }
登录完成以后,我们可以根据房间名创建并加入房间,主要代码大概如下: EMClient.getInstance().conferenceManager().joinRoom(currentRoomname, currentPassword, conferenceRole,roomConfig, new EMValueCallBack<EMConference>(){
@Override
public void onSuccess(EMConference value) {
EMLog.i(TAG, "join conference success");
Intent intent = new Intent(MainActivity.this, ConferenceActivity.class);
startActivity(intent);
finish();
}
@Override
public void onError(final int error, final String errorMsg) {
EMLog.e(TAG, "join conference failed error " + error + ", msg " + errorMsg);
runOnUiThread(new Runnable() {
@Override
public void run() {
setBtnEnable(true);
if(error == CALL_TALKER_ISFULL) {
takerFullDialogDisplay();
}else{
Toast.makeText(getApplicationContext(), "Join conference failed " + error + " " + errorMsg, Toast.LENGTH_SHORT).show();
}
}
});
}
});
EMClient.getInstance().conferenceManager().joinRoom() API可以根据房间名创建指定会议,当以该房间名命名的会议不存在时候,会直接创建,当会议已经创建好 可以根据正确的房间名和密码加入房间 ,到这一步为止,我们已经成功的创建 并加入会议。
5.加入会议以后我们进入到会议界面,展示从DemoHelper类 EMConferenceListener 中的 onStreamAdded 回调 和 onMemberJoined 获取到的流和主播列表 ,在ConferenceActivity 中实现 EMConferenceListener ,然后直接把 ConferenceActivity 注册监听,用以下方法  EMClient.getInstance().conferenceManager().addConferenceListener(this); 这样就可实现 EMConferenceListener 事件的处理,比如
 主播 进出房间 :
public void onMemberJoined(final EMConferenceMember member);
public void onMemberExited(final EMConferenceMember member);

增加流 移除流:
public void onStreamAdded(final EMConferenceStream stream)
 public void onStreamRemoved(final EMConferenceStream stream)

管理员变更: 
public void onAdminAdded(String streamId) ;  
public void onAdminRemoved(String streamId)

角色变更  用户被踢  谁在说话等各种回调,可以处理各种业务逻辑 ,详细的请参考 项目中的实现 ,最后会附上项目的开源地址。

6 进入会议房间以后如果用户角色为主播可以进行发布视频流 ,观众只能订阅视频流 不能发布视频流 ,可以调用SDK的publish接口发布流,该接口用到了EMStreamParam参数,你可以自由配置,比如是否上传视频,是否上传音频,使用前置或后置摄像头,视频码率,显示视频页面等等,具体实现可以参考 中发布 订阅视频流的内容, 关于以上的代码逻辑如以 如以下: //发布视频流
normalParam = new EMStreamParam();
normalParam.setStreamType(EMConferenceStream.StreamType.NORMAL);
normalParam.setVideoOff(true);
normalParam.setAudioOff(true);

EMClient.getInstance().conferenceManager().publish(normalParam, new EMValueCallBack<String>() {
@Override
public void onSuccess(String value) {
conference.setPubStreamId(value, EMConferenceStream.StreamType.NORMAL);
addOrUpdateStreamList("local-stream", value);

PhoneStateManager.get(ConferenceActivity.this).addStateCallback(phoneStateCallback);
}

@Override
public void onError(int error, String errorMsg) {
EMLog.i(TAG, "publish failed: error=" + error + ", msg=" + errorMsg);
}
});
//订阅其他主播的视频流
private void subscribe(EMConferenceStream stream, EMCallSurfaceView surfaceView) {
EMClient.getInstance().conferenceManager().subscribe(stream, surfaceView, new EMValueCallBack<String>() {
@Override
public void onSuccess(String value) {
}

@Override
public void onError(int error, String errorMsg) {

}
});
}
7.有关上麦 下麦 的逻辑处理,观众可以请求上麦成为主播,主播可以下麦成为观众,上麦 下麦 是利用 EMConferenceAttribute进行处理 ,EMConferenceAttribute  是一个事件广播,广播事件是一个key-value格式,key-value 可以由开发者进行自行定义,增添事件以后 ,服务器会把事件进行广播。会议中成员会收到 onAttributesUpdated回调。例如本项目中的会议上麦 下麦 代码如下所示://上麦申请

EMClient.getInstance().conferenceManager().setConferenceAttribute(
EMClient.getInstance().getCurrentUser(),
"request_tobe_speaker",
new EMValueCallBack<Void>() {
@Override
public void onSuccess(Void value) {
EMLog.i(TAG, "request_tobe_speaker scuessed");

}

@Override
public void onError(int error, String errorMsg) {
EMLog.i(TAG, "request_tobe_speaker failed: error=" + error

}
});

//下麦申请
EMClient.getInstance().conferenceManager().setConferenceAttribute(EMClient.getInstance().getCurrentUser()
, "request_tobe_audience", new EMValueCallBack<Void>() {
@Override
public void onSuccess(Void value) {
EMLog.i(TAG, "request_tobe_audience scuessed");
}

@Override
public void onError(int error, String errorMsg) {
EMLog.i(TAG, "request_tobe_audience failed: error=" + error + ", msg=" + errorMsg);
}
});
 上麦 下麦 请求发出以后 只能由主持人去处理,处理在 EMConferenceListener  的回调 onAttributesUpdated(EMConferenceAttribute attributes) 去处理 ,收到回调以后 解析attributes 然后进行处理请求,处理的过程代码大概如下: EMClient.getInstance().conferenceManager().grantRole(conference.getConferenceId()
, new EMConferenceMember(memName, null, null,null)
, EMConferenceManager.EMConferenceRole.Talker, new EMValueCallBack<String>() {
@Override
public void onSuccess(String value) {
EMLog.i(TAG, " requestTalkerDisplay request_tobe_speaker changeRole success, result: " + value);
dialog.dismiss();
}
@Override
public void onError(int error, String errorMsg) {
EMLog.i(TAG, " requestTalkerDisplay request_tobe_speaker changeRole failed, error: " + error + " - " + errorMsg);

}
});
下麦也是和上麦一样是利用 EMConferenceAttribute进行处理。

9.有关退出会议 销毁会议 普通主播  观众只能退出会议 ,主持人还可以 销毁会议 正在进行中的会议可以进行销毁,退出会议 销毁会议 具体代码如下: EMClient.getInstance().conferenceManager().exitConference(new EMValueCallBack() {
@Override
public void onSuccess(Object value) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(getApplicationContext(), "您已成功退出当前会议!", Toast.LENGTH_SHORT).show();
}
});
}

@Override
public void onError(int error, String errorMsg) {
EMLog.i(TAG, "exit conference failed " + error + ", " + errorMsg);
}
});

EMClient.getInstance().conferenceManager().destroyConference(new EMValueCallBack() {
@Override
public void onSuccess(Object value) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(getApplicationContext(), "您已成功销毁当前会议!", Toast.LENGTH_SHORT).show();
}
});
EMLog.i(TAG, "finish ConferenceActivity");
finish();
}

尾语
至此 整个 多人音视频会议开发的详细步骤 已经完成 ,虽然比较麻烦 但是每个步骤都很清晰 ,有不太清楚的欢迎大家积极讨论, 附上本项目的github地址:https://github.com/easemob/videocall-android  欢迎大家积极参与 ,谢谢支持。
Demo下载 二维码如下 欢迎大家体验(iOS版和web版下载地址请见:https://www.easemob.com/download/rtc)






  查看全部
今年是不平凡的一年,没错,就是因为疫情,因为疫情原因 ,大家只能呆着家里,严重影响了我们正常的学习 生活 工作,在这种情况下,只能在家办公,这时候大家就会想到线上视频会议,目前很多互联网公司有这个产品,比较出名的就比如 腾讯会议 钉钉 zoom等,用这些是很方便,但是如果能开发自己的视频会议,那会不会更好或者是更有成就感,下面简单介绍这个这个项目,和大概的开发过程。本项目基于环信音视频云来完成,实现的主要功能有:
  •   创建会议、删除会议、获取指定会议室详情、加入会议室、退出会议室等关于会议的管理 ;
  •   获取会议室参会人名列表、踢人,设置观众为主播,设置主播为观众等关于会议室的人员管理;
  •   共享桌面(web端);

 三个端的实现:Android,iOS,Web

上面这些功能在项目中都已经实现。还有水印 ,变声等高级功能在环信音视频SDK的接口内部都已经封装好,本项目没有实现 ,大家可以自行去实现。有关多人音视频功能更详细的介绍大家可以参考:这儿。多人音视频实现的实现主要有以下一些场景:社交交友,远程心理咨询、远程医疗、一对一在线教育、远程视频辅助等。咳咳 ,接下来就是纯干货了,给大家介绍我是如何一步步开发出一个完整的多人音视频app。
 
项目截图
 
首先给大家展示下项目运行的效果图,会议界面 主窗口是一个大的 RelativeLayout ,最下面的那一排排小窗口是的实现方法是HorizontalScrollView加上一个开源的组件 com.jaouan.compoundlayout.RadioLayoutGroup 实现的,点击下面的小窗口后,可以 把小窗口的视频流显示在大屏上,具体是调用 updateRemoteSurfaceView(String streamId, EMCallSurfaceView remoteView)来更新SurfaceView,具体的细节大家可以看看代码里面的实现 最后会公布代码开源地址。

20200410140627956.jpg


20200410140855852.jpg


20200410141055394.jpg


准备工作
    大家得下载安装Android Studio,配置好Android 开发环境,怎么详细配置我就在这不再细说了 网上有很多的教程,大家自己可以找找看,然后大家可以看看环信多人音视频会议的主要功能和一些基本概念介绍。
集成 
  1. 首先大家会想问怎么调用环信的SDK ,大家可以使用 远程依赖SDK包,建议大家用最新版本的远程依赖:
     com.hyphenate:hyphenate-sdk:3.6.6 ,依赖包可以放在 build.gradle里面的 dependencies 选项下面,如下图所示
2020040917363212.png


2.其次怎么使用环信的appkey ,可以在环信音视频云后台注册一个 账号申请appkey ,可以参考这里 ,获取到  appkey 以后添加到AndroidManifest.xml中 ,如下图所示:

20200409213959916.png


3.经过以上两个重要的前期配置准备 ,接下来我们就可以开始进行代码开发了,首先我们先创建一个项目的DemoApplication类和      DemoHelper类,DemoApplication 类和DemoHelper类都是一个单例类 ,DemoApplication 主要功能就是进行DemoHelper 的初始化,而DemoHelper里面主要是主要有一些option 配置和EMClient 进行初始化,代码如下所示:
public void init(Context context) {
EMOptions options = initChatOptions(context);
EMClient.getInstance().init(context, options);
PreferenceManager.init(context);
}

  DemoHelper还有一个重要的功能就是设置  EMConferenceListener 进行会议监听,有关 EMConferenceListener的类的详细介绍 ,通过这个监听可以再加入会议的时候获取到已经在会议中的流和主播信息,分别是通过其中以下两个回调获取:
@Override 
public void onMemberJoined(EMConferenceMember member){

}

@Override
public void onStreamAdded(EMConferenceStream stream){

}

4.DemoApplication类完成以后,接下来就是怎么去登陆 环信IM 账号和 创建加入会议房间了,首次安装的时候都没有账号,我们使用的办法是自动注册一个账号 在本地进行保存,然后进行登录 ,注册 登录详细接口请看 这儿,  注册 登录的调用大概如下所示: 
 try {
        //注册一个环信ID
        EMClient.getInstance().createAccount(username, password);
            
        //注册成功进行登录
        PreferenceManager.getInstance().setCurrentUserName(username);
        PreferenceManager.getInstance().setCurrentuserPassword(password);
        login();
    } catch (final HyphenateException e) {
       runOnUiThread(new Runnable() {
               public void run() {
                  int errorCode=e.getErrorCode();
                   if(errorCode==EMError.NETWORK_ERROR){
                    Toast.makeText(getApplicationContext(), getResources().getString(R.string.network_anomalies), Toast.LENGTH_SHORT).show();
      }
   }   
}
 
  public void login() {
 
        //登录已经注册成功的环信ID
        EMClient.getInstance().login(username, password, new EMCallBack() {
            @Override
            public void onSuccess() {
                Log.d(TAG, "login: onSuccess");
                //登录成功进入会议房间
                joinRoom();
            }
            @Override
            public void onProgress(int progress, String status) {
                Log.d(TAG, "login: onProgress");
            }
            @Override
            public void onError(final int code, final String message) {
                Log.d(TAG, "login: onError: " + code);
                runOnUiThread(new Runnable() {
                    public void run() {
                        Toast.makeText(getApplicationContext(), getString(R.string.Login_failed) + message,Toast.LENGTH_SHORT).show();
                    }
                });
            }
        });
    }
登录完成以后,我们可以根据房间名创建并加入房间,主要代码大概如下:
 EMClient.getInstance().conferenceManager().joinRoom(currentRoomname, currentPassword, conferenceRole,roomConfig, new EMValueCallBack<EMConference>(){
@Override
public void onSuccess(EMConference value) {
EMLog.i(TAG, "join conference success");
Intent intent = new Intent(MainActivity.this, ConferenceActivity.class);
startActivity(intent);
finish();
}
@Override
public void onError(final int error, final String errorMsg) {
EMLog.e(TAG, "join conference failed error " + error + ", msg " + errorMsg);
runOnUiThread(new Runnable() {
@Override
public void run() {
setBtnEnable(true);
if(error == CALL_TALKER_ISFULL) {
takerFullDialogDisplay();
}else{
Toast.makeText(getApplicationContext(), "Join conference failed " + error + " " + errorMsg, Toast.LENGTH_SHORT).show();
}
}
});
}
});

EMClient.getInstance().conferenceManager().joinRoom() API可以根据房间名创建指定会议,当以该房间名命名的会议不存在时候,会直接创建,当会议已经创建好 可以根据正确的房间名和密码加入房间 ,到这一步为止,我们已经成功的创建 并加入会议。
5.加入会议以后我们进入到会议界面,展示从DemoHelper类 EMConferenceListener 中的 onStreamAdded 回调 和 onMemberJoined 获取到的流和主播列表 ,在ConferenceActivity 中实现 EMConferenceListener ,然后直接把 ConferenceActivity 注册监听,用以下方法  EMClient.getInstance().conferenceManager().addConferenceListener(this); 这样就可实现 EMConferenceListener 事件的处理,比如
 主播 进出房间 :
public void onMemberJoined(final EMConferenceMember member);
public void onMemberExited(final EMConferenceMember member);

增加流 移除流:
public void onStreamAdded(final EMConferenceStream stream)
 public void onStreamRemoved(final EMConferenceStream stream)

管理员变更: 
public void onAdminAdded(String streamId) ;  
public void onAdminRemoved(String streamId)

角色变更  用户被踢  谁在说话等各种回调,可以处理各种业务逻辑 ,详细的请参考 项目中的实现 ,最后会附上项目的开源地址。

6 进入会议房间以后如果用户角色为主播可以进行发布视频流 ,观众只能订阅视频流 不能发布视频流 ,可以调用SDK的publish接口发布流,该接口用到了EMStreamParam参数,你可以自由配置,比如是否上传视频,是否上传音频,使用前置或后置摄像头,视频码率,显示视频页面等等,具体实现可以参考 中发布 订阅视频流的内容, 关于以上的代码逻辑如以 如以下:
 //发布视频流
normalParam = new EMStreamParam();
normalParam.setStreamType(EMConferenceStream.StreamType.NORMAL);
normalParam.setVideoOff(true);
normalParam.setAudioOff(true);

EMClient.getInstance().conferenceManager().publish(normalParam, new EMValueCallBack<String>() {
@Override
public void onSuccess(String value) {
conference.setPubStreamId(value, EMConferenceStream.StreamType.NORMAL);
addOrUpdateStreamList("local-stream", value);

PhoneStateManager.get(ConferenceActivity.this).addStateCallback(phoneStateCallback);
}

@Override
public void onError(int error, String errorMsg) {
EMLog.i(TAG, "publish failed: error=" + error + ", msg=" + errorMsg);
}
});
//订阅其他主播的视频流
private void subscribe(EMConferenceStream stream, EMCallSurfaceView surfaceView) {
EMClient.getInstance().conferenceManager().subscribe(stream, surfaceView, new EMValueCallBack<String>() {
@Override
public void onSuccess(String value) {
}

@Override
public void onError(int error, String errorMsg) {

}
});
}

7.有关上麦 下麦 的逻辑处理,观众可以请求上麦成为主播,主播可以下麦成为观众,上麦 下麦 是利用 EMConferenceAttribute进行处理 ,EMConferenceAttribute  是一个事件广播,广播事件是一个key-value格式,key-value 可以由开发者进行自行定义,增添事件以后 ,服务器会把事件进行广播。会议中成员会收到 onAttributesUpdated回调。例如本项目中的会议上麦 下麦 代码如下所示:
//上麦申请  

EMClient.getInstance().conferenceManager().setConferenceAttribute(
EMClient.getInstance().getCurrentUser(),
"request_tobe_speaker",
new EMValueCallBack<Void>() {
@Override
public void onSuccess(Void value) {
EMLog.i(TAG, "request_tobe_speaker scuessed");

}

@Override
public void onError(int error, String errorMsg) {
EMLog.i(TAG, "request_tobe_speaker failed: error=" + error

}
});

//下麦申请
EMClient.getInstance().conferenceManager().setConferenceAttribute(EMClient.getInstance().getCurrentUser()
, "request_tobe_audience", new EMValueCallBack<Void>() {
@Override
public void onSuccess(Void value) {
EMLog.i(TAG, "request_tobe_audience scuessed");
}

@Override
public void onError(int error, String errorMsg) {
EMLog.i(TAG, "request_tobe_audience failed: error=" + error + ", msg=" + errorMsg);
}
});

 上麦 下麦 请求发出以后 只能由主持人去处理,处理在 EMConferenceListener  的回调 onAttributesUpdated(EMConferenceAttribute attributes) 去处理 ,收到回调以后 解析attributes 然后进行处理请求,处理的过程代码大概如下:
   EMClient.getInstance().conferenceManager().grantRole(conference.getConferenceId()
, new EMConferenceMember(memName, null, null,null)
, EMConferenceManager.EMConferenceRole.Talker, new EMValueCallBack<String>() {
@Override
public void onSuccess(String value) {
EMLog.i(TAG, " requestTalkerDisplay request_tobe_speaker changeRole success, result: " + value);
dialog.dismiss();
}
@Override
public void onError(int error, String errorMsg) {
EMLog.i(TAG, " requestTalkerDisplay request_tobe_speaker changeRole failed, error: " + error + " - " + errorMsg);

}
});

下麦也是和上麦一样是利用 EMConferenceAttribute进行处理。

9.有关退出会议 销毁会议 普通主播  观众只能退出会议 ,主持人还可以 销毁会议 正在进行中的会议可以进行销毁,退出会议 销毁会议 具体代码如下:
 EMClient.getInstance().conferenceManager().exitConference(new EMValueCallBack() {
@Override
public void onSuccess(Object value) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(getApplicationContext(), "您已成功退出当前会议!", Toast.LENGTH_SHORT).show();
}
});
}

@Override
public void onError(int error, String errorMsg) {
EMLog.i(TAG, "exit conference failed " + error + ", " + errorMsg);
}
});

EMClient.getInstance().conferenceManager().destroyConference(new EMValueCallBack() {
@Override
public void onSuccess(Object value) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(getApplicationContext(), "您已成功销毁当前会议!", Toast.LENGTH_SHORT).show();
}
});
EMLog.i(TAG, "finish ConferenceActivity");
finish();
}

尾语
至此 整个 多人音视频会议开发的详细步骤 已经完成 ,虽然比较麻烦 但是每个步骤都很清晰 ,有不太清楚的欢迎大家积极讨论, 附上本项目的github地址:https://github.com/easemob/videocall-android  欢迎大家积极参与 ,谢谢支持。
Demo下载 二维码如下 欢迎大家体验(iOS版和web版下载地址请见:https://www.easemob.com/download/rtc)


20200410142228530.png

 
2
评论

基于环信sdk在uni-app框架中快速开发一款多平台社交Demo SDK uni_app 环信

beyond 发表了文章 • 556 次浏览 • 2020-05-11 11:34 • 来自相关话题

说在前面:此款 demo 是基于 环信sdk 开发的一款具有单聊、群聊、聊天室、音视频等功能的应用。在此之前我们已经开发完 Vue、react(web端)、微信小程序。这三个热门领域的版本,如有需要源码可以后台留言索取。





 
安装开发工具

我们选用微信小程序来用做示例(如果选择百度、支付宝安装对应开发者工具即可)、

微信开发者工具建议还是安装最新版的。uni-app的开发也必须安装HBuilderX工具,这个是捆绑的,没得选择。要用uni-app,你必须得装!

工具安装:

微信开发者工具

HBuilderX

项目demo介绍:






项目demo启动预览:





 
快速集成环信 sdk:

1、复制整个utils文件






如果你想具体了解主要配置文件 请看这个链接:

http://docs-im.easemob.com/im/web/intro/start

2、如何使用环信的appkey ,可以在环信 console 后台注册一个 账号申请appkey ,可以参考这里 ,获取到  appkey 以后添加到配置文件中 ,如下图所示:






以上两个重要的配置准备完成之后就可以进行一系列的操作了(收发消息、好友申请、进群入群通知等)

在uni-app中 使用环信 sdk 实现添加、删除好友:

1、在全局 App.vue 文件 钩子函数 onLaunch() 中监听各种事件 (好友申请、收到各类消息等)如图:






发送好友请求:






在onPresence(message)事件中接收到好友消息申请:






同意好友请求:






拒绝好友请求:






实现收发消息:

1、给好友发送消息:






2、接收到消息:

在onTextMessage(message)事件中接收到好友消息,然后做消息上屏处理(具体消息上屏逻辑可看demo中代码示例):





以上展示的仅仅为基本业务场景,更多的业务逻辑详情请看demo示例。api具体详情可以查看 环信sdk 文档

最后结语:基于uni-app这个框架可实现多平台, 虽然目前一期集成环信sdk的版本仅支持微信小程序版本,但二期我们将加入头条、支付宝等小程序,敬请期待。PS:对于安卓、ios移动端,我们建议使用针对移动端开发的sdk版本。

基于uni-app的开发其中也趟了不少坑,在这里就不多赘述了。回归到框架的选型来讲,选用uni-app开发小程序,可同时并行多端小程序,这点是真香,一次开发多端发布。至于审核嘛~ 时快时慢。 查看全部
说在前面:此款 demo 是基于 环信sdk 开发的一款具有单聊、群聊、聊天室、音视频等功能的应用。在此之前我们已经开发完 Vue、react(web端)、微信小程序。这三个热门领域的版本,如有需要源码可以后台留言索取。

1.jpg

 
安装开发工具

我们选用微信小程序来用做示例(如果选择百度、支付宝安装对应开发者工具即可)、

微信开发者工具建议还是安装最新版的。uni-app的开发也必须安装HBuilderX工具,这个是捆绑的,没得选择。要用uni-app,你必须得装!

工具安装:

微信开发者工具

HBuilderX

项目demo介绍:

2.jpg


项目demo启动预览:

3.jpg

 
快速集成环信 sdk:

1、复制整个utils文件

4.jpg


如果你想具体了解主要配置文件 请看这个链接:

http://docs-im.easemob.com/im/web/intro/start

2、如何使用环信的appkey ,可以在环信 console 后台注册一个 账号申请appkey ,可以参考这里 ,获取到  appkey 以后添加到配置文件中 ,如下图所示:

5.jpg


以上两个重要的配置准备完成之后就可以进行一系列的操作了(收发消息、好友申请、进群入群通知等)

在uni-app中 使用环信 sdk 实现添加、删除好友:

1、在全局 App.vue 文件 钩子函数 onLaunch() 中监听各种事件 (好友申请、收到各类消息等)如图:

6.jpg


发送好友请求:

7.jpg


在onPresence(message)事件中接收到好友消息申请:

8.jpg


同意好友请求:

9.jpg


拒绝好友请求:

10.jpg


实现收发消息:

1、给好友发送消息:

11.jpg


2、接收到消息:

在onTextMessage(message)事件中接收到好友消息,然后做消息上屏处理(具体消息上屏逻辑可看demo中代码示例):

12.jpg

以上展示的仅仅为基本业务场景,更多的业务逻辑详情请看demo示例。api具体详情可以查看 环信sdk 文档

最后结语:基于uni-app这个框架可实现多平台, 虽然目前一期集成环信sdk的版本仅支持微信小程序版本,但二期我们将加入头条、支付宝等小程序,敬请期待。PS:对于安卓、ios移动端,我们建议使用针对移动端开发的sdk版本。

基于uni-app的开发其中也趟了不少坑,在这里就不多赘述了。回归到框架的选型来讲,选用uni-app开发小程序,可同时并行多端小程序,这点是真香,一次开发多端发布。至于审核嘛~ 时快时慢。
1
评论

手把手教程:4小时开发一个视频会议APP【附开源代码】 环信 开源 视频会议

fat1 发表了文章 • 1054 次浏览 • 2020-04-17 00:29 • 来自相关话题

今年是不平凡的一年,没错,就是因为疫情,因为疫情原因 ,大家只能呆着家里,严重影响了我们正常的学习 生活 工作,在这种情况下,只能在家办公,这时候大家就会想到线上视频会议,目前很多互联网公司有这个产品,比较出名的就比如 腾讯会议 钉钉 zoom等,用这些是很方便,但是如果能开发自己的视频会议,那会不会更好或者是更有成就感,下面简单介绍这个这个项目,和大概的开发过程。本项目基于环信音视频云来完成,实现的主要功能有:
  创建会议、删除会议、获取指定会议室详情、加入会议室、退出会议室等关于会议的管理 ;  获取会议室参会人名列表、踢人,设置观众为主播,设置主播为观众等关于会议室的人员管理;  共享桌面(web端);
 三个端的实现:Android,iOS,Web

上面这些功能在项目中都已经实现。还有水印 ,变声等高级功能在环信音视频SDK的接口内部都已经封装好,本项目没有实现 ,大家可以自行去实现。有关多人音视频功能更详细的介绍大家可以参考:这儿。多人音视频实现的实现主要有以下一些场景:社交交友,远程心理咨询、远程医疗、一对一在线教育、远程视频辅助等。咳咳 ,接下来就是纯干货了,给大家介绍我是如何一步步开发出一个完整的多人音视频app。
 
项目截图
 
首先给大家展示下项目运行的效果图,会议界面 主窗口是一个大的 RelativeLayout ,最下面的那一排排小窗口是的实现方法是HorizontalScrollView加上一个开源的组件 com.jaouan.compoundlayout.RadioLayoutGroup 实现的,点击下面的小窗口后,可以 把小窗口的视频流显示在大屏上,具体是调用 updateRemoteSurfaceView(String streamId, EMCallSurfaceView remoteView)来更新SurfaceView,具体的细节大家可以看看代码里面的实现 最后会公布代码开源地址。
















准备工作
    大家得下载安装Android Studio,配置好Android 开发环境,怎么详细配置我就在这不再细说了 网上有很多的教程,大家自己可以找找看,然后大家可以看看环信多人音视频会议的主要功能和一些基本概念介绍。
集成 
  1. 首先大家会想问怎么调用环信的SDK ,大家可以使用 远程依赖SDK包,建议大家用最新版本的远程依赖:
     com.hyphenate:hyphenate-sdk:3.6.6 ,依赖包可以放在 build.gradle里面的 dependencies 选项下面,如下图所示





2.其次怎么使用环信的appkey ,可以在环信音视频云后台注册一个 账号申请appkey ,可以参考这里 ,获取到  appkey 以后添加到AndroidManifest.xml中 ,如下图所示:






3.经过以上两个重要的前期配置准备 ,接下来我们就可以开始进行代码开发了,首先我们先创建一个项目的DemoApplication类和      DemoHelper类,DemoApplication 类和DemoHelper类都是一个单例类 ,DemoApplication 主要功能就是进行DemoHelper 的初始化,而DemoHelper里面主要是主要有一些option 配置和EMClient 进行初始化,代码如下所示:public void init(Context context) {
EMOptions options = initChatOptions(context);
EMClient.getInstance().init(context, options);
PreferenceManager.init(context);
}
  DemoHelper还有一个重要的功能就是设置  EMConferenceListener 进行会议监听,有关 EMConferenceListener的类的详细介绍 ,通过这个监听可以再加入会议的时候获取到已经在会议中的流和主播信息,分别是通过其中以下两个回调获取:@Override
public void onMemberJoined(EMConferenceMember member){

}

@Override
public void onStreamAdded(EMConferenceStream stream){

}
4.DemoApplication类完成以后,接下来就是怎么去登陆 环信IM 账号和 创建加入会议房间了,首次安装的时候都没有账号,我们使用的办法是自动注册一个账号 在本地进行保存,然后进行登录 ,注册 登录详细接口请看 这儿,  注册 登录的调用大概如下所示: 
 try {
        //注册一个环信ID
        EMClient.getInstance().createAccount(username, password);
            
        //注册成功进行登录
        PreferenceManager.getInstance().setCurrentUserName(username);
        PreferenceManager.getInstance().setCurrentuserPassword(password);
        login();
    } catch (final HyphenateException e) {
       runOnUiThread(new Runnable() {
               public void run() {
                  int errorCode=e.getErrorCode();
                   if(errorCode==EMError.NETWORK_ERROR){
                    Toast.makeText(getApplicationContext(), getResources().getString(R.string.network_anomalies), Toast.LENGTH_SHORT).show();
      }
   }   
}
 
  public void login() {
 
        //登录已经注册成功的环信ID
        EMClient.getInstance().login(username, password, new EMCallBack() {
            @Override
            public void onSuccess() {
                Log.d(TAG, "login: onSuccess");
                //登录成功进入会议房间
                joinRoom();
            }
            @Override
            public void onProgress(int progress, String status) {
                Log.d(TAG, "login: onProgress");
            }
            @Override
            public void onError(final int code, final String message) {
                Log.d(TAG, "login: onError: " + code);
                runOnUiThread(new Runnable() {
                    public void run() {
                        Toast.makeText(getApplicationContext(), getString(R.string.Login_failed) + message,Toast.LENGTH_SHORT).show();
                    }
                });
            }
        });
    }
登录完成以后,我们可以根据房间名创建并加入房间,主要代码大概如下: EMClient.getInstance().conferenceManager().joinRoom(currentRoomname, currentPassword, conferenceRole,roomConfig, new EMValueCallBack<EMConference>(){
@Override
public void onSuccess(EMConference value) {
EMLog.i(TAG, "join conference success");
Intent intent = new Intent(MainActivity.this, ConferenceActivity.class);
startActivity(intent);
finish();
}
@Override
public void onError(final int error, final String errorMsg) {
EMLog.e(TAG, "join conference failed error " + error + ", msg " + errorMsg);
runOnUiThread(new Runnable() {
@Override
public void run() {
setBtnEnable(true);
if(error == CALL_TALKER_ISFULL) {
takerFullDialogDisplay();
}else{
Toast.makeText(getApplicationContext(), "Join conference failed " + error + " " + errorMsg, Toast.LENGTH_SHORT).show();
}
}
});
}
});
EMClient.getInstance().conferenceManager().joinRoom() API可以根据房间名创建指定会议,当以该房间名命名的会议不存在时候,会直接创建,当会议已经创建好 可以根据正确的房间名和密码加入房间 ,到这一步为止,我们已经成功的创建 并加入会议。
5.加入会议以后我们进入到会议界面,展示从DemoHelper类 EMConferenceListener 中的 onStreamAdded 回调 和 onMemberJoined 获取到的流和主播列表 ,在ConferenceActivity 中实现 EMConferenceListener ,然后直接把 ConferenceActivity 注册监听,用以下方法  EMClient.getInstance().conferenceManager().addConferenceListener(this); 这样就可实现 EMConferenceListener 事件的处理,比如
 主播 进出房间 :
public void onMemberJoined(final EMConferenceMember member);
public void onMemberExited(final EMConferenceMember member);

增加流 移除流:
public void onStreamAdded(final EMConferenceStream stream)
 public void onStreamRemoved(final EMConferenceStream stream)

管理员变更: 
public void onAdminAdded(String streamId) ;  
public void onAdminRemoved(String streamId)

角色变更  用户被踢  谁在说话等各种回调,可以处理各种业务逻辑 ,详细的请参考 项目中的实现 ,最后会附上项目的开源地址。

6 进入会议房间以后如果用户角色为主播可以进行发布视频流 ,观众只能订阅视频流 不能发布视频流 ,可以调用SDK的publish接口发布流,该接口用到了EMStreamParam参数,你可以自由配置,比如是否上传视频,是否上传音频,使用前置或后置摄像头,视频码率,显示视频页面等等,具体实现可以参考 中发布 订阅视频流的内容, 关于以上的代码逻辑如以 如以下: //发布视频流
normalParam = new EMStreamParam();
normalParam.setStreamType(EMConferenceStream.StreamType.NORMAL);
normalParam.setVideoOff(true);
normalParam.setAudioOff(true);

EMClient.getInstance().conferenceManager().publish(normalParam, new EMValueCallBack<String>() {
@Override
public void onSuccess(String value) {
conference.setPubStreamId(value, EMConferenceStream.StreamType.NORMAL);
addOrUpdateStreamList("local-stream", value);

PhoneStateManager.get(ConferenceActivity.this).addStateCallback(phoneStateCallback);
}

@Override
public void onError(int error, String errorMsg) {
EMLog.i(TAG, "publish failed: error=" + error + ", msg=" + errorMsg);
}
});
//订阅其他主播的视频流
private void subscribe(EMConferenceStream stream, EMCallSurfaceView surfaceView) {
EMClient.getInstance().conferenceManager().subscribe(stream, surfaceView, new EMValueCallBack<String>() {
@Override
public void onSuccess(String value) {
}

@Override
public void onError(int error, String errorMsg) {

}
});
}
7.有关上麦 下麦 的逻辑处理,观众可以请求上麦成为主播,主播可以下麦成为观众,上麦 下麦 是利用 EMConferenceAttribute进行处理 ,EMConferenceAttribute  是一个事件广播,广播事件是一个key-value格式,key-value 可以由开发者进行自行定义,增添事件以后 ,服务器会把事件进行广播。会议中成员会收到 onAttributesUpdated回调。例如本项目中的会议上麦 下麦 代码如下所示://上麦申请

EMClient.getInstance().conferenceManager().setConferenceAttribute(
EMClient.getInstance().getCurrentUser(),
"request_tobe_speaker",
new EMValueCallBack<Void>() {
@Override
public void onSuccess(Void value) {
EMLog.i(TAG, "request_tobe_speaker scuessed");

}

@Override
public void onError(int error, String errorMsg) {
EMLog.i(TAG, "request_tobe_speaker failed: error=" + error

}
});

//下麦申请
EMClient.getInstance().conferenceManager().setConferenceAttribute(EMClient.getInstance().getCurrentUser()
, "request_tobe_audience", new EMValueCallBack<Void>() {
@Override
public void onSuccess(Void value) {
EMLog.i(TAG, "request_tobe_audience scuessed");
}

@Override
public void onError(int error, String errorMsg) {
EMLog.i(TAG, "request_tobe_audience failed: error=" + error + ", msg=" + errorMsg);
}
});
 上麦 下麦 请求发出以后 只能由主持人去处理,处理在 EMConferenceListener  的回调 onAttributesUpdated(EMConferenceAttribute attributes) 去处理 ,收到回调以后 解析attributes 然后进行处理请求,处理的过程代码大概如下: EMClient.getInstance().conferenceManager().grantRole(conference.getConferenceId()
, new EMConferenceMember(memName, null, null,null)
, EMConferenceManager.EMConferenceRole.Talker, new EMValueCallBack<String>() {
@Override
public void onSuccess(String value) {
EMLog.i(TAG, " requestTalkerDisplay request_tobe_speaker changeRole success, result: " + value);
dialog.dismiss();
}
@Override
public void onError(int error, String errorMsg) {
EMLog.i(TAG, " requestTalkerDisplay request_tobe_speaker changeRole failed, error: " + error + " - " + errorMsg);

}
});
下麦也是和上麦一样是利用 EMConferenceAttribute进行处理。

9.有关退出会议 销毁会议 普通主播  观众只能退出会议 ,主持人还可以 销毁会议 正在进行中的会议可以进行销毁,退出会议 销毁会议 具体代码如下: EMClient.getInstance().conferenceManager().exitConference(new EMValueCallBack() {
@Override
public void onSuccess(Object value) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(getApplicationContext(), "您已成功退出当前会议!", Toast.LENGTH_SHORT).show();
}
});
}

@Override
public void onError(int error, String errorMsg) {
EMLog.i(TAG, "exit conference failed " + error + ", " + errorMsg);
}
});

EMClient.getInstance().conferenceManager().destroyConference(new EMValueCallBack() {
@Override
public void onSuccess(Object value) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(getApplicationContext(), "您已成功销毁当前会议!", Toast.LENGTH_SHORT).show();
}
});
EMLog.i(TAG, "finish ConferenceActivity");
finish();
}

尾语
至此 整个 多人音视频会议开发的详细步骤 已经完成 ,虽然比较麻烦 但是每个步骤都很清晰 ,有不太清楚的欢迎大家积极讨论, 附上本项目的github地址:https://github.com/easemob/videocall-android  欢迎大家积极参与 ,谢谢支持。
Demo下载 二维码如下 欢迎大家体验(iOS版和web版下载地址请见:https://www.easemob.com/download/rtc)






  查看全部
今年是不平凡的一年,没错,就是因为疫情,因为疫情原因 ,大家只能呆着家里,严重影响了我们正常的学习 生活 工作,在这种情况下,只能在家办公,这时候大家就会想到线上视频会议,目前很多互联网公司有这个产品,比较出名的就比如 腾讯会议 钉钉 zoom等,用这些是很方便,但是如果能开发自己的视频会议,那会不会更好或者是更有成就感,下面简单介绍这个这个项目,和大概的开发过程。本项目基于环信音视频云来完成,实现的主要功能有:
  •   创建会议、删除会议、获取指定会议室详情、加入会议室、退出会议室等关于会议的管理 ;
  •   获取会议室参会人名列表、踢人,设置观众为主播,设置主播为观众等关于会议室的人员管理;
  •   共享桌面(web端);

 三个端的实现:Android,iOS,Web

上面这些功能在项目中都已经实现。还有水印 ,变声等高级功能在环信音视频SDK的接口内部都已经封装好,本项目没有实现 ,大家可以自行去实现。有关多人音视频功能更详细的介绍大家可以参考:这儿。多人音视频实现的实现主要有以下一些场景:社交交友,远程心理咨询、远程医疗、一对一在线教育、远程视频辅助等。咳咳 ,接下来就是纯干货了,给大家介绍我是如何一步步开发出一个完整的多人音视频app。
 
项目截图
 
首先给大家展示下项目运行的效果图,会议界面 主窗口是一个大的 RelativeLayout ,最下面的那一排排小窗口是的实现方法是HorizontalScrollView加上一个开源的组件 com.jaouan.compoundlayout.RadioLayoutGroup 实现的,点击下面的小窗口后,可以 把小窗口的视频流显示在大屏上,具体是调用 updateRemoteSurfaceView(String streamId, EMCallSurfaceView remoteView)来更新SurfaceView,具体的细节大家可以看看代码里面的实现 最后会公布代码开源地址。

20200410140627956.jpg


20200410140855852.jpg


20200410141055394.jpg


准备工作
    大家得下载安装Android Studio,配置好Android 开发环境,怎么详细配置我就在这不再细说了 网上有很多的教程,大家自己可以找找看,然后大家可以看看环信多人音视频会议的主要功能和一些基本概念介绍。
集成 
  1. 首先大家会想问怎么调用环信的SDK ,大家可以使用 远程依赖SDK包,建议大家用最新版本的远程依赖:
     com.hyphenate:hyphenate-sdk:3.6.6 ,依赖包可以放在 build.gradle里面的 dependencies 选项下面,如下图所示
2020040917363212.png


2.其次怎么使用环信的appkey ,可以在环信音视频云后台注册一个 账号申请appkey ,可以参考这里 ,获取到  appkey 以后添加到AndroidManifest.xml中 ,如下图所示:

20200409213959916.png


3.经过以上两个重要的前期配置准备 ,接下来我们就可以开始进行代码开发了,首先我们先创建一个项目的DemoApplication类和      DemoHelper类,DemoApplication 类和DemoHelper类都是一个单例类 ,DemoApplication 主要功能就是进行DemoHelper 的初始化,而DemoHelper里面主要是主要有一些option 配置和EMClient 进行初始化,代码如下所示:
public void init(Context context) {
EMOptions options = initChatOptions(context);
EMClient.getInstance().init(context, options);
PreferenceManager.init(context);
}

  DemoHelper还有一个重要的功能就是设置  EMConferenceListener 进行会议监听,有关 EMConferenceListener的类的详细介绍 ,通过这个监听可以再加入会议的时候获取到已经在会议中的流和主播信息,分别是通过其中以下两个回调获取:
@Override 
public void onMemberJoined(EMConferenceMember member){

}

@Override
public void onStreamAdded(EMConferenceStream stream){

}

4.DemoApplication类完成以后,接下来就是怎么去登陆 环信IM 账号和 创建加入会议房间了,首次安装的时候都没有账号,我们使用的办法是自动注册一个账号 在本地进行保存,然后进行登录 ,注册 登录详细接口请看 这儿,  注册 登录的调用大概如下所示: 
 try {
        //注册一个环信ID
        EMClient.getInstance().createAccount(username, password);
            
        //注册成功进行登录
        PreferenceManager.getInstance().setCurrentUserName(username);
        PreferenceManager.getInstance().setCurrentuserPassword(password);
        login();
    } catch (final HyphenateException e) {
       runOnUiThread(new Runnable() {
               public void run() {
                  int errorCode=e.getErrorCode();
                   if(errorCode==EMError.NETWORK_ERROR){
                    Toast.makeText(getApplicationContext(), getResources().getString(R.string.network_anomalies), Toast.LENGTH_SHORT).show();
      }
   }   
}
 
  public void login() {
 
        //登录已经注册成功的环信ID
        EMClient.getInstance().login(username, password, new EMCallBack() {
            @Override
            public void onSuccess() {
                Log.d(TAG, "login: onSuccess");
                //登录成功进入会议房间
                joinRoom();
            }
            @Override
            public void onProgress(int progress, String status) {
                Log.d(TAG, "login: onProgress");
            }
            @Override
            public void onError(final int code, final String message) {
                Log.d(TAG, "login: onError: " + code);
                runOnUiThread(new Runnable() {
                    public void run() {
                        Toast.makeText(getApplicationContext(), getString(R.string.Login_failed) + message,Toast.LENGTH_SHORT).show();
                    }
                });
            }
        });
    }
登录完成以后,我们可以根据房间名创建并加入房间,主要代码大概如下:
 EMClient.getInstance().conferenceManager().joinRoom(currentRoomname, currentPassword, conferenceRole,roomConfig, new EMValueCallBack<EMConference>(){
@Override
public void onSuccess(EMConference value) {
EMLog.i(TAG, "join conference success");
Intent intent = new Intent(MainActivity.this, ConferenceActivity.class);
startActivity(intent);
finish();
}
@Override
public void onError(final int error, final String errorMsg) {
EMLog.e(TAG, "join conference failed error " + error + ", msg " + errorMsg);
runOnUiThread(new Runnable() {
@Override
public void run() {
setBtnEnable(true);
if(error == CALL_TALKER_ISFULL) {
takerFullDialogDisplay();
}else{
Toast.makeText(getApplicationContext(), "Join conference failed " + error + " " + errorMsg, Toast.LENGTH_SHORT).show();
}
}
});
}
});

EMClient.getInstance().conferenceManager().joinRoom() API可以根据房间名创建指定会议,当以该房间名命名的会议不存在时候,会直接创建,当会议已经创建好 可以根据正确的房间名和密码加入房间 ,到这一步为止,我们已经成功的创建 并加入会议。
5.加入会议以后我们进入到会议界面,展示从DemoHelper类 EMConferenceListener 中的 onStreamAdded 回调 和 onMemberJoined 获取到的流和主播列表 ,在ConferenceActivity 中实现 EMConferenceListener ,然后直接把 ConferenceActivity 注册监听,用以下方法  EMClient.getInstance().conferenceManager().addConferenceListener(this); 这样就可实现 EMConferenceListener 事件的处理,比如
 主播 进出房间 :
public void onMemberJoined(final EMConferenceMember member);
public void onMemberExited(final EMConferenceMember member);

增加流 移除流:
public void onStreamAdded(final EMConferenceStream stream)
 public void onStreamRemoved(final EMConferenceStream stream)

管理员变更: 
public void onAdminAdded(String streamId) ;  
public void onAdminRemoved(String streamId)

角色变更  用户被踢  谁在说话等各种回调,可以处理各种业务逻辑 ,详细的请参考 项目中的实现 ,最后会附上项目的开源地址。

6 进入会议房间以后如果用户角色为主播可以进行发布视频流 ,观众只能订阅视频流 不能发布视频流 ,可以调用SDK的publish接口发布流,该接口用到了EMStreamParam参数,你可以自由配置,比如是否上传视频,是否上传音频,使用前置或后置摄像头,视频码率,显示视频页面等等,具体实现可以参考 中发布 订阅视频流的内容, 关于以上的代码逻辑如以 如以下:
 //发布视频流
normalParam = new EMStreamParam();
normalParam.setStreamType(EMConferenceStream.StreamType.NORMAL);
normalParam.setVideoOff(true);
normalParam.setAudioOff(true);

EMClient.getInstance().conferenceManager().publish(normalParam, new EMValueCallBack<String>() {
@Override
public void onSuccess(String value) {
conference.setPubStreamId(value, EMConferenceStream.StreamType.NORMAL);
addOrUpdateStreamList("local-stream", value);

PhoneStateManager.get(ConferenceActivity.this).addStateCallback(phoneStateCallback);
}

@Override
public void onError(int error, String errorMsg) {
EMLog.i(TAG, "publish failed: error=" + error + ", msg=" + errorMsg);
}
});
//订阅其他主播的视频流
private void subscribe(EMConferenceStream stream, EMCallSurfaceView surfaceView) {
EMClient.getInstance().conferenceManager().subscribe(stream, surfaceView, new EMValueCallBack<String>() {
@Override
public void onSuccess(String value) {
}

@Override
public void onError(int error, String errorMsg) {

}
});
}

7.有关上麦 下麦 的逻辑处理,观众可以请求上麦成为主播,主播可以下麦成为观众,上麦 下麦 是利用 EMConferenceAttribute进行处理 ,EMConferenceAttribute  是一个事件广播,广播事件是一个key-value格式,key-value 可以由开发者进行自行定义,增添事件以后 ,服务器会把事件进行广播。会议中成员会收到 onAttributesUpdated回调。例如本项目中的会议上麦 下麦 代码如下所示:
//上麦申请  

EMClient.getInstance().conferenceManager().setConferenceAttribute(
EMClient.getInstance().getCurrentUser(),
"request_tobe_speaker",
new EMValueCallBack<Void>() {
@Override
public void onSuccess(Void value) {
EMLog.i(TAG, "request_tobe_speaker scuessed");

}

@Override
public void onError(int error, String errorMsg) {
EMLog.i(TAG, "request_tobe_speaker failed: error=" + error

}
});

//下麦申请
EMClient.getInstance().conferenceManager().setConferenceAttribute(EMClient.getInstance().getCurrentUser()
, "request_tobe_audience", new EMValueCallBack<Void>() {
@Override
public void onSuccess(Void value) {
EMLog.i(TAG, "request_tobe_audience scuessed");
}

@Override
public void onError(int error, String errorMsg) {
EMLog.i(TAG, "request_tobe_audience failed: error=" + error + ", msg=" + errorMsg);
}
});

 上麦 下麦 请求发出以后 只能由主持人去处理,处理在 EMConferenceListener  的回调 onAttributesUpdated(EMConferenceAttribute attributes) 去处理 ,收到回调以后 解析attributes 然后进行处理请求,处理的过程代码大概如下:
   EMClient.getInstance().conferenceManager().grantRole(conference.getConferenceId()
, new EMConferenceMember(memName, null, null,null)
, EMConferenceManager.EMConferenceRole.Talker, new EMValueCallBack<String>() {
@Override
public void onSuccess(String value) {
EMLog.i(TAG, " requestTalkerDisplay request_tobe_speaker changeRole success, result: " + value);
dialog.dismiss();
}
@Override
public void onError(int error, String errorMsg) {
EMLog.i(TAG, " requestTalkerDisplay request_tobe_speaker changeRole failed, error: " + error + " - " + errorMsg);

}
});

下麦也是和上麦一样是利用 EMConferenceAttribute进行处理。

9.有关退出会议 销毁会议 普通主播  观众只能退出会议 ,主持人还可以 销毁会议 正在进行中的会议可以进行销毁,退出会议 销毁会议 具体代码如下:
 EMClient.getInstance().conferenceManager().exitConference(new EMValueCallBack() {
@Override
public void onSuccess(Object value) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(getApplicationContext(), "您已成功退出当前会议!", Toast.LENGTH_SHORT).show();
}
});
}

@Override
public void onError(int error, String errorMsg) {
EMLog.i(TAG, "exit conference failed " + error + ", " + errorMsg);
}
});

EMClient.getInstance().conferenceManager().destroyConference(new EMValueCallBack() {
@Override
public void onSuccess(Object value) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(getApplicationContext(), "您已成功销毁当前会议!", Toast.LENGTH_SHORT).show();
}
});
EMLog.i(TAG, "finish ConferenceActivity");
finish();
}

尾语
至此 整个 多人音视频会议开发的详细步骤 已经完成 ,虽然比较麻烦 但是每个步骤都很清晰 ,有不太清楚的欢迎大家积极讨论, 附上本项目的github地址:https://github.com/easemob/videocall-android  欢迎大家积极参与 ,谢谢支持。
Demo下载 二维码如下 欢迎大家体验(iOS版和web版下载地址请见:https://www.easemob.com/download/rtc)


20200410142228530.png

 
2
回复

环信发送语音问题! 环信_WebIM集成问题 webIm H5 环信_WebIM 环信_RestAPI

回复

lizg 回复了问题 • 3 人关注 • 79 次浏览 • 2020-06-06 18:30 • 来自相关话题

11
最佳

webim 得到音频blob地址,页面刷新后就不能播放了 环信_WebIM webIm H5

回复

vtchg 回复了问题 • 2 人关注 • 762 次浏览 • 2020-06-05 10:13 • 来自相关话题

1
最佳

请问 h5 端有直播间吗 环信_WebIM集成问题 环信_WebIM使用问题 环信_WebIM

回复

lizg 回复了问题 • 2 人关注 • 127 次浏览 • 2020-06-02 10:12 • 来自相关话题

2
回复

音频消息,如何转化为mp3地址 webIm H5 环信_RestAPI 环信_WebIM 环信_WebIM使用问题

回复

vtchg 回复了问题 • 2 人关注 • 196 次浏览 • 2020-05-31 15:59 • 来自相关话题

2
回复

uni-app 是否支持实时音视频 环信_WebIM

回复

lizg 回复了问题 • 2 人关注 • 1972 次浏览 • 2020-05-27 15:10 • 来自相关话题

2
回复

Web 端怎么获取历史记录 环信_WebIM

回复

lizg 回复了问题 • 3 人关注 • 158 次浏览 • 2020-05-26 18:44 • 来自相关话题

1
回复

环信上传图片信息返回的share-secret为null 环信_WebIM

回复

carlwang 回复了问题 • 2 人关注 • 215 次浏览 • 2020-05-20 17:04 • 来自相关话题

1
回复

请教支持在微信公众号H5页面中集成1vs1音视频通讯功能吗? 环信_WebIM 环信移动客服

回复

lizg 回复了问题 • 2 人关注 • 380 次浏览 • 2020-05-18 19:20 • 来自相关话题

1
回复

(毕业真实版本)《加州大学伯克利分校毕业证|UCB毕业一样证书 环信_WebIM

回复

lizg 回复了问题 • 2 人关注 • 396 次浏览 • 2020-05-18 19:19 • 来自相关话题

2
回复

webim没有聊天界面吗 环信_WebIM

回复

lizg 回复了问题 • 2 人关注 • 2196 次浏览 • 2020-05-18 17:56 • 来自相关话题

1
回复

我们开发的小伙伴最近在用flutter开发,想整合环信调试,是否支持呢? 环信_WebIM

回复

andy2015 回复了问题 • 1 人关注 • 2915 次浏览 • 2020-05-15 10:35 • 来自相关话题

1
回复

小程序端 如何修改头像 环信_WebIM

回复

lizg 回复了问题 • 2 人关注 • 2241 次浏览 • 2020-05-11 10:38 • 来自相关话题

1
回复

webim发送文本消息ext传头像和昵称怎么书写,放在那好个地方呢 环信_WebIM

回复

lizg 回复了问题 • 2 人关注 • 876 次浏览 • 2020-04-20 18:24 • 来自相关话题

2
回复

微信小程序sdk支持1v1语言通话吗? 环信_WebIM 微信小程序

回复

迷之de小孩灬 回复了问题 • 2 人关注 • 1504 次浏览 • 2020-04-09 12:11 • 来自相关话题

1
回复

easemob-websdk 3.0.7 没有setPresence方法啊 环信_WebIM

回复

lizg 回复了问题 • 3 人关注 • 903 次浏览 • 2020-03-11 07:05 • 来自相关话题

3
回复

webIM vue如何获取所有未读消息数量?有相应的示例代码么 环信_WebIM

回复

lizg 回复了问题 • 4 人关注 • 1068 次浏览 • 2020-03-11 03:05 • 来自相关话题

1
回复

webIM群组发送消息 onError type=503是什么意思 没权限吗 环信_WebIM

回复

lizg 回复了问题 • 2 人关注 • 597 次浏览 • 2020-03-09 18:49 • 来自相关话题

1
回复

websdk ext字段number格式转换为long 环信_WebIM

回复

lizg 回复了问题 • 2 人关注 • 740 次浏览 • 2020-02-26 12:15 • 来自相关话题

1
回复

多登陆被踢不返回login在返回多加了个东西求解答 小程序api 被踢 环信_RestAPI 环信_WebIM

回复

lizg 回复了问题 • 2 人关注 • 787 次浏览 • 2020-02-11 17:34 • 来自相关话题

1
回复

sdk文件放到现有小程序里,报错:Identifier 'DOMParser' has already been declared 环信_WebIM

回复

lizg 回复了问题 • 2 人关注 • 1124 次浏览 • 2020-01-09 17:37 • 来自相关话题

1
回复

自定义消息,webIM发送的boolean值,在android这边取到一直是false 环信_Android 环信_WebIM

回复

lizg 回复了问题 • 2 人关注 • 969 次浏览 • 2020-01-02 17:45 • 来自相关话题

3
最佳

小程序中发现一个问题isAutoLogin为true不手动setPresence接收不到离线消息 小程序 环信_WebIM

回复

lizg 回复了问题 • 2 人关注 • 1762 次浏览 • 2019-12-23 17:29 • 来自相关话题

1
回复

怎么导出环信当天所有聊天记录 环信_RestAPI 环信_管理后台 环信_WebIM 环信_Android 环信_iOS

回复

lizg 回复了问题 • 2 人关注 • 1329 次浏览 • 2019-12-23 17:27 • 来自相关话题

1
回复

vue项目 webIM pc端集成 连接服务器正常 报405跨域错误 环信_WebIM

回复

lizg 回复了问题 • 2 人关注 • 1037 次浏览 • 2019-12-23 17:26 • 来自相关话题

2
评论

基于环信sdk在uni-app框架中快速开发一款多平台社交Demo SDK uni_app 环信

beyond 发表了文章 • 556 次浏览 • 2020-05-11 11:34 • 来自相关话题

说在前面:此款 demo 是基于 环信sdk 开发的一款具有单聊、群聊、聊天室、音视频等功能的应用。在此之前我们已经开发完 Vue、react(web端)、微信小程序。这三个热门领域的版本,如有需要源码可以后台留言索取。





 
安装开发工具

我们选用微信小程序来用做示例(如果选择百度、支付宝安装对应开发者工具即可)、

微信开发者工具建议还是安装最新版的。uni-app的开发也必须安装HBuilderX工具,这个是捆绑的,没得选择。要用uni-app,你必须得装!

工具安装:

微信开发者工具

HBuilderX

项目demo介绍:






项目demo启动预览:





 
快速集成环信 sdk:

1、复制整个utils文件






如果你想具体了解主要配置文件 请看这个链接:

http://docs-im.easemob.com/im/web/intro/start

2、如何使用环信的appkey ,可以在环信 console 后台注册一个 账号申请appkey ,可以参考这里 ,获取到  appkey 以后添加到配置文件中 ,如下图所示:






以上两个重要的配置准备完成之后就可以进行一系列的操作了(收发消息、好友申请、进群入群通知等)

在uni-app中 使用环信 sdk 实现添加、删除好友:

1、在全局 App.vue 文件 钩子函数 onLaunch() 中监听各种事件 (好友申请、收到各类消息等)如图:






发送好友请求:






在onPresence(message)事件中接收到好友消息申请:






同意好友请求:






拒绝好友请求:






实现收发消息:

1、给好友发送消息:






2、接收到消息:

在onTextMessage(message)事件中接收到好友消息,然后做消息上屏处理(具体消息上屏逻辑可看demo中代码示例):





以上展示的仅仅为基本业务场景,更多的业务逻辑详情请看demo示例。api具体详情可以查看 环信sdk 文档

最后结语:基于uni-app这个框架可实现多平台, 虽然目前一期集成环信sdk的版本仅支持微信小程序版本,但二期我们将加入头条、支付宝等小程序,敬请期待。PS:对于安卓、ios移动端,我们建议使用针对移动端开发的sdk版本。

基于uni-app的开发其中也趟了不少坑,在这里就不多赘述了。回归到框架的选型来讲,选用uni-app开发小程序,可同时并行多端小程序,这点是真香,一次开发多端发布。至于审核嘛~ 时快时慢。 查看全部
说在前面:此款 demo 是基于 环信sdk 开发的一款具有单聊、群聊、聊天室、音视频等功能的应用。在此之前我们已经开发完 Vue、react(web端)、微信小程序。这三个热门领域的版本,如有需要源码可以后台留言索取。

1.jpg

 
安装开发工具

我们选用微信小程序来用做示例(如果选择百度、支付宝安装对应开发者工具即可)、

微信开发者工具建议还是安装最新版的。uni-app的开发也必须安装HBuilderX工具,这个是捆绑的,没得选择。要用uni-app,你必须得装!

工具安装:

微信开发者工具

HBuilderX

项目demo介绍:

2.jpg


项目demo启动预览:

3.jpg

 
快速集成环信 sdk:

1、复制整个utils文件

4.jpg


如果你想具体了解主要配置文件 请看这个链接:

http://docs-im.easemob.com/im/web/intro/start

2、如何使用环信的appkey ,可以在环信 console 后台注册一个 账号申请appkey ,可以参考这里 ,获取到  appkey 以后添加到配置文件中 ,如下图所示:

5.jpg


以上两个重要的配置准备完成之后就可以进行一系列的操作了(收发消息、好友申请、进群入群通知等)

在uni-app中 使用环信 sdk 实现添加、删除好友:

1、在全局 App.vue 文件 钩子函数 onLaunch() 中监听各种事件 (好友申请、收到各类消息等)如图:

6.jpg


发送好友请求:

7.jpg


在onPresence(message)事件中接收到好友消息申请:

8.jpg


同意好友请求:

9.jpg


拒绝好友请求:

10.jpg


实现收发消息:

1、给好友发送消息:

11.jpg


2、接收到消息:

在onTextMessage(message)事件中接收到好友消息,然后做消息上屏处理(具体消息上屏逻辑可看demo中代码示例):

12.jpg

以上展示的仅仅为基本业务场景,更多的业务逻辑详情请看demo示例。api具体详情可以查看 环信sdk 文档

最后结语:基于uni-app这个框架可实现多平台, 虽然目前一期集成环信sdk的版本仅支持微信小程序版本,但二期我们将加入头条、支付宝等小程序,敬请期待。PS:对于安卓、ios移动端,我们建议使用针对移动端开发的sdk版本。

基于uni-app的开发其中也趟了不少坑,在这里就不多赘述了。回归到框架的选型来讲,选用uni-app开发小程序,可同时并行多端小程序,这点是真香,一次开发多端发布。至于审核嘛~ 时快时慢。
1
评论

手把手教程:4小时开发一个视频会议APP【附开源代码】 环信 开源 视频会议

fat1 发表了文章 • 1054 次浏览 • 2020-04-17 00:29 • 来自相关话题

今年是不平凡的一年,没错,就是因为疫情,因为疫情原因 ,大家只能呆着家里,严重影响了我们正常的学习 生活 工作,在这种情况下,只能在家办公,这时候大家就会想到线上视频会议,目前很多互联网公司有这个产品,比较出名的就比如 腾讯会议 钉钉 zoom等,用这些是很方便,但是如果能开发自己的视频会议,那会不会更好或者是更有成就感,下面简单介绍这个这个项目,和大概的开发过程。本项目基于环信音视频云来完成,实现的主要功能有:
  创建会议、删除会议、获取指定会议室详情、加入会议室、退出会议室等关于会议的管理 ;  获取会议室参会人名列表、踢人,设置观众为主播,设置主播为观众等关于会议室的人员管理;  共享桌面(web端);
 三个端的实现:Android,iOS,Web

上面这些功能在项目中都已经实现。还有水印 ,变声等高级功能在环信音视频SDK的接口内部都已经封装好,本项目没有实现 ,大家可以自行去实现。有关多人音视频功能更详细的介绍大家可以参考:这儿。多人音视频实现的实现主要有以下一些场景:社交交友,远程心理咨询、远程医疗、一对一在线教育、远程视频辅助等。咳咳 ,接下来就是纯干货了,给大家介绍我是如何一步步开发出一个完整的多人音视频app。
 
项目截图
 
首先给大家展示下项目运行的效果图,会议界面 主窗口是一个大的 RelativeLayout ,最下面的那一排排小窗口是的实现方法是HorizontalScrollView加上一个开源的组件 com.jaouan.compoundlayout.RadioLayoutGroup 实现的,点击下面的小窗口后,可以 把小窗口的视频流显示在大屏上,具体是调用 updateRemoteSurfaceView(String streamId, EMCallSurfaceView remoteView)来更新SurfaceView,具体的细节大家可以看看代码里面的实现 最后会公布代码开源地址。
















准备工作
    大家得下载安装Android Studio,配置好Android 开发环境,怎么详细配置我就在这不再细说了 网上有很多的教程,大家自己可以找找看,然后大家可以看看环信多人音视频会议的主要功能和一些基本概念介绍。
集成 
  1. 首先大家会想问怎么调用环信的SDK ,大家可以使用 远程依赖SDK包,建议大家用最新版本的远程依赖:
     com.hyphenate:hyphenate-sdk:3.6.6 ,依赖包可以放在 build.gradle里面的 dependencies 选项下面,如下图所示





2.其次怎么使用环信的appkey ,可以在环信音视频云后台注册一个 账号申请appkey ,可以参考这里 ,获取到  appkey 以后添加到AndroidManifest.xml中 ,如下图所示:






3.经过以上两个重要的前期配置准备 ,接下来我们就可以开始进行代码开发了,首先我们先创建一个项目的DemoApplication类和      DemoHelper类,DemoApplication 类和DemoHelper类都是一个单例类 ,DemoApplication 主要功能就是进行DemoHelper 的初始化,而DemoHelper里面主要是主要有一些option 配置和EMClient 进行初始化,代码如下所示:public void init(Context context) {
EMOptions options = initChatOptions(context);
EMClient.getInstance().init(context, options);
PreferenceManager.init(context);
}
  DemoHelper还有一个重要的功能就是设置  EMConferenceListener 进行会议监听,有关 EMConferenceListener的类的详细介绍 ,通过这个监听可以再加入会议的时候获取到已经在会议中的流和主播信息,分别是通过其中以下两个回调获取:@Override
public void onMemberJoined(EMConferenceMember member){

}

@Override
public void onStreamAdded(EMConferenceStream stream){

}
4.DemoApplication类完成以后,接下来就是怎么去登陆 环信IM 账号和 创建加入会议房间了,首次安装的时候都没有账号,我们使用的办法是自动注册一个账号 在本地进行保存,然后进行登录 ,注册 登录详细接口请看 这儿,  注册 登录的调用大概如下所示: 
 try {
        //注册一个环信ID
        EMClient.getInstance().createAccount(username, password);
            
        //注册成功进行登录
        PreferenceManager.getInstance().setCurrentUserName(username);
        PreferenceManager.getInstance().setCurrentuserPassword(password);
        login();
    } catch (final HyphenateException e) {
       runOnUiThread(new Runnable() {
               public void run() {
                  int errorCode=e.getErrorCode();
                   if(errorCode==EMError.NETWORK_ERROR){
                    Toast.makeText(getApplicationContext(), getResources().getString(R.string.network_anomalies), Toast.LENGTH_SHORT).show();
      }
   }   
}
 
  public void login() {
 
        //登录已经注册成功的环信ID
        EMClient.getInstance().login(username, password, new EMCallBack() {
            @Override
            public void onSuccess() {
                Log.d(TAG, "login: onSuccess");
                //登录成功进入会议房间
                joinRoom();
            }
            @Override
            public void onProgress(int progress, String status) {
                Log.d(TAG, "login: onProgress");
            }
            @Override
            public void onError(final int code, final String message) {
                Log.d(TAG, "login: onError: " + code);
                runOnUiThread(new Runnable() {
                    public void run() {
                        Toast.makeText(getApplicationContext(), getString(R.string.Login_failed) + message,Toast.LENGTH_SHORT).show();
                    }
                });
            }
        });
    }
登录完成以后,我们可以根据房间名创建并加入房间,主要代码大概如下: EMClient.getInstance().conferenceManager().joinRoom(currentRoomname, currentPassword, conferenceRole,roomConfig, new EMValueCallBack<EMConference>(){
@Override
public void onSuccess(EMConference value) {
EMLog.i(TAG, "join conference success");
Intent intent = new Intent(MainActivity.this, ConferenceActivity.class);
startActivity(intent);
finish();
}
@Override
public void onError(final int error, final String errorMsg) {
EMLog.e(TAG, "join conference failed error " + error + ", msg " + errorMsg);
runOnUiThread(new Runnable() {
@Override
public void run() {
setBtnEnable(true);
if(error == CALL_TALKER_ISFULL) {
takerFullDialogDisplay();
}else{
Toast.makeText(getApplicationContext(), "Join conference failed " + error + " " + errorMsg, Toast.LENGTH_SHORT).show();
}
}
});
}
});
EMClient.getInstance().conferenceManager().joinRoom() API可以根据房间名创建指定会议,当以该房间名命名的会议不存在时候,会直接创建,当会议已经创建好 可以根据正确的房间名和密码加入房间 ,到这一步为止,我们已经成功的创建 并加入会议。
5.加入会议以后我们进入到会议界面,展示从DemoHelper类 EMConferenceListener 中的 onStreamAdded 回调 和 onMemberJoined 获取到的流和主播列表 ,在ConferenceActivity 中实现 EMConferenceListener ,然后直接把 ConferenceActivity 注册监听,用以下方法  EMClient.getInstance().conferenceManager().addConferenceListener(this); 这样就可实现 EMConferenceListener 事件的处理,比如
 主播 进出房间 :
public void onMemberJoined(final EMConferenceMember member);
public void onMemberExited(final EMConferenceMember member);

增加流 移除流:
public void onStreamAdded(final EMConferenceStream stream)
 public void onStreamRemoved(final EMConferenceStream stream)

管理员变更: 
public void onAdminAdded(String streamId) ;  
public void onAdminRemoved(String streamId)

角色变更  用户被踢  谁在说话等各种回调,可以处理各种业务逻辑 ,详细的请参考 项目中的实现 ,最后会附上项目的开源地址。

6 进入会议房间以后如果用户角色为主播可以进行发布视频流 ,观众只能订阅视频流 不能发布视频流 ,可以调用SDK的publish接口发布流,该接口用到了EMStreamParam参数,你可以自由配置,比如是否上传视频,是否上传音频,使用前置或后置摄像头,视频码率,显示视频页面等等,具体实现可以参考 中发布 订阅视频流的内容, 关于以上的代码逻辑如以 如以下: //发布视频流
normalParam = new EMStreamParam();
normalParam.setStreamType(EMConferenceStream.StreamType.NORMAL);
normalParam.setVideoOff(true);
normalParam.setAudioOff(true);

EMClient.getInstance().conferenceManager().publish(normalParam, new EMValueCallBack<String>() {
@Override
public void onSuccess(String value) {
conference.setPubStreamId(value, EMConferenceStream.StreamType.NORMAL);
addOrUpdateStreamList("local-stream", value);

PhoneStateManager.get(ConferenceActivity.this).addStateCallback(phoneStateCallback);
}

@Override
public void onError(int error, String errorMsg) {
EMLog.i(TAG, "publish failed: error=" + error + ", msg=" + errorMsg);
}
});
//订阅其他主播的视频流
private void subscribe(EMConferenceStream stream, EMCallSurfaceView surfaceView) {
EMClient.getInstance().conferenceManager().subscribe(stream, surfaceView, new EMValueCallBack<String>() {
@Override
public void onSuccess(String value) {
}

@Override
public void onError(int error, String errorMsg) {

}
});
}
7.有关上麦 下麦 的逻辑处理,观众可以请求上麦成为主播,主播可以下麦成为观众,上麦 下麦 是利用 EMConferenceAttribute进行处理 ,EMConferenceAttribute  是一个事件广播,广播事件是一个key-value格式,key-value 可以由开发者进行自行定义,增添事件以后 ,服务器会把事件进行广播。会议中成员会收到 onAttributesUpdated回调。例如本项目中的会议上麦 下麦 代码如下所示://上麦申请

EMClient.getInstance().conferenceManager().setConferenceAttribute(
EMClient.getInstance().getCurrentUser(),
"request_tobe_speaker",
new EMValueCallBack<Void>() {
@Override
public void onSuccess(Void value) {
EMLog.i(TAG, "request_tobe_speaker scuessed");

}

@Override
public void onError(int error, String errorMsg) {
EMLog.i(TAG, "request_tobe_speaker failed: error=" + error

}
});

//下麦申请
EMClient.getInstance().conferenceManager().setConferenceAttribute(EMClient.getInstance().getCurrentUser()
, "request_tobe_audience", new EMValueCallBack<Void>() {
@Override
public void onSuccess(Void value) {
EMLog.i(TAG, "request_tobe_audience scuessed");
}

@Override
public void onError(int error, String errorMsg) {
EMLog.i(TAG, "request_tobe_audience failed: error=" + error + ", msg=" + errorMsg);
}
});
 上麦 下麦 请求发出以后 只能由主持人去处理,处理在 EMConferenceListener  的回调 onAttributesUpdated(EMConferenceAttribute attributes) 去处理 ,收到回调以后 解析attributes 然后进行处理请求,处理的过程代码大概如下: EMClient.getInstance().conferenceManager().grantRole(conference.getConferenceId()
, new EMConferenceMember(memName, null, null,null)
, EMConferenceManager.EMConferenceRole.Talker, new EMValueCallBack<String>() {
@Override
public void onSuccess(String value) {
EMLog.i(TAG, " requestTalkerDisplay request_tobe_speaker changeRole success, result: " + value);
dialog.dismiss();
}
@Override
public void onError(int error, String errorMsg) {
EMLog.i(TAG, " requestTalkerDisplay request_tobe_speaker changeRole failed, error: " + error + " - " + errorMsg);

}
});
下麦也是和上麦一样是利用 EMConferenceAttribute进行处理。

9.有关退出会议 销毁会议 普通主播  观众只能退出会议 ,主持人还可以 销毁会议 正在进行中的会议可以进行销毁,退出会议 销毁会议 具体代码如下: EMClient.getInstance().conferenceManager().exitConference(new EMValueCallBack() {
@Override
public void onSuccess(Object value) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(getApplicationContext(), "您已成功退出当前会议!", Toast.LENGTH_SHORT).show();
}
});
}

@Override
public void onError(int error, String errorMsg) {
EMLog.i(TAG, "exit conference failed " + error + ", " + errorMsg);
}
});

EMClient.getInstance().conferenceManager().destroyConference(new EMValueCallBack() {
@Override
public void onSuccess(Object value) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(getApplicationContext(), "您已成功销毁当前会议!", Toast.LENGTH_SHORT).show();
}
});
EMLog.i(TAG, "finish ConferenceActivity");
finish();
}

尾语
至此 整个 多人音视频会议开发的详细步骤 已经完成 ,虽然比较麻烦 但是每个步骤都很清晰 ,有不太清楚的欢迎大家积极讨论, 附上本项目的github地址:https://github.com/easemob/videocall-android  欢迎大家积极参与 ,谢谢支持。
Demo下载 二维码如下 欢迎大家体验(iOS版和web版下载地址请见:https://www.easemob.com/download/rtc)






  查看全部
今年是不平凡的一年,没错,就是因为疫情,因为疫情原因 ,大家只能呆着家里,严重影响了我们正常的学习 生活 工作,在这种情况下,只能在家办公,这时候大家就会想到线上视频会议,目前很多互联网公司有这个产品,比较出名的就比如 腾讯会议 钉钉 zoom等,用这些是很方便,但是如果能开发自己的视频会议,那会不会更好或者是更有成就感,下面简单介绍这个这个项目,和大概的开发过程。本项目基于环信音视频云来完成,实现的主要功能有:
  •   创建会议、删除会议、获取指定会议室详情、加入会议室、退出会议室等关于会议的管理 ;
  •   获取会议室参会人名列表、踢人,设置观众为主播,设置主播为观众等关于会议室的人员管理;
  •   共享桌面(web端);

 三个端的实现:Android,iOS,Web

上面这些功能在项目中都已经实现。还有水印 ,变声等高级功能在环信音视频SDK的接口内部都已经封装好,本项目没有实现 ,大家可以自行去实现。有关多人音视频功能更详细的介绍大家可以参考:这儿。多人音视频实现的实现主要有以下一些场景:社交交友,远程心理咨询、远程医疗、一对一在线教育、远程视频辅助等。咳咳 ,接下来就是纯干货了,给大家介绍我是如何一步步开发出一个完整的多人音视频app。
 
项目截图
 
首先给大家展示下项目运行的效果图,会议界面 主窗口是一个大的 RelativeLayout ,最下面的那一排排小窗口是的实现方法是HorizontalScrollView加上一个开源的组件 com.jaouan.compoundlayout.RadioLayoutGroup 实现的,点击下面的小窗口后,可以 把小窗口的视频流显示在大屏上,具体是调用 updateRemoteSurfaceView(String streamId, EMCallSurfaceView remoteView)来更新SurfaceView,具体的细节大家可以看看代码里面的实现 最后会公布代码开源地址。

20200410140627956.jpg


20200410140855852.jpg


20200410141055394.jpg


准备工作
    大家得下载安装Android Studio,配置好Android 开发环境,怎么详细配置我就在这不再细说了 网上有很多的教程,大家自己可以找找看,然后大家可以看看环信多人音视频会议的主要功能和一些基本概念介绍。
集成 
  1. 首先大家会想问怎么调用环信的SDK ,大家可以使用 远程依赖SDK包,建议大家用最新版本的远程依赖:
     com.hyphenate:hyphenate-sdk:3.6.6 ,依赖包可以放在 build.gradle里面的 dependencies 选项下面,如下图所示
2020040917363212.png


2.其次怎么使用环信的appkey ,可以在环信音视频云后台注册一个 账号申请appkey ,可以参考这里 ,获取到  appkey 以后添加到AndroidManifest.xml中 ,如下图所示:

20200409213959916.png


3.经过以上两个重要的前期配置准备 ,接下来我们就可以开始进行代码开发了,首先我们先创建一个项目的DemoApplication类和      DemoHelper类,DemoApplication 类和DemoHelper类都是一个单例类 ,DemoApplication 主要功能就是进行DemoHelper 的初始化,而DemoHelper里面主要是主要有一些option 配置和EMClient 进行初始化,代码如下所示:
public void init(Context context) {
EMOptions options = initChatOptions(context);
EMClient.getInstance().init(context, options);
PreferenceManager.init(context);
}

  DemoHelper还有一个重要的功能就是设置  EMConferenceListener 进行会议监听,有关 EMConferenceListener的类的详细介绍 ,通过这个监听可以再加入会议的时候获取到已经在会议中的流和主播信息,分别是通过其中以下两个回调获取:
@Override 
public void onMemberJoined(EMConferenceMember member){

}

@Override
public void onStreamAdded(EMConferenceStream stream){

}

4.DemoApplication类完成以后,接下来就是怎么去登陆 环信IM 账号和 创建加入会议房间了,首次安装的时候都没有账号,我们使用的办法是自动注册一个账号 在本地进行保存,然后进行登录 ,注册 登录详细接口请看 这儿,  注册 登录的调用大概如下所示: 
 try {
        //注册一个环信ID
        EMClient.getInstance().createAccount(username, password);
            
        //注册成功进行登录
        PreferenceManager.getInstance().setCurrentUserName(username);
        PreferenceManager.getInstance().setCurrentuserPassword(password);
        login();
    } catch (final HyphenateException e) {
       runOnUiThread(new Runnable() {
               public void run() {
                  int errorCode=e.getErrorCode();
                   if(errorCode==EMError.NETWORK_ERROR){
                    Toast.makeText(getApplicationContext(), getResources().getString(R.string.network_anomalies), Toast.LENGTH_SHORT).show();
      }
   }   
}
 
  public void login() {
 
        //登录已经注册成功的环信ID
        EMClient.getInstance().login(username, password, new EMCallBack() {
            @Override
            public void onSuccess() {
                Log.d(TAG, "login: onSuccess");
                //登录成功进入会议房间
                joinRoom();
            }
            @Override
            public void onProgress(int progress, String status) {
                Log.d(TAG, "login: onProgress");
            }
            @Override
            public void onError(final int code, final String message) {
                Log.d(TAG, "login: onError: " + code);
                runOnUiThread(new Runnable() {
                    public void run() {
                        Toast.makeText(getApplicationContext(), getString(R.string.Login_failed) + message,Toast.LENGTH_SHORT).show();
                    }
                });
            }
        });
    }
登录完成以后,我们可以根据房间名创建并加入房间,主要代码大概如下:
 EMClient.getInstance().conferenceManager().joinRoom(currentRoomname, currentPassword, conferenceRole,roomConfig, new EMValueCallBack<EMConference>(){
@Override
public void onSuccess(EMConference value) {
EMLog.i(TAG, "join conference success");
Intent intent = new Intent(MainActivity.this, ConferenceActivity.class);
startActivity(intent);
finish();
}
@Override
public void onError(final int error, final String errorMsg) {
EMLog.e(TAG, "join conference failed error " + error + ", msg " + errorMsg);
runOnUiThread(new Runnable() {
@Override
public void run() {
setBtnEnable(true);
if(error == CALL_TALKER_ISFULL) {
takerFullDialogDisplay();
}else{
Toast.makeText(getApplicationContext(), "Join conference failed " + error + " " + errorMsg, Toast.LENGTH_SHORT).show();
}
}
});
}
});

EMClient.getInstance().conferenceManager().joinRoom() API可以根据房间名创建指定会议,当以该房间名命名的会议不存在时候,会直接创建,当会议已经创建好 可以根据正确的房间名和密码加入房间 ,到这一步为止,我们已经成功的创建 并加入会议。
5.加入会议以后我们进入到会议界面,展示从DemoHelper类 EMConferenceListener 中的 onStreamAdded 回调 和 onMemberJoined 获取到的流和主播列表 ,在ConferenceActivity 中实现 EMConferenceListener ,然后直接把 ConferenceActivity 注册监听,用以下方法  EMClient.getInstance().conferenceManager().addConferenceListener(this); 这样就可实现 EMConferenceListener 事件的处理,比如
 主播 进出房间 :
public void onMemberJoined(final EMConferenceMember member);
public void onMemberExited(final EMConferenceMember member);

增加流 移除流:
public void onStreamAdded(final EMConferenceStream stream)
 public void onStreamRemoved(final EMConferenceStream stream)

管理员变更: 
public void onAdminAdded(String streamId) ;  
public void onAdminRemoved(String streamId)

角色变更  用户被踢  谁在说话等各种回调,可以处理各种业务逻辑 ,详细的请参考 项目中的实现 ,最后会附上项目的开源地址。

6 进入会议房间以后如果用户角色为主播可以进行发布视频流 ,观众只能订阅视频流 不能发布视频流 ,可以调用SDK的publish接口发布流,该接口用到了EMStreamParam参数,你可以自由配置,比如是否上传视频,是否上传音频,使用前置或后置摄像头,视频码率,显示视频页面等等,具体实现可以参考 中发布 订阅视频流的内容, 关于以上的代码逻辑如以 如以下:
 //发布视频流
normalParam = new EMStreamParam();
normalParam.setStreamType(EMConferenceStream.StreamType.NORMAL);
normalParam.setVideoOff(true);
normalParam.setAudioOff(true);

EMClient.getInstance().conferenceManager().publish(normalParam, new EMValueCallBack<String>() {
@Override
public void onSuccess(String value) {
conference.setPubStreamId(value, EMConferenceStream.StreamType.NORMAL);
addOrUpdateStreamList("local-stream", value);

PhoneStateManager.get(ConferenceActivity.this).addStateCallback(phoneStateCallback);
}

@Override
public void onError(int error, String errorMsg) {
EMLog.i(TAG, "publish failed: error=" + error + ", msg=" + errorMsg);
}
});
//订阅其他主播的视频流
private void subscribe(EMConferenceStream stream, EMCallSurfaceView surfaceView) {
EMClient.getInstance().conferenceManager().subscribe(stream, surfaceView, new EMValueCallBack<String>() {
@Override
public void onSuccess(String value) {
}

@Override
public void onError(int error, String errorMsg) {

}
});
}

7.有关上麦 下麦 的逻辑处理,观众可以请求上麦成为主播,主播可以下麦成为观众,上麦 下麦 是利用 EMConferenceAttribute进行处理 ,EMConferenceAttribute  是一个事件广播,广播事件是一个key-value格式,key-value 可以由开发者进行自行定义,增添事件以后 ,服务器会把事件进行广播。会议中成员会收到 onAttributesUpdated回调。例如本项目中的会议上麦 下麦 代码如下所示:
//上麦申请  

EMClient.getInstance().conferenceManager().setConferenceAttribute(
EMClient.getInstance().getCurrentUser(),
"request_tobe_speaker",
new EMValueCallBack<Void>() {
@Override
public void onSuccess(Void value) {
EMLog.i(TAG, "request_tobe_speaker scuessed");

}

@Override
public void onError(int error, String errorMsg) {
EMLog.i(TAG, "request_tobe_speaker failed: error=" + error

}
});

//下麦申请
EMClient.getInstance().conferenceManager().setConferenceAttribute(EMClient.getInstance().getCurrentUser()
, "request_tobe_audience", new EMValueCallBack<Void>() {
@Override
public void onSuccess(Void value) {
EMLog.i(TAG, "request_tobe_audience scuessed");
}

@Override
public void onError(int error, String errorMsg) {
EMLog.i(TAG, "request_tobe_audience failed: error=" + error + ", msg=" + errorMsg);
}
});

 上麦 下麦 请求发出以后 只能由主持人去处理,处理在 EMConferenceListener  的回调 onAttributesUpdated(EMConferenceAttribute attributes) 去处理 ,收到回调以后 解析attributes 然后进行处理请求,处理的过程代码大概如下:
   EMClient.getInstance().conferenceManager().grantRole(conference.getConferenceId()
, new EMConferenceMember(memName, null, null,null)
, EMConferenceManager.EMConferenceRole.Talker, new EMValueCallBack<String>() {
@Override
public void onSuccess(String value) {
EMLog.i(TAG, " requestTalkerDisplay request_tobe_speaker changeRole success, result: " + value);
dialog.dismiss();
}
@Override
public void onError(int error, String errorMsg) {
EMLog.i(TAG, " requestTalkerDisplay request_tobe_speaker changeRole failed, error: " + error + " - " + errorMsg);

}
});

下麦也是和上麦一样是利用 EMConferenceAttribute进行处理。

9.有关退出会议 销毁会议 普通主播  观众只能退出会议 ,主持人还可以 销毁会议 正在进行中的会议可以进行销毁,退出会议 销毁会议 具体代码如下:
 EMClient.getInstance().conferenceManager().exitConference(new EMValueCallBack() {
@Override
public void onSuccess(Object value) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(getApplicationContext(), "您已成功退出当前会议!", Toast.LENGTH_SHORT).show();
}
});
}

@Override
public void onError(int error, String errorMsg) {
EMLog.i(TAG, "exit conference failed " + error + ", " + errorMsg);
}
});

EMClient.getInstance().conferenceManager().destroyConference(new EMValueCallBack() {
@Override
public void onSuccess(Object value) {
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(getApplicationContext(), "您已成功销毁当前会议!", Toast.LENGTH_SHORT).show();
}
});
EMLog.i(TAG, "finish ConferenceActivity");
finish();
}

尾语
至此 整个 多人音视频会议开发的详细步骤 已经完成 ,虽然比较麻烦 但是每个步骤都很清晰 ,有不太清楚的欢迎大家积极讨论, 附上本项目的github地址:https://github.com/easemob/videocall-android  欢迎大家积极参与 ,谢谢支持。
Demo下载 二维码如下 欢迎大家体验(iOS版和web版下载地址请见:https://www.easemob.com/download/rtc)


20200410142228530.png

 
1
评论

微信小程序sdk支持1v1语言通话吗? 环信_WebIM

无为 发表了文章 • 102 次浏览 • 2020-05-28 11:22 • 来自相关话题

微信小程序sdk支持1v1语言通话吗?
微信小程序sdk支持1v1语言通话吗?
1
评论

聊天记录怎么导出呢 环信_WebIM webIm H5

vtchg 发表了文章 • 483 次浏览 • 2019-12-20 15:19 • 来自相关话题

这个sq条件怎么书写呢
这个sq条件怎么书写呢
5
评论

环信webim,收发消息,收发图片,收发表情,未读已读消息数量 环信_WebIM

/fd 发表了文章 • 5577 次浏览 • 2018-05-25 16:43 • 来自相关话题

大家好,这是根据我们公司的项目需求集成的webim demo,具体效果如上图。
此demo不依赖后台纯属前端demo,且同步聊天记录安卓和ios(只要用户不清楚浏览器缓存),数据全部储存在本地
那么接下来 咱们开始看看代码吧
第一步:
    1、注册环信即时通信云获得appkey,注册账号之后登录环信后台创建应用就可以得到appkey
第二步:
    2.引用本地文件
    <script type='text/javascript' src='webim.config.js'></script>  
    <script type='text/javascript' src='strophe-1.2.8.min.js'></script>  
    <script type='text/javascript' src='websdk-1.4.11.js'></script> 
这三个文件的顺序 不要修改 ,就这么引入
然后把appkey替换成自己的就可以。
第三步:
    1.注册
     var options = {  
             username: userid,  
             password: password,  
             nickname: nickname,  
             appKey: WebIM.config.appkey,  
             success: function () {//注册成功之后回调函数  
                                      
             },  
             error: function () {},  
             apiUrl: WebIM.config.apiURL  
      };  
     conn.registerUser(options);  
    2.登录
    var options = {  
        apiUrl: WebIM.config.apiURL,  
        user: user,  
        pwd: password,  
        appKey: WebIM.config.appkey  
    };  
    conn.open(options);  
    3.创建连接
    var conn = new WebIM.connection({  
    isMultiLoginSessions: WebIM.config.isMultiLoginSessions,  
    https: typeof WebIM.config.https === 'boolean' ? WebIM.config.https : location.protocol === 'https:',  
    url: WebIM.config.xmppURL,  
    heartBeatWait: WebIM.config.heartBeatWait,  
    autoReconnectNumMax: WebIM.config.autoReconnectNumMax,  
    autoReconnectInterval: WebIM.config.autoReconnectInterval,  
    apiUrl: WebIM.config.apiURL,  
    isAutoLogin: true  
});  
conn.listen({  
    onOpened: function ( message ) {},      //连接成功回调  
    onClosed: function ( message ) {},         //连接关闭回调  
    onTextMessage: function ( message ) {//收到文本消息  
        console.log(message);  
        console.log('收到'+message.from+'发送的消息:'+message.data);  
        setTimeout(function(){
              //这一步或许有人会问为什么要加setTimeout,在这里解释一下,是为了同步执行下去
              var str = message.ext.chatIcon;
              if(str.indexOf("http")>=0){
                        str = str.slice(32);
               }
              else{
                        str = message.ext.chatIcon;
               }
              //这一步是因为ios和安卓发送消息时,人物头像连接一个是http开头地址  一个是不带本域名的地址,所有要做判断
             var getUserItem = localStorage.getItem('user_id');//这个user_id 是ios和安卓的conversation_id 这个id是在我们人才详情页面,点击立即沟通时,存储到用户的id
             然后进入聊天界面,然后模拟点击显示与此用户的界面
       
            if(message.from==getUserItem){
                getNowFormatDate();//时间函数
                showChatMessage(timestamp3,str,message.data,message.id);//此函数为展示消息函数
                //这里执行这一步,是判断如果收到的消息是当前用户,就直接显示在本聊天界面
                detailMessage(timestamp3,message.data,message.from,'text','',message.id,message.from,str,'','','has_read','');//此函数为存储消息函数
                //为了显示消息,这里是对所有收到的消息都做了存储,下文会介绍消息函数各个参数。
            }
            else{//收到的消息不是当前用户
                detailMessage(timestamp3,message.data,message.from,'text','',message.id,message.from,str,'','','no_read','');//存储消息
                var w = localStorage[message.from];//获取本地所有存储用户的消息列表
                var getList = JSON.parse(localStorage[message.from]);//转换成json数组
                for(var i = 0; i<getList.length; i++){
                        var reserve = getList[i].message[0].reserve;//此处是为存储的消息设置的已读、未读状态(上文的has_read,no_read)
                         if(reserve=='no_read'){
                                 var listNumber = Number(getList.length)-Number(i);//这是获取的未读消息的数量
                                 getList[0].message[0].number=listNumber;//此处是把未读消息的数量存储到 该用户第一条消息的number里面       
                                $('.top-list li.'+message.from+'').find('span.notice-badge').show();
                                $('.top-list li.'+message.from+'').find('span.notice-badge').text(listNumber);
                                //此处是显示未读消息的数量,该li的class是收到消息用户的id
                                localStorage[message.from] = JSON.stringify(getList);
                                return false;//然后把消息的未读已读状态更改保存回本地         
                         }
                }
            }
        },0)
    },  
    onEmojiMessage: function ( message ) {//收到表情消息  
        console.log('收到'+message.from+'发送的Emoji'+':'+message.data);  
        //缓存数据  
        for(var i=0;i<message.data.length;i++){  
            var img = message.data[i];  
            var string;  
            if (img.type=='txt') {string = string+img.data;}  
            else{string = string+'<img class="emoji" '+'src="'+img.data +'">';}  
        }  
        string = string.replace('undefined','');  
        console.log(string);  
       //此处的方法同收到文本消息,不过需要赋予字符串emoji表情标签(转化字符串为img标签)
      //下面代码需要拿来出,放到你的$(function(){})里面,放到下面只是为了,解释给读者
      WebIM.Emoji = {  
            path: '../images/faces/',  
            map: {  
                '[):]': 'ee_1.png',  
                '[:D]': 'ee_2.png',  
                '[;)]': 'ee_3.png',  
                '[:-o]': 'ee_4.png',  
                '[:p]': 'ee_5.png',  
                '[(H)]': 'ee_6.png',  
                '[:@]': 'ee_7.png',  
                '[:s]': 'ee_8.png',  
                '[:$]': 'ee_9.png',  
                '[:(]': 'ee_10.png',  
                '[:\'(]': 'ee_11.png',  
                '[:|]': 'ee_12.png',  
                '[(a)]': 'ee_13.png',  
                '[8o|]': 'ee_14.png',  
                '[|]': 'ee_15.png',  
                '[+o(]': 'ee_16.png',  
                '[<o)]': 'ee_17.png',  
                '[|-)]': 'ee_18.png',  
                '[*-)]': 'ee_19.png',  
                '[:-#]': 'ee_20.png',  
                '[:-*]': 'ee_21.png',  
                '[^o)]': 'ee_22.png',  
                '[8-)]': 'ee_23.png',  
                '[(|)]': 'ee_24.png',  
                '[(u)]': 'ee_25.png',  
                '[(S)]': 'ee_26.png',  
                '[(*)]': 'ee_27.png',  
                '[(#)]': 'ee_28.png',  
                '[(R)]': 'ee_29.png',  
                '[({)]': 'ee_30.png',  
                '[(})]': 'ee_31.png',  
                '[(k)]': 'ee_32.png',  
                '[(F)]': 'ee_33.png',  
                '[(W)]': 'ee_34.png',  
                '[(D)]': 'ee_35.png'  
            }  
        }; 
    },  
    onPictureMessage: function ( message ) {//收到图片消息  
        console.log(message);  
        console.log('收到'+message.from+'发送的图片'+':'+message.url);  
        getNowFormatDate();//时间函数
        showChatMessage(timestamp3,str,message.data,message.id);//此函数为展示图片消息函数
    },  
    onCmdMessage: function ( message ) {},     //收到命令消息  
    onAudioMessage: function ( message ) {},   //收到音频消息  
    onLocationMessage: function ( message ) {},//收到位置消息  
    onFileMessage: function ( message ) {//收到文件消息  
        console.log(message);  
        console.log('收到'+message.from+'发送的文件'+':'+message.url);  
    },  
    onVideoMessage: function (message) {  },   //收到视频消息  
    onPresence: function ( message ) {},       //处理“广播”或“发布-订阅”消息,如联系人订阅请求、处理群组、聊天室被踢解散等消息  
    onRoster: function ( message ) {},         //处理好友申请  
    onInviteMessage: function ( message ) {},  //处理群组邀请  
    onOnline: function () {},                  //本机网络连接成功  
    onOffline: function () {},                 //本机网络掉线  
    onError: function ( message ) {},          //失败回调  
    onBlacklistUpdate: function (list) {},   //黑名单变动                    
    onReceivedMessage: function(message){},    //收到消息送达客户端回执  
    onDeliveredMessage: function(message){},   //收到消息送达服务器回执  
    onReadMessage: function(message){
        //此处为收到已读消息的回执
         setTimeout(function(){//为了保持同步操作
                  var getLength = $("#recordchat-main li.item-myself").length
                  //此处获取的长度是,当前聊天窗口的长度,且消息是本人发出的,因为现实已读未读只显示己方消息
                  for (var i = 0; i < getLength ; i++) {
                      var getLiName = $('#recordchat-main li.item-myself:eq('+i+')').attr('id');
                       //获取当前我发出消息的id,此id为user_id也就是上文的conversation_id
                      // console.log(getLiName)
                      var getMid = $('#recordchat-main li.item-myself:eq('+i+')').attr('mid');
                      //此处是获取当前我发出消息的mid,与消息回执的mid进行匹配
                      //console.log(getMid)
                      if(message.mid==getMid){
                        $('#recordchat-main li.item-myself:eq('+i+')').find('.no-read').text('已读');
                        //如果收到回执的消息mid等于当前窗口消息列表的mid,把当前的未读状态改变成已读状态
                      }
                      statusRead('已读',message.mid,getLiName,'');//此函数是把每一条的消息mid存储到本地且存储了状态
                  }
                  
          },0)
},        //收到消息已读回执  
    onCreateGroup: function(message){},        //创建群组成功回执(需调用createGroupNew)  
    onMutedMessage: function(message){}        //如果用户在A群组被禁言,在A群发消息会走这个回调并且消息不会传递给群其它成员  
});  

//此处是左侧联系人列表,此方法是应用的layui的流加载,本想着用户多了,会使用layim+环信集成社区聊天模板
        flow.load({
            elem: '.top-list' //流加载容器
            ,mb:10
            ,isAuto: false
            ,isLazyimg: true
            ,done: function(page, next){ //执行下一页的回调
                //数据插入
                $.ajax({
                    url:'',
                    type:'get',
                    data:{page:page,user_type:2},
                    error:function(data){
                        layer.msg("世上难得两全法,您看是不是您的网络问题,如果不是刷新一下试试哦~")
                    },
                    success:function(data){
                        // console.log(data)
                        var obj = eval('('+data+')');
                        var length = obj.result.length;
                        var pages = length/10;
                        var lis = [];
                        for(var i = 0; i < length; i++)
                        {
                            lis.push('<li class='+obj.result[i].conversation_id+' chatId="'+obj.result[i].conversation_id+'" chatIcon="'+obj.result[i].info.user_img+'" add_time="'+obj.result[i].add_time+'" uid="'+obj.result[i].chat_id+'" chatName = "'+obj.result[i].conversation_id+'" career_name = "'+obj.result[i].info.career_name+'" city = "'+obj.result[i].info.city+'" education = "'+obj.result[i].info.education+'" industry_id = "'+obj.result[i].info.industry_id+'" salary_range = "'+obj.result[i].info.salary_range+'" school_name="'+obj.result[i].info.school_name+'" school_status="'+obj.result[i].info.school_status+'" specialty="'+obj.result[i].info.specialty+'" user_age="'+obj.result[i].info.user_age+'" user_sex="'+obj.result[i].info.user_sex+'" user_name="'+obj.result[i].info.user_name+'"><a href="javascript:;" data-url=""><div class="figure"><img src="'+obj.result[i].info.user_img+'"></div><div class="text"><div class="title"><div class="text-clear"><span class="name">'+obj.result[i].info.user_name+'</span><span class="time">'+obj.result[i].add_time+'</span></div><p class="gray"> '+obj.result[i].info.school_name+' | '+obj.result[i].info.career_name+' | '+obj.result[i].info.education+'</p></div><span class="notice-badge" style="display: none;"></span></div></a></li>')
                        }
                        next(lis.join(''),page< pages); //总页数
                        var getUserId = localStorage.getItem("user_id");
                        //模拟点击 当用户直接从人才列表点击进来
                        for (var i = 0; i < $(".top-list li").length; i++) {
                            var getItem = $('.top-list li:eq('+i+')').attr('chatid');
                            // console.log(getItem)此处是conversation_id
                            if(getUserId==getItem){
                                $('.top-list li:eq('+i+')').trigger("click");
                                var height = $("#recordchat-main").height();
                                $("#chat-list").scrollTop(height);
                                //此处代码是项目中有需求,点击左侧联系人的时候,右侧聊天窗口显示最底部的消息
                            } 
                            if(localStorage[getItem]){
                            var chatSen = localStorage[getItem];
                            //console.log(chatSen)然后在加载左侧结束的同时,获取本地存储所有该用户的聊天记录
                            var chatSenGetItem = JSON.parse(localStorage[getItem]);
                            //console.log(chatSenGetItem)转换改聊天记录为json数组
                            var number = chatSenGetItem[0].message[0].number;
                            //console.log(number)此处为读取存储本地未读的消息
                            if(number!=''){
                            $('.top-list li:eq('+i+')').find("span.notice-badge").show();
                            $('.top-list li:eq('+i+')').find("span.notice-badge").text(number);
                            }
                            else{
                            $('.top-list li:eq('+i+')').find("span.notice-badge").hide();
                            $('.top-list li:eq('+i+')').find("span.notice-badge").text(number);
                            }
                            //未读消息的展示
                            }
                        } 
                    }
                });
            }
        })
//此处是左侧联系人列表点击的时候,由于没有后台支撑,所有我把所有的用户信息,全部存储到li里面,此处只是demo测试,正式版本肯定会有接口支持
        $(".top-list").on("click","li",function(){
            //此处点击切换数据代码已裁剪掉,只写本地存储,im相关   
           var chatId = $(this).attr("class") //上文提到过conversation_id为了li的class
            //获取聊天记录
            if(localStorage[chatId]){
            var localContent = JSON.parse(localStorage[chatId]);//点击获取该用户的所有聊天记录
            if(localContent[0].message[0].number!=''){//此number是未读消息的数量
            localContent[0].message[0].number='';//如果未读消息不为空的话,点击该用户,该用户的未读消息数量清空
            localStorage[chatId]=JSON.stringify(localContent);
            //console.log(localContent)
            }
                var localContent = JSON.parse(localStorage[chatId]);
                //console.log(localContent)
                for (var i = 0; i < localContent.length; i++) {
                    var data = localContent[i].message[0].data;
                    var chatId = localContent[i].message[0].chatId;
                    var from = localContent[i].message[0].from;
                    var time = localContent[i].message[0].time;
                    var type = localContent[i].message[0].type;
                    var filename = localContent[i].message[0].filename;
                    var id = localContent[i].message[0].id;
                    var mid = localContent[i].message[0].mid;
                    var chatIcon = localContent[i].message[0].chatIcon;
                    var reserve = localContent[i].message[0].reserve;
                    if(chatIcon==undefined){
                        chatIcon = '/head_img/12064_15087368603690.png';//如果数据库人物头像丢失,显示默认头像
                    }
                    if(localContent[i].message[0].reserve=='no_read'){
                       // localStorage[chatId].message[0].reserve = 'has_read'
                        localContent[i].message[0].reserve='has_read';
                        //console.log(localContent)
                        localContent[0].message[0].number='';
                        localStorage[chatId]=JSON.stringify(localContent);
                        //console.log(localStorage[chatId])  判断该消息的未读已读状态,点击该用户进行转换状态
                    }
                    var chat_status;
                    // console.log(localStorage[chatId])
                    if(localStorage[id]){
                        var localStatusContent = JSON.parse(localStorage[id]);
                        //console.log(localStatusContent)
                        chat_status = localStatusContent[0].status;
                    }
                    else{
                        //console.log(localStorage[id])
                        chat_status = '未读';
                    }
                    if(from=='me'){
                        if(type=='text'){
                            $("#recordchat-main").append('<li class="item-time"><span class="time">'+time+'</span></li><li class="item-myself" id='+id+' mid='+mid+'><div class="no-read">'+chat_status+'</div><div class="text">'+data+'</div></li>');
                        }
                        else if(type=='picture'){
                            $("#recordchat-main").append('<li class="item-time"><span class="time">'+time+'</span></li><li class="selfPrture item-myself" id='+id+' mid='+mid+'><div class="text"><img class="img_url" src='+data+' alt="内容图片" /></div></li>');
                        }
                    }else{
                        if(type=='text'){
                            $("#recordchat-main").append(
                             '<li class="item-time"><span class="time">'+time+'</span></li>'
                            +'<li class="item-friend" id='+mid+'>'
                            +'<div class="figure"><img src="http://beta.app.first-job-1.com'+chatIcon+'" alt="人物头像" /></div>'//<img src="http://beta.app.first-job-1.com'+message.ext.chatIcon+'">
                            +'<div class="text">'+data+'</div>'
                            +'</li>');
                        }
                        else if(type=='picture'){
                            $("#recordchat-main").append(
                             '<li class="item-time"><span class="time">'+time+'</span></li>'
                            +'<li class="item-friend selfPrture" id='+mid+'>'
                            +'<div class="figure"><img src="http://beta.app.first-job-1.com'+chatIcon+'" alt="人物头像" /></div>'
                            +'<div class="text"><img class="img_url" src='+data+' alt="内容图片" /></div>'
                            +'</li>');
                        }
                    }
                }
                //刷新时读取本地消息 展示在聊天窗口
                //此处为jq写法插入数据,正式版本是改成tpl模板渲染,想想用2句代码就可以展示消息 还是蛮激动的。
            }
            else{
                //console.log("暂无聊天记录")
            }
            $(this).find('span.notice-badge').hide();//隐藏数量小红点
        }) 
//发送文本消息函数
        var sendPrivateText = function(msg_content){
            var name = top.$(".figure>a>img").attr("src");//该企业用户头像
            var chatIcon = name.slice(31);//该企业用户头像地址裁剪
            var chatName = top.$(".figure>span").text();//该企业用户名称
            var chatId = $(".chatId").val();//chat-id
            var id = conn.getUniqueId();
            var msg = new WebIM.message('txt', id);
            msg.set({
                msg: msg_content,
                to: chatId,
                roomType: false,
                chatType: 'singleChat', 
                success: function(id, serverMsgId){
                    $(".chat-input").html("");
                    getNowFormatDate();
                    var emojiMessage = WebIM.utils.parseEmoji(msg_content); //表情解析工具
                   showChatMessage(timestamp3,str,message.data,message.id);
                    var recordchat = document.getElementById('chat-list');
                    recordchat.scrollTop = recordchat.scrollHeight;//发送消息时,滚动条出现在底部
                    //暂时插入数据,tpl绑定模板插入渲染
                    $("#btn-send").addClass("disabled");//发送按钮置灰
                    detailMessage(timestamp3,emojiMessage,"me",'text',id,serverMsgId,chatId,'','未读','','','');  
                },
                fail: function(e){
                    //console.log("fail")
                }
            });

            msg.body.chatType = 'singleChat';
            // msg.setGroup('singleChat');  
            msg.body.ext.chatName= chatName;//传递chatName->ios,java
            msg.body.ext.chatIcon= chatIcon;//传递chatIcon->ios,java
            conn.send(msg.body);
        };
//发送图片消息函数
        var sendPrivateFile = function () {
            var name = top.$(".figure>a>img").attr("src");//该企业用户头像
            var chatIcon = name.slice(31);//该企业用户头像地址裁剪
            var chatName = top.$(".figure>span").text();//该企业用户名称
            var chatId = $(".chatId").val();//chat-id
            var id = conn.getUniqueId();                   // 生成本地消息id  
            var msg = new WebIM.message('img', id);        // 创建图片消息  
            var input = document.getElementById('image');  // 选择图片的input   id必填  
            var file = WebIM.utils.getFileUrl(input);      // 将图片转化为二进制文件  
            var allowType = {'jpg': true,'gif': true,'png': true,'bmp': true};  
            var img_url;  
            if (file.filetype.toLowerCase() in allowType) {  
                var option = {  
                    apiUrl: WebIM.config.apiURL,  
                    file: file,  
                    to: chatId,                       // 接收消息对象  
                    roomType: false,  
                    chatType: 'singleChat',  
                    onFileUploadError: function () {      // 消息上传失败  
                        //console.log('图片发送失败!');  
                    },  
                    onFileUploadComplete: function (aa) {   // 消息上传成功  
                        //console.log('onFileUploadComplete');
                        img_url = aa.uri+"/"+aa.entities[0].uuid;  
                    },  
                    success: function (id, serverMsgId) {                // 消息发送成功 
                        //console.log(id)
                        getNowFormatDate() 
                        showChatMessage(timestamp3,str,message.data,message.id);
                        var recordchat = document.getElementById('chat-list');
                        recordchat.scrollTop = recordchat.scrollHeight;
                        detailMessage(timestamp3,img_url,"me",'picture',file.id,serverMsgId,chatId,'','','','',''); 
                        // showMessage();
                    },  
                    flashUpload: WebIM.flashUpload  
                };  
                msg.set(option); 
                msg.body.chatType = 'singleChat'; 
                msg.body.ext.chatName= chatName;//传递chatName->ios,java
                msg.body.ext.chatIcon= chatIcon;//传递chatIcon->ios,java
                conn.send(msg.body);  
            }  
        };
        //时间函数
        function getNowFormatDate(){
          var timestamp1 = new Date().getTime();//获取时间戳此方法准确
          var timestamp2 = new Date(timestamp1);
          timestamp3 = timestamp2.toLocaleDateString().replace(/\//g, "-") + " " + timestamp2.toTimeString().substr(0, 8);
        }
       //展示消息  根据个人项目需求,正式版本会使用tpl模板 
        function showChatMessage(timestamp3,str,data,id){
            $("#recordchat-main").append(
                             '<li class="item-time"><span class="time">'+timestamp3+'</span></li>'
                            +'<li class="item-friend" id='+id+'>'
                            +'<div class="figure"><img src="http://beta.app.first-job-1.com'+str+'"></div>'
                            +'<div class="text">'+data+'</div>'
                            +'</li>');
        }
     //绑定数据模板
        function detailMessage(timestamp3,data,from,type,id,mid,chatId,chatIcon,status,filename,reserve,number){  
            var localContent = new Array();
            if (localStorage[chatId]) {  
                localContent = JSON.parse(localStorage[chatId]);  
            }  
            localContent[localContent.length]= { 'message':[{ 
                'time':timestamp3,  
                'data':data,//数据  
                'from':from,//谁发的  
                'type':type,//文本类型 text,file,picture  
                'id':id,//消息id  
                'mid':mid,
                'chatId':chatId,
                'chatIcon':chatIcon,
                'status':status,//状态
                'filename':filename, //文件名字 
                'reserve':reserve,//已读未读
                'number':number,//未读数量
            }]};  
            localStorage[chatId] = JSON.stringify(localContent);//存储本地; 
            //console.log(JSON.parse(localStorage[chatId]))
            
        }  
       //根据每一条消息的id存储本地 存储未读已读状态
        function statusRead(status,mid,id,reserve){
            var localStatusContent = new Array();
            if(localStatusContent[id]){
                localStatusContent = JSON.parse(localStorage[id]); 

            }
            localStatusContent[localStatusContent.length]={
                'status':status,
                'mid':mid,
                'id':id,
                'reserve':reserve
            };
            localStorage[id] = JSON.stringify(localStatusContent);
            //console.log(localStorage[id])
        }

项目需求只有表情和图片,文件视频,音频其实都是属于文件的一种,若有帮助请赞赏一下吧。












  查看全部


QQ截图20180525151449.jpg


QQ图片20180525154227.png



大家好,这是根据我们公司的项目需求集成的webim demo,具体效果如上图。
此demo不依赖后台纯属前端demo,且同步聊天记录安卓和ios(只要用户不清楚浏览器缓存),数据全部储存在本地
那么接下来 咱们开始看看代码吧
第一步:
    1、注册环信即时通信云获得appkey,注册账号之后登录环信后台创建应用就可以得到appkey
第二步:
    2.引用本地文件
    <script type='text/javascript' src='webim.config.js'></script>  
    <script type='text/javascript' src='strophe-1.2.8.min.js'></script>  
    <script type='text/javascript' src='websdk-1.4.11.js'></script> 
这三个文件的顺序 不要修改 ,就这么引入
然后把appkey替换成自己的就可以。
第三步:
    1.注册
     var options = {  
             username: userid,  
             password: password,  
             nickname: nickname,  
             appKey: WebIM.config.appkey,  
             success: function () {//注册成功之后回调函数  
                                      
             },  
             error: function () {},  
             apiUrl: WebIM.config.apiURL  
      };  
     conn.registerUser(options);  
    2.登录
    var options = {  
        apiUrl: WebIM.config.apiURL,  
        user: user,  
        pwd: password,  
        appKey: WebIM.config.appkey  
    };  
    conn.open(options);  
    3.创建连接
    var conn = new WebIM.connection({  
    isMultiLoginSessions: WebIM.config.isMultiLoginSessions,  
    https: typeof WebIM.config.https === 'boolean' ? WebIM.config.https : location.protocol === 'https:',  
    url: WebIM.config.xmppURL,  
    heartBeatWait: WebIM.config.heartBeatWait,  
    autoReconnectNumMax: WebIM.config.autoReconnectNumMax,  
    autoReconnectInterval: WebIM.config.autoReconnectInterval,  
    apiUrl: WebIM.config.apiURL,  
    isAutoLogin: true  
});  
conn.listen({  
    onOpened: function ( message ) {},      //连接成功回调  
    onClosed: function ( message ) {},         //连接关闭回调  
    onTextMessage: function ( message ) {//收到文本消息  
        console.log(message);  
        console.log('收到'+message.from+'发送的消息:'+message.data);  
        setTimeout(function(){
              //这一步或许有人会问为什么要加setTimeout,在这里解释一下,是为了同步执行下去
              var str = message.ext.chatIcon;
              if(str.indexOf("http")>=0){
                        str = str.slice(32);
               }
              else{
                        str = message.ext.chatIcon;
               }
              //这一步是因为ios和安卓发送消息时,人物头像连接一个是http开头地址  一个是不带本域名的地址,所有要做判断
             var getUserItem = localStorage.getItem('user_id');//这个user_id 是ios和安卓的conversation_id 这个id是在我们人才详情页面,点击立即沟通时,存储到用户的id
             然后进入聊天界面,然后模拟点击显示与此用户的界面
       
            if(message.from==getUserItem){
                getNowFormatDate();//时间函数
                showChatMessage(timestamp3,str,message.data,message.id);//此函数为展示消息函数
                //这里执行这一步,是判断如果收到的消息是当前用户,就直接显示在本聊天界面
                detailMessage(timestamp3,message.data,message.from,'text','',message.id,message.from,str,'','','has_read','');//此函数为存储消息函数
                //为了显示消息,这里是对所有收到的消息都做了存储,下文会介绍消息函数各个参数。
            }
            else{//收到的消息不是当前用户
                detailMessage(timestamp3,message.data,message.from,'text','',message.id,message.from,str,'','','no_read','');//存储消息
                var w = localStorage[message.from];//获取本地所有存储用户的消息列表
                var getList = JSON.parse(localStorage[message.from]);//转换成json数组
                for(var i = 0; i<getList.length; i++){
                        var reserve = getList[i].message[0].reserve;//此处是为存储的消息设置的已读、未读状态(上文的has_read,no_read)
                         if(reserve=='no_read'){
                                 var listNumber = Number(getList.length)-Number(i);//这是获取的未读消息的数量
                                 getList[0].message[0].number=listNumber;//此处是把未读消息的数量存储到 该用户第一条消息的number里面       
                                $('.top-list li.'+message.from+'').find('span.notice-badge').show();
                                $('.top-list li.'+message.from+'').find('span.notice-badge').text(listNumber);
                                //此处是显示未读消息的数量,该li的class是收到消息用户的id
                                localStorage[message.from] = JSON.stringify(getList);
                                return false;//然后把消息的未读已读状态更改保存回本地         
                         }
                }
            }
        },0)
    },  
    onEmojiMessage: function ( message ) {//收到表情消息  
        console.log('收到'+message.from+'发送的Emoji'+':'+message.data);  
        //缓存数据  
        for(var i=0;i<message.data.length;i++){  
            var img = message.data[i];  
            var string;  
            if (img.type=='txt') {string = string+img.data;}  
            else{string = string+'<img class="emoji" '+'src="'+img.data +'">';}  
        }  
        string = string.replace('undefined','');  
        console.log(string);  
       //此处的方法同收到文本消息,不过需要赋予字符串emoji表情标签(转化字符串为img标签)
      //下面代码需要拿来出,放到你的$(function(){})里面,放到下面只是为了,解释给读者
      WebIM.Emoji = {  
            path: '../images/faces/',  
            map: {  
                '[):]': 'ee_1.png',  
                '[:D]': 'ee_2.png',  
                '[;)]': 'ee_3.png',  
                '[:-o]': 'ee_4.png',  
                '[:p]': 'ee_5.png',  
                '[(H)]': 'ee_6.png',  
                '[:@]': 'ee_7.png',  
                '[:s]': 'ee_8.png',  
                '[:$]': 'ee_9.png',  
                '[:(]': 'ee_10.png',  
                '[:\'(]': 'ee_11.png',  
                '[:|]': 'ee_12.png',  
                '[(a)]': 'ee_13.png',  
                '[8o|]': 'ee_14.png',  
                '[|]': 'ee_15.png',  
                '[+o(]': 'ee_16.png',  
                '[<o)]': 'ee_17.png',  
                '[|-)]': 'ee_18.png',  
                '[*-)]': 'ee_19.png',  
                '[:-#]': 'ee_20.png',  
                '[:-*]': 'ee_21.png',  
                '[^o)]': 'ee_22.png',  
                '[8-)]': 'ee_23.png',  
                '[(|)]': 'ee_24.png',  
                '[(u)]': 'ee_25.png',  
                '[(S)]': 'ee_26.png',  
                '[(*)]': 'ee_27.png',  
                '[(#)]': 'ee_28.png',  
                '[(R)]': 'ee_29.png',  
                '[({)]': 'ee_30.png',  
                '[(})]': 'ee_31.png',  
                '[(k)]': 'ee_32.png',  
                '[(F)]': 'ee_33.png',  
                '[(W)]': 'ee_34.png',  
                '[(D)]': 'ee_35.png'  
            }  
        }; 
    },  
    onPictureMessage: function ( message ) {//收到图片消息  
        console.log(message);  
        console.log('收到'+message.from+'发送的图片'+':'+message.url);  
        getNowFormatDate();//时间函数
        showChatMessage(timestamp3,str,message.data,message.id);//此函数为展示图片消息函数
    },  
    onCmdMessage: function ( message ) {},     //收到命令消息  
    onAudioMessage: function ( message ) {},   //收到音频消息  
    onLocationMessage: function ( message ) {},//收到位置消息  
    onFileMessage: function ( message ) {//收到文件消息  
        console.log(message);  
        console.log('收到'+message.from+'发送的文件'+':'+message.url);  
    },  
    onVideoMessage: function (message) {  },   //收到视频消息  
    onPresence: function ( message ) {},       //处理“广播”或“发布-订阅”消息,如联系人订阅请求、处理群组、聊天室被踢解散等消息  
    onRoster: function ( message ) {},         //处理好友申请  
    onInviteMessage: function ( message ) {},  //处理群组邀请  
    onOnline: function () {},                  //本机网络连接成功  
    onOffline: function () {},                 //本机网络掉线  
    onError: function ( message ) {},          //失败回调  
    onBlacklistUpdate: function (list) {},   //黑名单变动                    
    onReceivedMessage: function(message){},    //收到消息送达客户端回执  
    onDeliveredMessage: function(message){},   //收到消息送达服务器回执  
    onReadMessage: function(message){
        //此处为收到已读消息的回执
         setTimeout(function(){//为了保持同步操作
                  var getLength = $("#recordchat-main li.item-myself").length
                  //此处获取的长度是,当前聊天窗口的长度,且消息是本人发出的,因为现实已读未读只显示己方消息
                  for (var i = 0; i < getLength ; i++) {
                      var getLiName = $('#recordchat-main li.item-myself:eq('+i+')').attr('id');
                       //获取当前我发出消息的id,此id为user_id也就是上文的conversation_id
                      // console.log(getLiName)
                      var getMid = $('#recordchat-main li.item-myself:eq('+i+')').attr('mid');
                      //此处是获取当前我发出消息的mid,与消息回执的mid进行匹配
                      //console.log(getMid)
                      if(message.mid==getMid){
                        $('#recordchat-main li.item-myself:eq('+i+')').find('.no-read').text('已读');
                        //如果收到回执的消息mid等于当前窗口消息列表的mid,把当前的未读状态改变成已读状态
                      }
                      statusRead('已读',message.mid,getLiName,'');//此函数是把每一条的消息mid存储到本地且存储了状态
                  }
                  
          },0)
},        //收到消息已读回执  
    onCreateGroup: function(message){},        //创建群组成功回执(需调用createGroupNew)  
    onMutedMessage: function(message){}        //如果用户在A群组被禁言,在A群发消息会走这个回调并且消息不会传递给群其它成员  
});  

//此处是左侧联系人列表,此方法是应用的layui的流加载,本想着用户多了,会使用layim+环信集成社区聊天模板
        flow.load({
            elem: '.top-list' //流加载容器
            ,mb:10
            ,isAuto: false
            ,isLazyimg: true
            ,done: function(page, next){ //执行下一页的回调
                //数据插入
                $.ajax({
                    url:'',
                    type:'get',
                    data:{page:page,user_type:2},
                    error:function(data){
                        layer.msg("世上难得两全法,您看是不是您的网络问题,如果不是刷新一下试试哦~")
                    },
                    success:function(data){
                        // console.log(data)
                        var obj = eval('('+data+')');
                        var length = obj.result.length;
                        var pages = length/10;
                        var lis = [];
                        for(var i = 0; i < length; i++)
                        {
                            lis.push('<li class='+obj.result[i].conversation_id+' chatId="'+obj.result[i].conversation_id+'" chatIcon="'+obj.result[i].info.user_img+'" add_time="'+obj.result[i].add_time+'" uid="'+obj.result[i].chat_id+'" chatName = "'+obj.result[i].conversation_id+'" career_name = "'+obj.result[i].info.career_name+'" city = "'+obj.result[i].info.city+'" education = "'+obj.result[i].info.education+'" industry_id = "'+obj.result[i].info.industry_id+'" salary_range = "'+obj.result[i].info.salary_range+'" school_name="'+obj.result[i].info.school_name+'" school_status="'+obj.result[i].info.school_status+'" specialty="'+obj.result[i].info.specialty+'" user_age="'+obj.result[i].info.user_age+'" user_sex="'+obj.result[i].info.user_sex+'" user_name="'+obj.result[i].info.user_name+'"><a href="javascript:;" data-url=""><div class="figure"><img src="'+obj.result[i].info.user_img+'"></div><div class="text"><div class="title"><div class="text-clear"><span class="name">'+obj.result[i].info.user_name+'</span><span class="time">'+obj.result[i].add_time+'</span></div><p class="gray"> '+obj.result[i].info.school_name+' | '+obj.result[i].info.career_name+' | '+obj.result[i].info.education+'</p></div><span class="notice-badge" style="display: none;"></span></div></a></li>')
                        }
                        next(lis.join(''),page< pages); //总页数
                        var getUserId = localStorage.getItem("user_id");
                        //模拟点击 当用户直接从人才列表点击进来
                        for (var i = 0; i < $(".top-list li").length; i++) {
                            var getItem = $('.top-list li:eq('+i+')').attr('chatid');
                            // console.log(getItem)此处是conversation_id
                            if(getUserId==getItem){
                                $('.top-list li:eq('+i+')').trigger("click");
                                var height = $("#recordchat-main").height();
                                $("#chat-list").scrollTop(height);
                                //此处代码是项目中有需求,点击左侧联系人的时候,右侧聊天窗口显示最底部的消息
                            } 
                            if(localStorage[getItem]){
                            var chatSen = localStorage[getItem];
                            //console.log(chatSen)然后在加载左侧结束的同时,获取本地存储所有该用户的聊天记录
                            var chatSenGetItem = JSON.parse(localStorage[getItem]);
                            //console.log(chatSenGetItem)转换改聊天记录为json数组
                            var number = chatSenGetItem[0].message[0].number;
                            //console.log(number)此处为读取存储本地未读的消息
                            if(number!=''){
                            $('.top-list li:eq('+i+')').find("span.notice-badge").show();
                            $('.top-list li:eq('+i+')').find("span.notice-badge").text(number);
                            }
                            else{
                            $('.top-list li:eq('+i+')').find("span.notice-badge").hide();
                            $('.top-list li:eq('+i+')').find("span.notice-badge").text(number);
                            }
                            //未读消息的展示
                            }
                        } 
                    }
                });
            }
        })
//此处是左侧联系人列表点击的时候,由于没有后台支撑,所有我把所有的用户信息,全部存储到li里面,此处只是demo测试,正式版本肯定会有接口支持
        $(".top-list").on("click","li",function(){
            //此处点击切换数据代码已裁剪掉,只写本地存储,im相关   
           var chatId = $(this).attr("class") //上文提到过conversation_id为了li的class
            //获取聊天记录
            if(localStorage[chatId]){
            var localContent = JSON.parse(localStorage[chatId]);//点击获取该用户的所有聊天记录
            if(localContent[0].message[0].number!=''){//此number是未读消息的数量
            localContent[0].message[0].number='';//如果未读消息不为空的话,点击该用户,该用户的未读消息数量清空
            localStorage[chatId]=JSON.stringify(localContent);
            //console.log(localContent)
            }
                var localContent = JSON.parse(localStorage[chatId]);
                //console.log(localContent)
                for (var i = 0; i < localContent.length; i++) {
                    var data = localContent[i].message[0].data;
                    var chatId = localContent[i].message[0].chatId;
                    var from = localContent[i].message[0].from;
                    var time = localContent[i].message[0].time;
                    var type = localContent[i].message[0].type;
                    var filename = localContent[i].message[0].filename;
                    var id = localContent[i].message[0].id;
                    var mid = localContent[i].message[0].mid;
                    var chatIcon = localContent[i].message[0].chatIcon;
                    var reserve = localContent[i].message[0].reserve;
                    if(chatIcon==undefined){
                        chatIcon = '/head_img/12064_15087368603690.png';//如果数据库人物头像丢失,显示默认头像
                    }
                    if(localContent[i].message[0].reserve=='no_read'){
                       // localStorage[chatId].message[0].reserve = 'has_read'
                        localContent[i].message[0].reserve='has_read';
                        //console.log(localContent)
                        localContent[0].message[0].number='';
                        localStorage[chatId]=JSON.stringify(localContent);
                        //console.log(localStorage[chatId])  判断该消息的未读已读状态,点击该用户进行转换状态
                    }
                    var chat_status;
                    // console.log(localStorage[chatId])
                    if(localStorage[id]){
                        var localStatusContent = JSON.parse(localStorage[id]);
                        //console.log(localStatusContent)
                        chat_status = localStatusContent[0].status;
                    }
                    else{
                        //console.log(localStorage[id])
                        chat_status = '未读';
                    }
                    if(from=='me'){
                        if(type=='text'){
                            $("#recordchat-main").append('<li class="item-time"><span class="time">'+time+'</span></li><li class="item-myself" id='+id+' mid='+mid+'><div class="no-read">'+chat_status+'</div><div class="text">'+data+'</div></li>');
                        }
                        else if(type=='picture'){
                            $("#recordchat-main").append('<li class="item-time"><span class="time">'+time+'</span></li><li class="selfPrture item-myself" id='+id+' mid='+mid+'><div class="text"><img class="img_url" src='+data+' alt="内容图片" /></div></li>');
                        }
                    }else{
                        if(type=='text'){
                            $("#recordchat-main").append(
                             '<li class="item-time"><span class="time">'+time+'</span></li>'
                            +'<li class="item-friend" id='+mid+'>'
                            +'<div class="figure"><img src="http://beta.app.first-job-1.com'+chatIcon+'" alt="人物头像" /></div>'//<img src="http://beta.app.first-job-1.com'+message.ext.chatIcon+'">
                            +'<div class="text">'+data+'</div>'
                            +'</li>');
                        }
                        else if(type=='picture'){
                            $("#recordchat-main").append(
                             '<li class="item-time"><span class="time">'+time+'</span></li>'
                            +'<li class="item-friend selfPrture" id='+mid+'>'
                            +'<div class="figure"><img src="http://beta.app.first-job-1.com'+chatIcon+'" alt="人物头像" /></div>'
                            +'<div class="text"><img class="img_url" src='+data+' alt="内容图片" /></div>'
                            +'</li>');
                        }
                    }
                }
                //刷新时读取本地消息 展示在聊天窗口
                //此处为jq写法插入数据,正式版本是改成tpl模板渲染,想想用2句代码就可以展示消息 还是蛮激动的。
            }
            else{
                //console.log("暂无聊天记录")
            }
            $(this).find('span.notice-badge').hide();//隐藏数量小红点
        }) 
//发送文本消息函数
        var sendPrivateText = function(msg_content){
            var name = top.$(".figure>a>img").attr("src");//该企业用户头像
            var chatIcon = name.slice(31);//该企业用户头像地址裁剪
            var chatName = top.$(".figure>span").text();//该企业用户名称
            var chatId = $(".chatId").val();//chat-id
            var id = conn.getUniqueId();
            var msg = new WebIM.message('txt', id);
            msg.set({
                msg: msg_content,
                to: chatId,
                roomType: false,
                chatType: 'singleChat', 
                success: function(id, serverMsgId){
                    $(".chat-input").html("");
                    getNowFormatDate();
                    var emojiMessage = WebIM.utils.parseEmoji(msg_content); //表情解析工具
                   showChatMessage(timestamp3,str,message.data,message.id);
                    var recordchat = document.getElementById('chat-list');
                    recordchat.scrollTop = recordchat.scrollHeight;//发送消息时,滚动条出现在底部
                    //暂时插入数据,tpl绑定模板插入渲染
                    $("#btn-send").addClass("disabled");//发送按钮置灰
                    detailMessage(timestamp3,emojiMessage,"me",'text',id,serverMsgId,chatId,'','未读','','','');  
                },
                fail: function(e){
                    //console.log("fail")
                }
            });

            msg.body.chatType = 'singleChat';
            // msg.setGroup('singleChat');  
            msg.body.ext.chatName= chatName;//传递chatName->ios,java
            msg.body.ext.chatIcon= chatIcon;//传递chatIcon->ios,java
            conn.send(msg.body);
        };
//发送图片消息函数
        var sendPrivateFile = function () {
            var name = top.$(".figure>a>img").attr("src");//该企业用户头像
            var chatIcon = name.slice(31);//该企业用户头像地址裁剪
            var chatName = top.$(".figure>span").text();//该企业用户名称
            var chatId = $(".chatId").val();//chat-id
            var id = conn.getUniqueId();                   // 生成本地消息id  
            var msg = new WebIM.message('img', id);        // 创建图片消息  
            var input = document.getElementById('image');  // 选择图片的input   id必填  
            var file = WebIM.utils.getFileUrl(input);      // 将图片转化为二进制文件  
            var allowType = {'jpg': true,'gif': true,'png': true,'bmp': true};  
            var img_url;  
            if (file.filetype.toLowerCase() in allowType) {  
                var option = {  
                    apiUrl: WebIM.config.apiURL,  
                    file: file,  
                    to: chatId,                       // 接收消息对象  
                    roomType: false,  
                    chatType: 'singleChat',  
                    onFileUploadError: function () {      // 消息上传失败  
                        //console.log('图片发送失败!');  
                    },  
                    onFileUploadComplete: function (aa) {   // 消息上传成功  
                        //console.log('onFileUploadComplete');
                        img_url = aa.uri+"/"+aa.entities[0].uuid;  
                    },  
                    success: function (id, serverMsgId) {                // 消息发送成功 
                        //console.log(id)
                        getNowFormatDate() 
                        showChatMessage(timestamp3,str,message.data,message.id);
                        var recordchat = document.getElementById('chat-list');
                        recordchat.scrollTop = recordchat.scrollHeight;
                        detailMessage(timestamp3,img_url,"me",'picture',file.id,serverMsgId,chatId,'','','','',''); 
                        // showMessage();
                    },  
                    flashUpload: WebIM.flashUpload  
                };  
                msg.set(option); 
                msg.body.chatType = 'singleChat'; 
                msg.body.ext.chatName= chatName;//传递chatName->ios,java
                msg.body.ext.chatIcon= chatIcon;//传递chatIcon->ios,java
                conn.send(msg.body);  
            }  
        };
        //时间函数
        function getNowFormatDate(){
          var timestamp1 = new Date().getTime();//获取时间戳此方法准确
          var timestamp2 = new Date(timestamp1);
          timestamp3 = timestamp2.toLocaleDateString().replace(/\//g, "-") + " " + timestamp2.toTimeString().substr(0, 8);
        }
       //展示消息  根据个人项目需求,正式版本会使用tpl模板 
        function showChatMessage(timestamp3,str,data,id){
            $("#recordchat-main").append(
                             '<li class="item-time"><span class="time">'+timestamp3+'</span></li>'
                            +'<li class="item-friend" id='+id+'>'
                            +'<div class="figure"><img src="http://beta.app.first-job-1.com'+str+'"></div>'
                            +'<div class="text">'+data+'</div>'
                            +'</li>');
        }
     //绑定数据模板
        function detailMessage(timestamp3,data,from,type,id,mid,chatId,chatIcon,status,filename,reserve,number){  
            var localContent = new Array();
            if (localStorage[chatId]) {  
                localContent = JSON.parse(localStorage[chatId]);  
            }  
            localContent[localContent.length]= { 'message':[{ 
                'time':timestamp3,  
                'data':data,//数据  
                'from':from,//谁发的  
                'type':type,//文本类型 text,file,picture  
                'id':id,//消息id  
                'mid':mid,
                'chatId':chatId,
                'chatIcon':chatIcon,
                'status':status,//状态
                'filename':filename, //文件名字 
                'reserve':reserve,//已读未读
                'number':number,//未读数量
            }]};  
            localStorage[chatId] = JSON.stringify(localContent);//存储本地; 
            //console.log(JSON.parse(localStorage[chatId]))
            
        }  
       //根据每一条消息的id存储本地 存储未读已读状态
        function statusRead(status,mid,id,reserve){
            var localStatusContent = new Array();
            if(localStatusContent[id]){
                localStatusContent = JSON.parse(localStorage[id]); 

            }
            localStatusContent[localStatusContent.length]={
                'status':status,
                'mid':mid,
                'id':id,
                'reserve':reserve
            };
            localStorage[id] = JSON.stringify(localStatusContent);
            //console.log(localStorage[id])
        }

项目需求只有表情和图片,文件视频,音频其实都是属于文件的一种,若有帮助请赞赏一下吧。

微信图片_20180525164006.jpg


微信图片_20180525164014.png



 
1
评论

web端的那个表情文件在哪里?[(;] 后面 有一个value 那个value 代表的是 二进制流还是 图片路径 ? 表情 文件 环信_WebIM

环信专业服务 发表了文章 • 4966 次浏览 • 2015-09-23 20:25 • 来自相关话题

使用的是base64编码,在http://webim.easemob.com/jquery-1.11.1.js var emotionPicData 。
使用的是base64编码,在http://webim.easemob.com/jquery-1.11.1.js var emotionPicData 。
0
评论

web如何利用环信做私信功能? @ @功能 环信_WebIM

环信专业服务 发表了文章 • 5723 次浏览 • 2015-09-19 21:33 • 来自相关话题

网页端可以用rest接口做。即发消息时调用环信rest接口。收消息时是用浏览器来轮询服务器。同时服务器和环信服务器之间用聊天记录导出接口定时同步(比如每2分钟一次)。
网页端可以用rest接口做。即发消息时调用环信rest接口。收消息时是用浏览器来轮询服务器。同时服务器和环信服务器之间用聊天记录导出接口定时同步(比如每2分钟一次)。
0
评论

请问Web可以加入群吗? 环信_WebIM

环信专业服务 发表了文章 • 2557 次浏览 • 2015-09-19 14:24 • 来自相关话题

demo没实现,开发者可以调用rest加http://docs.easemob.com/doku.p ... mt%23群组操作。
demo没实现,开发者可以调用rest加http://docs.easemob.com/doku.p ... mt%23群组操作。
0
评论

webIM明文传输,安全性不够吧? webim 环信_WebIM

环信专业服务 发表了文章 • 2880 次浏览 • 2015-09-16 14:52 • 来自相关话题

webim支持https
webim支持https
0
评论

web-im没有黑名单的功能吗?通过rest的话,环信这边加入黑名单之后可以移除用户窗口,可是对面还是有,怎么操作? 黑名单 环信_WebIM

环信专业服务 发表了文章 • 2751 次浏览 • 2015-09-14 11:36 • 来自相关话题

通过rest进行黑名单的操作。在加入黑名单的同时,对面也调用rest把这边加入黑名单。
通过rest进行黑名单的操作。在加入黑名单的同时,对面也调用rest把这边加入黑名单。
0
评论

web 版的是否后续会支持实时音视频聊天? 环信_WebIM

环信专业服务 发表了文章 • 2776 次浏览 • 2015-09-12 07:54 • 来自相关话题

目前没有排在计划里,以后看需求量。
目前没有排在计划里,以后看需求量。
0
评论

web版im聊天只要刷新就需要重新登录么? 环信_WebIM

环信专业服务 发表了文章 • 4032 次浏览 • 2015-09-11 15:46 • 来自相关话题

多页面登录,暂时没有好的方式,可以考虑用token登录看看能不能解决用户的问题,web这块是支持token登录的:
conn.open({
apiUrl : apiURL,
user : user,
pwd : pass,
//连接时提供appkey
appKey : appkey
//accessToken : 'YWMt8bfZfFk5EeSiAzsQ0OXu4QAAAUpoZFOMJ66ic5m2LOZRhYUsRKZWINA06HI'
});
open连接时提供accessToken 而不是密码pwdconn.open的时候多传递参数,resource:随机值,每个tab中随机值不能一样,一样则会把前一个登录的踢出,默认都是webim使用accessToken登录的时候不用传pwd值,每次刷新页面获取一个新的accessToken传进去。 查看全部
多页面登录,暂时没有好的方式,可以考虑用token登录看看能不能解决用户的问题,web这块是支持token登录的:
conn.open({
apiUrl : apiURL,
user : user,
pwd : pass,
//连接时提供appkey
appKey : appkey
//accessToken : 'YWMt8bfZfFk5EeSiAzsQ0OXu4QAAAUpoZFOMJ66ic5m2LOZRhYUsRKZWINA06HI'
});
open连接时提供accessToken 而不是密码pwdconn.open的时候多传递参数,resource:随机值,每个tab中随机值不能一样,一样则会把前一个登录的踢出,默认都是webim使用accessToken登录的时候不用传pwd值,每次刷新页面获取一个新的accessToken传进去。
0
评论

在web端 如何获取消息的messageId ? 环信_WebIM

环信专业服务 发表了文章 • 3189 次浏览 • 2015-09-07 17:58 • 来自相关话题

当接收到消息时,会有回调,可以从message对象里面取到 message.id 。
当接收到消息时,会有回调,可以从message对象里面取到 message.id 。
0
评论

请教个webim问题,报错“同源策略禁止读取位于http://im-api.easemob.com/http-bind/的无程资源”,请问怎么解决? webim 环信_WebIM

环信专业服务 发表了文章 • 3591 次浏览 • 2015-09-06 20:22 • 来自相关话题

设置 apiurl 为https。
设置 apiurl 为https。
1
评论

创建应用的时候的应用名称跟产品名称有什么区别?可以随便填写吗? 环信_WebIM

环信专业服务 发表了文章 • 2872 次浏览 • 2015-09-06 01:22 • 来自相关话题

应用名称: 作为环信体系中的一个app唯一标识,只能是字母,数字或字母数字组合。产品名称: app对应的产品叫什么? 只能是汉字,字母,数字、横线、下划线及其组合。可以,只要符合规则就行了,不过对于产品名称,建议填写真实信息。
应用名称: 作为环信体系中的一个app唯一标识,只能是字母,数字或字母数字组合。产品名称: app对应的产品叫什么? 只能是汉字,字母,数字、横线、下划线及其组合。可以,只要符合规则就行了,不过对于产品名称,建议填写真实信息。
0
评论

进入开发者控制后台,点击应用,看到的应用概况为空? 环信_WebIM

环信专业服务 发表了文章 • 2537 次浏览 • 2015-09-01 16:50 • 来自相关话题

可以退出重新登录,刷新页面试试。如果发现还解决不了,麻烦给在线客服人员提供账号密码,由客服联系相关人员解决。
可以退出重新登录,刷新页面试试。如果发现还解决不了,麻烦给在线客服人员提供账号密码,由客服联系相关人员解决。
0
评论

WEB-IM中在客户端的代码中可以发送透穿消息吗? 环信_WebIM

环信专业服务 发表了文章 • 2784 次浏览 • 2015-09-01 09:24 • 来自相关话题

webIM通过sdk或rest发透传这两种方式都可以,sdk方式的话按着看下透传的消息格式改一下就行了。
webIM通过sdk或rest发透传这两种方式都可以,sdk方式的话按着看下透传的消息格式改一下就行了。
0
评论

webIM如果直接关闭聊天窗口,那当前登录人的状态还是在线? webim 在线 在线状态 环信_WebIM

环信专业服务 发表了文章 • 3671 次浏览 • 2015-08-31 20:15 • 来自相关话题

有个缓冲时间,如果是点击退出,应该是立刻;如果要是直接关闭浏览器,服务器没法立刻知道,需要有个缓冲时间。
有个缓冲时间,如果是点击退出,应该是立刻;如果要是直接关闭浏览器,服务器没法立刻知道,需要有个缓冲时间。
0
评论

1.webim-sdk是依赖jQuery吗?2.webim-sdk可以不依赖 jQuery吗? webim 环信_WebIM

环信专业服务 发表了文章 • 3431 次浏览 • 2015-08-31 08:46 • 来自相关话题

1.webim-SDK 依赖于 jQuery; 2.目前不行,除非jQuery做的完全用js重写。
1.webim-SDK 依赖于 jQuery; 2.目前不行,除非jQuery做的完全用js重写。
0
评论

WEB-IM中在客户端的代码中可以发送透穿消息吗? 环信_WebIM

环信专业服务 发表了文章 • 2611 次浏览 • 2015-08-31 03:31 • 来自相关话题

webIM通过sdk或rest发透传这两种方式都可以。Sdk发透传的话根据透传的消息格式改一下就可以了。
webIM通过sdk或rest发透传这两种方式都可以。Sdk发透传的话根据透传的消息格式改一下就可以了。
0
评论

web端是否可以使用用户自己线上app的用户id和密码登陆? 环信_WebIM

环信专业服务 发表了文章 • 3021 次浏览 • 2015-08-30 21:29 • 来自相关话题

可以,但是需要修改appkey,在index.html里面修改。
可以,但是需要修改appkey,在index.html里面修改。
0
评论

WEBIM 可以支持IE6,IE7,IE8,IE9部署成功了吗?现在可以支持了吗? webim 环信_WebIM

环信专业服务 发表了文章 • 2986 次浏览 • 2015-08-30 18:43 • 来自相关话题

已经支持IE7,8,9。
已经支持IE7,8,9。
0
评论

web-im支持自定义扩展消息吗? 自定义扩展消息 消息扩展 环信_WebIM

环信专业服务 发表了文章 • 3376 次浏览 • 2015-08-30 00:53 • 来自相关话题

支持,webim是开源的,可以看js里面的代码。
支持,webim是开源的,可以看js里面的代码。
0
评论

web im https需要什么特别的证书吗? 环信用的https证书是startcom么?客户https访问证书不安全被拒绝无法连接到环信,嵌入在网页里的,而且是ajax请求,没法点击信任。 环信_WebIM

环信专业服务 发表了文章 • 2851 次浏览 • 2015-08-26 14:01 • 来自相关话题

环信用的是StartCom的证书,用https的方式连接时候在链接地址栏上都会有个额外的标记,IE浏览器上是个锁型图标在地址栏右端,Chrome在左端,点那个图标就能查看证书内容,可以让用户在那边看下证书详情,截图发过来核对一下和环信这边的证书是不是一致的,如果不一致的话有可能是被导向了钓鱼网站,一般情况下连接环信的https页面不应该有证书问题。 查看全部
环信用的是StartCom的证书,用https的方式连接时候在链接地址栏上都会有个额外的标记,IE浏览器上是个锁型图标在地址栏右端,Chrome在左端,点那个图标就能查看证书内容,可以让用户在那边看下证书详情,截图发过来核对一下和环信这边的证书是不是一致的,如果不一致的话有可能是被导向了钓鱼网站,一般情况下连接环信的https页面不应该有证书问题。
0
评论

web端私信为什么不建议用webIM,而是调用rest接口发送,用轮询是否会对后期服务器造成压力呢? webim 环信_WebIM

环信专业服务 发表了文章 • 3767 次浏览 • 2015-08-26 01:50 • 来自相关话题

这种方案最简单。但不会特别的实时。所以适合实时要求不是特别高的私信,如果用webim改,会非常实时。但工作量大。webim很复杂,用的库也多,ui也复杂。环信的webim是开源的,但工程师要把这些代码读懂,也需要些时间的。用轮询是否会对后期服务器造成压力呢 --这个要看系统以后设计容量是多少。 查看全部
这种方案最简单。但不会特别的实时。所以适合实时要求不是特别高的私信,如果用webim改,会非常实时。但工作量大。webim很复杂,用的库也多,ui也复杂。环信的webim是开源的,但工程师要把这些代码读懂,也需要些时间的。用轮询是否会对后期服务器造成压力呢 --这个要看系统以后设计容量是多少。
3
评论

请问:webim使用时,本地数据库和环信数据库的关系? 环信_WebIM

dsideal 发表了文章 • 4402 次浏览 • 2015-07-08 15:32 • 来自相关话题

我公司想使用环信这个聊天工具,有几个问题请环信工程师帮助解答一下,非常感谢
1. 我自己的本地程序数据库如何与环信数据库对接,比如好友,聊天信息,群组信息
2. 我发信息时,WEB-IM 和 IOS 聊天,用DEMO已经实现及时通信,怎么及时同步到我的数据库中?
3. 添加删除好友增量变化,如何及时同步到我的数据库中?

我是不是可以理解成
比如我添加一个好友,我第一步调用自己程序的方法,把这个操作添加到自己的数据库中,同时再调用环信的方法,就保存在了环信的数据库中?
有没有咱们环信的成功案例,给与我一个最好的解决方案?

如果我使用自己的数据
整体的解决方案是不是这样:
1. 我加载好友及群组数据,数据来源是自己的数据库
2. 我发送消息给好友,需要调用环信的发信息接口
3. 我添加好友删除好友等操作,也是保存到自己的数据库,不需要保存到环信的数据库中
也就是说,我只有在发送和接收消息的时候,才用到环信?

一些问题求解答,确实想使用环信,谢谢
  查看全部
我公司想使用环信这个聊天工具,有几个问题请环信工程师帮助解答一下,非常感谢
1. 我自己的本地程序数据库如何与环信数据库对接,比如好友,聊天信息,群组信息
2. 我发信息时,WEB-IM 和 IOS 聊天,用DEMO已经实现及时通信,怎么及时同步到我的数据库中?
3. 添加删除好友增量变化,如何及时同步到我的数据库中?

我是不是可以理解成
比如我添加一个好友,我第一步调用自己程序的方法,把这个操作添加到自己的数据库中,同时再调用环信的方法,就保存在了环信的数据库中?
有没有咱们环信的成功案例,给与我一个最好的解决方案?

如果我使用自己的数据
整体的解决方案是不是这样:
1. 我加载好友及群组数据,数据来源是自己的数据库
2. 我发送消息给好友,需要调用环信的发信息接口
3. 我添加好友删除好友等操作,也是保存到自己的数据库,不需要保存到环信的数据库中
也就是说,我只有在发送和接收消息的时候,才用到环信?

一些问题求解答,确实想使用环信,谢谢