[置顶] 融云RongIMKit即时通讯开发(让我们聊起来)
1.前言
现在APP功能越来越多,聊天成为很重要的一个功能,现在市面上流行的即时通讯SDK基本上只有融云和环信,而近年来融云在各个方面都有超过环信的趋势,所以在项目中用到融云的地方越来越多.那我们是不是也要介绍一下融云的实现了呢.
RongIM即时通讯分为两个部分.. 一个是KIT一个是LIB .. Kit属于封装程度更高的SDK.. 会话界面列表已经全部实现.. 调用方便但是定制性低… 适合初中级程序员(比如我)以及中小型公司高效快速的开发.. 而Lib属于封装程度很低的SDK.. 列表界面等都需要自己去再实现.. 适合中高级程序员(我的爸爸们)以及中大型公司去精雕细琢.. 本文主要针对安卓RongIMKit的实现作介绍… 如果爸爸们想了解Lib的实现那我只能说…
(噗通) 儿子给您跪下了… 您还收儿子么…
2.基本实现
前面纯属瞎杰宝扯淡… 下面进入正题..
RongIMKit实现其实很简单 , Kit主要分为三个模块, 分别是会话列表(io.rong.imkit.fragment.ConversationListFragment)、聚合会话列表(io.rong.imkit.fragment.SubConversationListFragment)和会话界面(io.rong.fast.activity.ConversationActivity) .. 会话列表就是消息界面.. 融云的特点就是只维护聊天列表.. 但他不管好友关系… 这就很尴尬了..(怎么处理后文会详述) 所以好友列表融云爸爸是不管的.. 我们只需要将这三个界面集成然后用融云开启界面的代码
//启动会话界面
if (RongIM.getInstance() != null)
RongIM.getInstance().startPrivateChat(this, "26594", "title");
//启动会话列表界面
if (RongIM.getInstance() != null)
RongIM.getInstance().startConversationList(this);
//启动聚合会话列表界面
if (RongIM.getInstance() != null)
RongIM.getInstance().startSubConversationList(this, Conversation.ConversationType.GROUP);
去进入界面即可.. 快速集成的流程见
好了本文到此结束了… 你们去看文档吧╮(╯▽╰)╭ … 感谢融云官方文档… 再见… 哎哎哎你们别打疼疼疼疼疼…
我其实想说的是… 需要注意的是..
2.1.意图选择器
官方让我们在manifest里配置如
<activity
android:name="io.rong.fast.activity.ConversationListActivity"
android:screenOrientation="portrait"
android:windowSoftInputMode="stateHidden|adjustResize">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data
android:host="io.rong.fast"
android:pathPrefix="/conversationlist"
android:scheme="rong" />
</intent-filter>
</activity>
的意图选择器.. 实际上有两个作用… 一是在收到推送时.. 点击通知栏可以进入到配置意图选择器的界面..(比如收到一个人消息.. 点击通知栏便可进入到单聊界面.. 接受到多个人的多条消息的话.. 点击就可以进入会话列表界面了) 而且这个界面中可能不仅有融云的fragment ,如本人APP的界面
那么在进入这个界面的时候一定要在这个界面manifast中配置完意图选择器后调用
RongIM.getInstance().startConversationList(this);
来进入界面.. 不然界面的初始化会不成功…
而列表本身其实只是个fragment.. 只要按
<fragment
android:id="@+id/conversationlist"
android:name="io.rong.imkit.fragment.ConversationListFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
来引用 或者动态引用都是可以的.. 详见
2.2.聚合会话列表与会话列表的区别
聚合会话列表(SubConversationList) 实际上英文也有亚会话列表的意思.. 实际上就是会话列表往下的一个子列表.. 比如QQ中..有群列表 系统消息列表等… 在会话列表点击后可以进入的另一个列表便是聚合会话列表.. 在融云中群聊消息都会在聚合会话列表中展示… 还有系统消息也会展示.. 至于推不推送系统消息是服务器处理的.. 如果服务器不推送系统消息则没有… 所以这个界面不是必须集成的.. 如果你们又没有群又没有系统消息.. 都是单聊的话.. 这个界面倒也可以不实现…
至此融云的界面基本就算搭建完成了… 如果你觉得这就完了? 图样=.=
3.融云实现细节处理
前文也提到了融云爸爸是不维护好友关系的… 这就很踏马尴尬了… 你不知道我们是好友怎么聊起来呢… 陌生人也能瞎聊么? 没有用户信息你怎么把我的头像名字贴上去呢… 这些问题就比较多了.. 需要从上古时期说起.. 从前有座山.. 山里有座庙… 庙里住着… 哎哎哎别打别打疼疼疼疼疼….
3.1.内容提供者
融云的头像和名字是需要用户使用内容提供者自己提供给融云本地使用.. 注意是本地.. 也就是说图片名字还是在本地只是帮你展示了一下.. 你需要在合适的时机调用
RongIM.setUserInfoProvider(new RongIM.UserInfoProvider() {
@Override
public UserInfo getUserInfo(String s) {
for (int i = 0; i < friendList.size(); i++) {
return findUserInfo();
}
}, true); // 提供一群人信息
RongIM.getInstance().refreshUserInfoCache(new UserInfo(friendList.get(i).userId + "", friendList.get(i).nickname + "", Uri.parse(friendList.get(i).avatar))); // 提供一个人信息
而在find方法中去找到用户信息传给userinfo .. 实例化后返回.. 如…
for (int i = 0; i < friendList.size(); i++) {
if ((friendList.get(i).userId + "").equals(s)) {
return new UserInfo(friendList.get(i).userId + "",
friendList.get(i).nickname + "", Uri.parse(friendList.get(i).avatar));
}
}
将信息查找后提供出去.. 这样融云才能根据ID正确匹配显示头像和昵称,需要注意的是自己的头像昵称也要提供… 不然自己是默认头像的… 所以需要用refreshUserInfoCache把自己一人的信息提供上去,refreshUserInfoCache同时也可以刷新内容提供者中某一条ID的信息… 实现单个改动
群组同样有群组提供者
RongIM.setGroupInfoProvider(new RongIM.GroupInfoProvider() {
@Override
public Group getGroupInfo(String s) {
int result = 0;
for (int i = 0; i < groupList.size(); i++) {
if (s.equals(groupList.get(i).id + ""))
result = i;
}
return new Group(groupList.get(result).id + "",
groupList.get(result).name, Uri.parse(groupList.get(result).picture));
}
}, true); //提供一群群组信息
RongIM.getInstance().refreshGroupInfoCache(new Group(groupList.get(i).id + "", groupList.get(i).name, Uri.parse(groupList.get(i).picture))); // 添加一个群组信息
这样你就可以看到别人的头像了..(虽然别人的头像还要我提供有点怪怪的= =;)
3.2.同步列表与好友关系一致
说到底… 我们还是一直在和融云不同步好友关系做斗争.. 因为融云不保存我们的好友关系.. 所以我们在使用融云的时候要想办法同步好友关系.. 你不能在别人把你删了拉黑了或者把你踢出群了… 那个人依然留存在消息列表中.. 而且因为融云也不知道你拉黑了… 所以聊天还能成功… 这就很踏马尴尬了..
所以针对这种情况…在自己删除对方好友关系时或者退群时.. 要调用
RongIM.getInstance().removeConversation(Conversation.ConversationType.PRIVATE, userId);//个人
RongIM.getInstance().removeConversation(Conversation.ConversationType.GROUP, targetGroupId);群组
去移除掉会话列表中存在的聊天关系..
而被删除的一方则需要通过推送去实时同步..(比如极光推送自定义消息… 把删除的ID传过来..)
而每次上线时.. 可以通过
RongIM.getInstance().getConversationList(Conversation.ConversationType.PRIVATE)//个人
RongIM.getInstance().getConversationList(Conversation.ConversationType.GROUP)//群组
去获取到会话列表中存在的个人和群组…然后获取这个list的Conversation对象.. 调用getTargetId()获取到ID… 再和自己从server上获取的好友列表和群组ID列表作对比去删除…
而这里需要注意的是,RongIM.getInstance().getConversationList(type)这个方法虽然可以get出来会话列表中的信息.. 但是这些消息不一定是属于自己本身这个账号的..
举个栗子..
你用ID为1的账号聊天.. 在会话列表中留下了聊天记录的缓存信息.. 但是你换ID为2的账号登录.. 调用RongIM.getInstance().getConversationList(type)方法却依然可以获取到1的会话列表里的记录.. 如果我们单纯拿list和自己的好友关系做对比.. 可能在2上就把1的会话记录给删了.. 再回换1登录就发现空空的…
所以解决方法就是.. 删的时候你得把conversation.getSenderUserId()与自己的ID做一个判断.. 不能把其他账号中的信息删了…
这样就可以保证列表和好友关系保持一致了…
4.最后介绍一些常用的API或者方法
4.1.撤回消息
撤回消息算是聊天中常用的一个功能.. 但是默认是关闭的… 需要在SDK中的res- values - rc_config.xml中配置.. 里面有注释自己找找就好改成true…=.=
4.2. 消息静音
这个功能往往也很常用..
调用的是
RongIM.getInstance().setConversationNotificationStatus(
Conversation.ConversationType.GROUP,//群聊或单聊
id,//群ID或单聊ID
Conversation.ConversationNotificationStatus.DO_NOT_DISTURB,//静音..打开改为NOTIFY即可
new RongIMClient.ResultCallback<Conversation.ConversationNotificationStatus>() { //设置成功后的回调
@Override
public void onSuccess(Conversation.ConversationNotificationStatus conversationNotificationStatus) {
}
@Override
public void onError(RongIMClient.ErrorCode errorCode) {
}
}
);
这样就可以实现免打扰了
4.3. 接收推送
官方描述的是…
2.6.0 之后的版本,我们的通知显示机制有所变化,需要您自定义一个继承 PushMessageReceiver 的广播接收器(必须实现,否则可能会导致点击后台通知没有反应,或者收不到推送通知等问题),具体可参考文档。
阿西吧… 哪个软件别人发信息来了你还不给推一下… 非要开发者自己实现广播接受者不是有病是什么= = ;
总之就是自己写个广播接受者然后在manifest里配置一下就行了.. 详情见
如何使用推送
这里需要注意的是一定要集成华为和小米推送.. 华为和小米会杀后台.. 如果不集成小米华为.. 你的推送活不过五分钟.. 至于集成流程在上面的官方文档里已经很详细.. 小米推送已经比文档里的新了.. 如果你要赶潮流使用新版的小米推送则需要按小米官网的流程去设置manifest.. 而且不知道融云能不能兼容.. 所以最好是拿融云官方demo嗨豹里的小米push.jar来集成就好了..
而华为push的坑则是与百度地图的不兼容.. 因为华为push里是集成了百度地图的定位功能的SDK实现了地理上报功能.. 如果你的项目中本身已经集成了百度地图的话.. 这样在编译的时候就会炸你个sakalaka.. 解决方法叫你的产品经理别用百度地图换高德=.=.. 那是不可能的..
实际上百度一下就能在
华为推送jar包冲突
这里找到答案… 你可以先用解压工具打开jar包.. 然后把百度地图相关的jar包删除掉.. 然后进入assert目录下把图片都删了.. 其实图片不删都没问题的.. 只是会增大你APP的大小… 所以最好删除掉..
这样推送基本就没问题了
4.4.重连
因为融云即时通讯需要保持连接… 如果APP长时间在后台的话.. 就会… 哎哎哎… 你醒醒啊云云.. 你不要死… 所以在聊天列表界面的生命周期resume()中建议进行重连..
@Override
public void onResume() {
if (RongIM.getInstance().getCurrentConnectionStatus() != RongIMClient.ConnectionStatusListener.ConnectionStatus.CONNECTED ) { //如果不是连接状态则重连
reconnect(SharedPrefUtil.getString(getActivity(),Constants.KEY_RONGCLOUD_TOKEN,""));
}
super.onResume();
}
/**
* 重连
*
* @param token
*/
private void reconnect(String token) {
if (getActivity().getApplicationInfo().packageName.equals(App.getCurProcessName(getActivity().getApplicationContext()))) {
RongIM.connect(token, new RongIMClient.ConnectCallback() {
@Override
public void onTokenIncorrect() {
}
@Override
public void onSuccess(String s) {
}
@Override
public void onError(RongIMClient.ErrorCode errorCode) {
}
});
}
}
这样才能保证每次进入到列表中都能正常运作
4.5.一个小技巧—利用融云实现踢人下线
因为融云本身聊天只允许一人在线.. 不允许同一ID多端同时在线.. 所以会出现踢下线的情况.. 我们可以利用这个监听.. 实现我们整个APP的踢下线功能…
监听链接状态
RongIM.setConnectionStatusListener(new RongIMClient.ConnectionStatusListener() {
@Override
public void onChanged(ConnectionStatus connectionStatus) {
if (connectionStatus == ConnectionStatus.KICKED_OFFLINE_BY_OTHER_CLIENT) {
EventBus.getDefault().post(new ExitEvent()); // 利用eventbus结束掉所有界面
runOnUiThread(new Runnable() {
@Override
public void run() {
ToastUtil.showToast(getApplicationContext(),"你的账号在别处登录,您被迫下线");
}
});
startActivity(new Intent(getApplicationContext(), DirectlyLoginActivity.class)); //重新回到登录界面
}
}
});
这是我的实现方式.. 当然可以有更好的实现方式.. 如果你有…
(噗通)爸爸儿子给您跪下了… 您私信偷偷教教儿子我呗…
4.6.优化列表长按dialog
因为dialog融云默认用的是原生的效果可能跟app的风格不符.. 所以我们有可能需要去自定义dialog.. 那怎么去干掉原生的丑陋dialog呢.. 首先你得
在RongIM.setConversationListBehaviorListener() RongIM.setConversationBehaviorListener() 把长按操作改为true.. 弹自己的dialog.. 然后对点击事件调用对应的api.. 我把我用到的API都说一下吧..
会话界面消息长按 RongIM.setConversationBehaviorListener() onMessageLongClick
其中message是onMessageLongClick方法中传出来的一个参数.. 你可以把它传进dialog构造方法中..
复制消息
ClipboardManager clipboard = (ClipboardManager) view.getContext().getSystemService(Context.CLIPBOARD_SERVICE);
clipboard.setText(((TextMessage) message.getContent()).getContent());
删除消息
int[] i = new int[] {message.getMessageId()};
RongIM.getInstance().deleteMessages(i);
撤回消息(注意这里直接调用撤回API是不受之前时间的限制的.. )
RongIM.getInstance().recallMessage(message);
然后需要处理的是.. 如果message.getSenderUserId()不是本人ID或超过一定时间则不让撤回显示, 如果(message.getContent() instanceof TextMessage)不为true 也就是说消息不是文本.. 是语音或者图片的话.. 则不让复制显示..
if(!(message.getContent() instanceof TextMessage)) {
RLMessageCopy.setVisibility(View.GONE);
}
if (!message.getSenderUserId() .equals(SharedPrefUtil.getString(getContext(), Constants.USER_ID,"")) || System.currentTimeMillis() -message.getSentTime() > 120000) {
RLMessageRecall.setVisibility(View.GONE);
}
会话列表界面消息长按 RongIM.setConversationListBehaviorListener() onConversationLongClick
其中uiConversation是onConversationLongClick 里传出来的参数.. 依然可以用构造方法传进dialog中..
置顶消息
RongIM.getInstance().setConversationToTop(uiConversation.getConversationType(),uiConversation.getConversationTargetId(),true);
删除消息
RongIM.getInstance().removeConversation(uiConversation.getConversationType(),uiConversation.getConversationTargetId());
这样你就可以对自己的dialog动画位置风格为所欲为做爱做的事了.. 嘿嘿嘿…
5.总结
至此.. 我已经将我的毕生所学… 啊不.. 这几天的心得.. 分享给大家了.. 融云的使用其实不难.. 主要是很多细节需要处理… 每种情况都需要去将好友列表和消息列表进行同步.. 如果你第一次做融云.. 可把我上面的方法仔细看看.. 在每次出现要同步时都先封装一下工具类复用.. 不然的话后期再回头改就很辛苦了..
如果大家对我的文章有什么不满意的地方欢迎给我私信… 反正我也不会理你们的… 哼╭(╯^╰)╮