自去年LeanCloud发布实时通信(IM)服务之后,基于用户反馈和工程师对需求的消化和对业务的提炼,上周正式发布了「实时通信2.0」。设计理念依然是「灵活、解耦、可组合、可定制」,具体可以参考《实时通信开发指南》,了解LeanCloud实时通信的基本概念和模型。
下载和安装
可以到LeanCloud官方下载点下载LeanCloudIMSDKv2版本。将下载到的jar包加入工程即可。
一对一的文本聊天
我们先从最简单的环节入手,看看怎么用LeanCloudIMSDKv2实现一对一文本聊天。
初始化
和LeanCloud其他服务一样,实时聊天服务的初始化也是在Application的onCreate方法中进行的:
复制代码代码如下:publicclassMyApplicationextendsApplication{publicvoidonCreate(){...AVOSCloud.initialize(this,"{{appId}}","{{appKey}}");...}}
复制代码代码如下:
建立对话
假定我们要跟「Bob」这个用户进行聊天,我们先创建一个对话,代码如下:
如何查询「对话」
如你所见,我们创建一个对话的时候,指定了成员(Tom和Bob)和一个额外的属性({type:0})。这些数据保存到云端后,你在控制台->存储->数据里面会看到,_Conversation表中增加了一条记录,新记录的m属性值为["Tom","Bob"],attr属性值为{"type":0}。如你所料,m属性就是对应着成员列表,attr属性就是用户增加的额外属性值(以对象的形式存储)。
与AVObject的检索方法一样,要检索这样的对话,我们需要通过imClient.getQuery()得到一个AVIMConversationQuery实例,然后调用conversationQuery.withMembers()来限定成员列表,调用conversationQuery.whereEqualTo()来限定额外的attr属性。按照AVQuery的惯例,限定额外的type条件的时候需要指定的属性名是attr.type。发送消息
建立好对话之后,要发送消息是很简单的:
复制代码代码如下:AVIMMessagemessage=newAVIMMessage();message.setContent("hello");conversation.sendMessage(message,newAVIMConversationCallback(){@Overridepublicvoiddone(AVExceptione){if(null!=e){//出错了。。。}else{}}});
好了,这样一条消息就发送过去了。但是问题来了,对于「Bob」而言,他怎么才能收到别人发给他的消息呢?
消息接收
在Bob这一端,要能接收到消息,需要如下几步:
2,实现自己的AVIMMessageHandler,响应新消息到达通知,主要是如下函数:
复制代码代码如下:publicvoidonMessage(AVIMMessagemessage,AVIMConversationconversation,AVIMClientclient);
对于Tom发过来的消息,要显示出来,我们只需实现onMessage即可,示例代码如下:
复制代码代码如下:classCustomMessageHandlerextendsAVIMMessageHandler{@OverridepublicvoidonMessage(AVIMMessagemessage,AVIMConversationconversation,AVIMClientclient){//新消息到来了。在这里增加你自己的处理代码。...}}AVIMMessageManager.registerDefaultMessageHandler(newCustomMessageHandler());
几个主要的回调接口
从上面的例子中可以看到,要接收到别人给你发送的消息,需要实现自己的AVIMMessageHandler类。从v2版开始,LeanCloudIMSDK大量采用回调来反馈操作结果,但是对于一些被动的消息通知,则还是采用接口来实现的,包括:
当前网络出现变化对话中有新的消息对话中有新成员加入对话中有成员离开被邀请加入某对话被踢出对话LeanCloudIMSDK内部使用了三种接口来响应这些事件。
网络事件响应接口(AVIMClientEventHandler)
主要用来处理网络变化事件,主要函数为:
复制代码代码如下:/***实现本方法以处理网络断开事件**@paramclient*@since3.0*/publicabstractvoidonConnectionPaused(AVIMClientclient);/***实现本方法以处理网络恢复事件**@since3.0*@paramclient*/publicabstractvoidonConnectionResume(AVIMClientclient);
在网络中断的情况下,所有的消息收发和对话操作都会出现问题。
通过AVIMClient.setClientEventHandler(AVIMClientEventHandlerhandler)可以设定全局的ClientEventHandler。
对话成员变化响应接口(AVIMConversationEventHandler)
主要用来处理对话中成员变化的事件,主要函数为:
通过AVIMMessageManager.setConversationEventHandler(AVIMConversationEventHandlerhandler)可以设置全局的ConversationEventHandler。
消息响应接口(MessageHandler)
主要用来处理新消息到达事件,主要的函数为:
复制代码代码如下://收到新的消息@OverridepublicabstractvoidonMessage(AVIMMessagemessage,AVIMConversationconversation);//自己发送的消息已经被对方接收@OverridepublicabstractvoidonMessageReceipt(AVIMMessagemessage,AVIMConversationconversation,AVIMClientclient);
通过AVIMMessageManager.registerDefaultMessageHandler(MessageHandlerhandler)可以设置全局的MessageHandler。
我们实现这三类接口,就可以处理所有的通知消息了(注意:LeanCloudIMSDK内部实现了一个空的AVIMMessageHandler,你可以从这里派生出进行实际处理的handler)。
支持富媒体的聊天消息
上面的代码演示了如何发送简单文本信息,但是现在的交互方式已经越来越多样化,图片、语音、视频已是非常普遍的消息类型。v2版的LeanCloudIMSDK已经可以很好地支持这些富媒体消息,具体说明如下:
基类:AVIMTypedMessage
复制代码代码如下://SDK定义的消息类型,LeanCloudSDK自身使用的类型是负数,所有正数留给开发者自定义扩展类型使用,0作为「没有类型」被保留起来。enumAVIMReservedMessageType{UnsupportedMessageType(0),TextMessageType(-1),ImageMessageType(-2),AudioMessageType(-3),VideoMessageType(-4),LocationMessageType(-5),FileMessageType(-6);};publicabstractclassAVIMTypedMessageextendsAVIMMessage{publicAVIMTypedMessage();publicintgetMessageType();@OverridepublicfinalStringgetContent();@OverridepublicfinalvoidsetContent(Stringcontent);}
文本消息(AVIMTextMessage)
复制代码代码如下:@AVIMMessageType(type=-1)publicclassAVIMTextMessageextendsAVIMTypedMessage{publicStringgetText();
publicvoidsetText(Stringtext);
publicMap
publicvoidsetAttrs(Map
要发送文本消息,示例代码为:
复制代码代码如下:AVIMTextMessagemessage=newAVIMTextMessage();message.setText("hello");conversation.sendMessage(message,newAVIMConversationCallback(){@Overridepublicvoiddone(AVExceptione){if(null!=e){//出错了。。。}else{}}});
图像消息(AVIMImageMessage)
复制代码代码如下:publicclassAVIMImageMessageextendsAVIMFileMessage{publicAVIMImageMessage();publicAVIMImageMessage(StringlocalPath)throwsFileNotFoundException,IOException;publicAVIMImageMessage(FilelocalFile)throwsFileNotFoundException,IOException;publicAVIMImageMessage(AVFilefile);/***获取文件的metaData**@return*/@OverridepublicMap
接收到这样消息之后,开发者可以获取到若干图片元数据(width,height,图片size,图片format)和一个包含图片数据的AVFile对象。
音频消息(AVIMAudioMessage)
复制代码代码如下:publicclassAVIMAudioMessageextendsAVIMFileMessage{publicAVIMAudioMessage();publicAVIMAudioMessage(StringlocalPath)throwsFileNotFoundException,IOException;publicAVIMAudioMessage(FilelocalFile)throwsFileNotFoundException,IOException;publicAVIMAudioMessage(AVFilefile);/***获取文件的metaData**@return*/@OverridepublicMap
/***获取音频的时长**@return*/publicdoublegetDuration();}
发送音频消息的示例代码为:
复制代码代码如下:StringlocalAudioPath;try{AVIMAudioMessagemessage=newAVIMAudioMessage(localAudioPath);conversation.sendMessage(message,newAVIMConversationCallback(){@Overridepublicvoiddone(AVExceptione){if(null!=e){//出错了。。。}else{}}});}catch(Exceptionex){}
接收到这样消息之后,开发者可以获取到若干音频元数据(时长duration、音频size,音频format)和一个包含音频数据的AVFile对象。
视频消息(AVIMVideoMessage)
复制代码代码如下:publicclassAVIMVideoMessageextendsAVIMFileMessage{publicAVIMVideoMessage();
publicAVIMVideoMessage(StringlocalPath)throwsFileNotFoundException,IOException;publicAVIMVideoMessage(FilelocalFile)throwsFileNotFoundException,IOException;publicAVIMVideoMessage(AVFilefile);
/***获取文件的metaData**@return*/@OverridepublicMap
/***获取时长**@return*/publicdoublegetDuration();}
发送视频消息的示例代码为:
复制代码代码如下:StringlocalVideoPath;try{AVIMVideoMessagemessage=newAVIMVideoMessage(localVideoPath);conversation.sendMessage(message,newAVIMConversationCallback(){@Overridepublicvoiddone(AVExceptione){if(null!=e){//出错了。。。}else{}}});}catch(Exceptionex){}
接收到这样消息之后,开发者可以获取到若干视频元数据(时长duration、视频size,视频format)和一个包含视频数据的AVFile对象。
地理位置消息(AVIMLocationMessage)
复制代码代码如下:publicclassAVIMLocationMessageextendsAVIMTypedMessage{publicStringgetText();publicvoidsetText(Stringtext);
publicMap
publicAVGeoPointgetLocation();publicvoidsetLocation(AVGeoPointlocation);}
要发送位置消息的示例代码为:
复制代码代码如下:AVIMLocationMessagemessage=newAVIMLocationMessage();message.setText("快点过来!");message.setLocation(newAVGeoPoint(15.9,56.4));conversation.sendMessage(message,newAVIMConversationCallback(){@Overridepublicvoiddone(AVExceptione){if(null!=e){//出错了。。。}else{}}});
接收到这样的消息之后,开发者可以获取到具体的地理位置数据。
如何接收富媒体消息
新版LeanCloudIMSDK内部封装了对富媒体消息的支持,所有富媒体消息都是从AVIMTypedMessage派生出来的。发送的时候可以直接调用conversation.sendMessage函数。在接收端,我们也专门增加了一类回调接口:
复制代码代码如下:publicclassAVIMTypedMessageHandler
LeanCloudIMSDK内部消息分发的逻辑是这样的:对于收到的任一新消息,SDK内部都会先解析消息的类型,根据类型找到开发者为这一类型注册的处理handler,然后逐一调用这些handler的onMessage函数。如果没有找到专门处理这一类型消息的handler,就会转交给defaultHandler处理。
这样一来,在开发者为TypedMessage(及其子类)指定了专门的handler,也指定了全局的defaultHandler了的时候,如果发送端发送的是通用的AVIMMessage消息,那么接受端就是AVIMMessageManager.registerDefaultMessageHandler()中指定的handler被调用;如果发送的是AVIMTypedMessage(及其子类)的消息,那么接受端就是AVIMMessageManager.registerMessageHandler()中指定的handler被调用。
接收端对于富媒体消息的通知处理代码片段如下:
复制代码代码如下:classMsgHandlerextendsAVIMTypedMessageHandler{@OverridepublicvoidonMessage(AVIMTypedMessagemessage,AVIMConversationconversation,AVIMClientclient){getInstance().onMessage(conversation,message);}@OverridepublicvoidonMessageReceipt(AVIMTypedMessagemessage,AVIMConversationconversation,AVIMClientclient){getInstance().onMessageDelivered(message);}}MsgHandlermsgHandler=newMsgHandler();AVIMMessageManager.registerMessageHandler(AVIMTypedMessage.class,msgHandler);
如何扩展自己的富媒体消息
继承于AVIMTypedMessage,开发者也可以扩展自己的富媒体消息,这一部分属于高阶内容,大家有兴趣可以参看这篇文档,这里不再赘述。
群组聊天
与前面的单聊类似,群组聊天也需要先建立一个对话(AVIMConversation),然后发送、接收新的消息。
创建群组
和单聊类似,建立一个多人聊天的群组也是很简单的。例如:
复制代码代码如下:Map
成功之后,我们就可以进入聊天界面了。
往群组发送消息
发送消息非常简单,与前面单聊的场景一样。
我们会注意到,AVIMConversation还有一个发送消息的方法:
复制代码代码如下:publicvoidsendMessage(finalAVIMMessagemessage,finalintmessageFlag,finalAVIMConversationCallbackcallback)
而这里flag的定义有如下三种类型:
暂态消息(AVIMConversation.TRANSIENT_MESSAGE_FLAG)。这种消息不会被自动保存(以后在历史消息中无法找到它),也不支持延迟接收,离线用户更不会收到推送通知,所以适合用来做控制协议。譬如聊天过程中「某某正在输入中...」这样的状态信息,就适合通过暂态消息来发送。普通消息(AVIMConversation.NONTRANSIENT_MESSAGE_FLAG)。这种消息就是我们最常用的消息类型,在LeanCloud云端会自动保存起来,支持延迟接收和离线推送,以后在历史消息中可以找到它。待回执消息(AVIMConversation.RECEIPT_MESSAGE_FLAG)。这也是一种普通消息,只是消息被对方收到之后LeanCloud服务端会发送一个回执通知给发送方(这就是AVIMMessageHandler中publicvoidonMessageReceipt(AVIMMessagemessage,AVIMConversationconversation,AVIMClientclient)函数被调用的时机)。接收群组消息
接收一个群组的消息,与接收单聊的消息也是一样的。
成员管理
在查询到聊天室成员之后,可以让用户邀请一些自己的朋友加入,作为管理员也可以剔除一些「可怕」的成员。加入新成员的API如下:
复制代码代码如下:List
邀请成功以后,通知的流程是这样的:
操作者(管理员)被邀请者其他人1,发出请求addMembers2,收到onInvited通知3,收到onMemberJoined通知收到onMemberJoined通知收到onMemberJoined通知相应地,踢人时的调用API是:
复制代码代码如下:List
踢人的通知流程如下:
操作者(管理员)被踢者其他人1,发出请求kickMembers2,收到onKicked通知3,收到onMemberLeft通知收到onMemberLeft通知注意!如果邀请、踢人操作发生的时候,被邀请者/被踢者当前不在线,那么通知消息并不会被离线缓存,所以他们再上线的时候将不会收到通知。获取历史消息
LeanMessage会将非暂态消息自动保存在云端,之后开发者可以通过AVIMConversation来获取该对话的所有历史消息。获取历史消息的API如下:
复制代码代码如下:StringoldestMsgId;longoldestMsgTimestamp;conversation.queryMessages(oldestMsgId,oldestMsgTimestamp,limit,newAVIMHistoryMessageCallback(){@Overridepublicvoiddone(List
启用离线消息推送(仅对iOS平台用户有效)
不管是单聊还是群聊,当用户A发出消息后,如果目标对话中有部分用户当前不在线,LeanCloud云端可以提供离线推送的方式来提醒用户。这一功能默认是关闭的,你可以在LeanCloud应用控制台中开启它。开启方法如下:
群组消息免打扰(仅对iOS平台用户有效)
不管是单聊还是群聊,对于发往普通的Conversation的普通消息,如果接收方当前不在线,LeanCloud云端支持通过PushNotification的方式进行提醒。一般情况下这都是很好的,但是如果某个群组特别活跃,那离线用户就会收到过多的推送,会形成不小的干扰。对此LeanCloudIM服务也允许单个用户来关闭/打开某个对话的离线推送功能。调用API如下:
复制代码代码如下:if(open){[_conversationmuteWithCallback:^(BOOLsucceeded,NSError*error){...}];}else{[_conversationunmuteWithCallback:^(BOOLsucceeded,NSError*error){...}];}
搜索群组
不管是单聊,还是群聊,在LeanCloudIMSDK里面都是对话(Conversation)。我们给对话设置了如下几种属性:
复制代码代码如下:List
AVIMConversationQuery中设置条件的方法与AVQuery类似,具体可以参看其头文件。这里要强调的一点是,对于自定义属性的约束条件,属性名一定要以attr开头。
开放聊天室
开放聊天室(也叫暂态对话)可以用于很多地方,譬如弹幕、直播等等。在LeanCloudIMSDK中,开放聊天室是一类特殊的群组,它也支持创建、加入/踢出成员等操作,消息记录会被保存并可供获取;与普通群组不一样的地方具体体现为:
和普通的群组类似,建立一个开放聊天室也是很简单的,只是在AVIMClient.createConversation(conversationMembers,name,attributes,isTransient,callback)中我们需要传入isTransient=true选项。例如:
复制代码代码如下:Map
加入成功之后,我们就可以进入聊天界面了。开放聊天室的其他操作,都与普通群组操作一样。
查询在线人数
通过AVIMConversation.getMemberCount()方法可以实时查询开放聊天室的在线人数。示例代码如下:
复制代码代码如下:conversation.getMemberCount(newAVIMConversationMemberCountCallback(){@Overridepublicvoiddone(IntegermemberCount,AVExceptione){if(null!=e){//出错了:(}else{//成功,此时memberCount的数值就是实时在线人数}}});
签名和安全
设定了signatureFactory之后,对于需要鉴权的操作,LeanCloudIMSDK与服务器端通讯的时候都会带上应用自己生成的Signature信息,LeanCloud云端会使用app的masterKey来验证信息的有效性,保证聊天渠道的安全。
对于SignatureFactory接口,我们只需要实现这两个函数即可:
复制代码代码如下:publicclassSignature{publicList
其中四个属性分别是:
LeanCloudIMSDK专注做好底层的通讯服务,有更多可以定制化的地方,譬如说:
账户系统和IM系统是分离的;消息变成离线推送的时候,推送内容开发者是可以定制的;通过webhook,开发者可以对消息进行更多处理;聊天过程中通过消息鉴权机制,开发者可以有更多控制;因为缺少UI组件,实事求是地讲在新用户接入成本可能稍高,但是在业务规模扩大、产品需求变多之后,相信大家会越来越喜欢LeanCloud这种自由灵活的使用体验,以及稳定迅捷的服务质量。