JChat 适配 SDK v3.1.0
一、SDK v3.1.0 主要变动
1. 消息同步功能
1.1 漫游消息
漫游消息不是必须的,开发者若需要APP实现漫游消息,需要在初始化SDK时调用新的初始化方法;
开启漫游:+ (void)setupJMessage:appKey:channel:apsForProduction:category:messageRoaming:
代理方法:- (void)onSyncRoamingMessageConversation:
1.2 离线消息
实现离线消息的代理方法,这个是必须的
代理方法:- (void)onSyncOfflineMessageConversation:offlineMessages:
2. 用户信息自动更新功能
这个功能是体现在,如果对方修改了用户信息(如:头像)之后,当对方再次发送消息时,SDK 内部会自动更新对方的用户信息;
这个更新不是立马就体现,网络慢时可能会要一小会;APP上层需要做的就是刷新一下就ok
3. 群组@功能
这个功能大家查看下相关API文档就应该知道用了,[文档链接](https://docs-test.jiguang.cn/jmessage/client/im_sdk_ios/)
4. 群消息屏蔽功能
这个功能大家查看下相关API文档就应该知道用了,[文档链接](https://docs-test.jiguang.cn/jmessage/client/im_sdk_ios/)
二、JChat 需要调整的地方
下面按 【启动 --> 会话列表界面 --> 聊天界面】 这个顺序进行相关调整说明。
1. SDK 启动 AppDelegate.m
1.1 设置漫游(非必须)
将 AppDelegate.m 里面初始化SDK的方法替换成新的
[JMessage setupJMessage:launchOptions
appKey:@"用户的AppKey"
channel:@"应用的渠道名称"
apsForProduction:NO
category:nil
messageRoaming:YES];
最后一个参数 isRoaming 是否启用消息漫游,默认关闭
2. 会话列表界面
2.1 实现监听 <漫游消息> 代理方法
- (void)onSyncRoamingMessageConversation:(JMSGConversation *)conversation {
// do something
// 一般情况这个漫游消息代理,只有在切换设备登录时才会有
// 当有某个会话有漫游消息时,会触发这个代理方法,UI 层需要根据自己逻辑去刷新会话列表
}
2.2 实现监听 <漫游消息> 代理方法
- (void)onSyncOfflineMessageConversation:(JMSGConversation *)conversation
offlineMessages:(NSArray<__kindof JMSGMessage *> *)offlineMessages {
// do something
// 这个代理方法,一般是在有离线消息前提下,启动、从后台唤起、长连接断开又重连 这些情况下APP时会触发这个方法
// 当某个会话有离线消息时,会触发这个代理方法,UI 层需要根据自己逻辑去刷新会话列表
}
2.3 关于UI层性能方面的建议
在会话列表这个界面,建议各位开发者不要每次监听到有消息下发、群组信息改变等就直接去调用 [allConversations:]这个接口,应该做一定的逻辑处理.
下面是一个简单的例子,可能不是很完美,大家可以自己做更好的优化,不要简单无脑的都直接去调用API,应该做一些相应的缓存:
#pragma mark JMSGMessageDelegate
- (void)onReceiveMessage:(JMSGMessage *)message error:(NSError *)error {
[self getConversationList];
}
- (void)onConversationChanged:(JMSGConversation *)conversation {
[self onSyncReloadConversationListWithConversation:conversation];
}
- (void)onGroupInfoChanged:(JMSGGroup *)group {
[self getConversationList];
}
- (void)onSyncOfflineMessageConversation:(JMSGConversation *)conversation
offlineMessages:(NSArray<__kindof JMSGMessage *> *)offlineMessages {
[self onSyncReloadConversationListWithConversation:conversation];
}
- (void)onSyncRoamingMessageConversation:(JMSGConversation *)conversation {
[self onSyncReloadConversationListWithConversation:conversation];
}
// 添加一个简单的逻辑处理,
//如果代理有传 conversation 就直接插入 _conversationArr 数组中,不去调用API接口获取列表
- (void)onSyncReloadConversationListWithConversation:(JMSGConversation *)conversation {
if(!conversation) {
return ;
}
BOOL isHave = NO;
if (conversation.conversationType == kJMSGConversationTypeSingle) {
JMSGUser *newUser = (JMSGUser *)conversation.target;
for (int i = 0; i < _conversationArr.count; i++) {
JMSGConversation *oldConversation = _conversationArr[i];
if (oldConversation.conversationType == kJMSGConversationTypeSingle) {
JMSGUser *oldUser = (JMSGUser *)oldConversation.target;
if ([newUser.username isEqualToString:oldUser.username] && [newUser.appKey isEqualToString:oldUser.appKey]) {
[_conversationArr replaceObjectAtIndex:i withObject:conversation];
isHave = YES;
break ;
}
}
}
}else{
JMSGGroup *newGroup = (JMSGGroup *)conversation.target;
for (int i = 0; i < _conversationArr.count; i++) {
JMSGConversation *oldConversation = _conversationArr[i];
if (oldConversation.conversationType == kJMSGConversationTypeGroup) {
JMSGGroup *oldGroup = (JMSGGroup *)oldConversation.target;
if ([newGroup.gid isEqualToString:oldGroup.gid]) {
[_conversationArr replaceObjectAtIndex:i withObject:conversation];
isHave = YES;
break ;
}
}
}
}
if (!isHave) {
[_conversationArr insertObject:conversation atIndex:0];
}
_conversationArr = [self sortConversation:_conversationArr];
_unreadCount = _unreadCount + [conversation.unreadCount integerValue];
[self saveBadge:_unreadCount];
[self.chatTableView reloadData];
}
// 获取所有会话的方法,做一个简单的缓存,不要每次都直接去调用 API ,
// 如果已经在获取中就不要再次调用API,当获取成功之后如果有缓存,就再次去获取一次会话列表
- (void)getConversationList {
if (isGetingAllConversation) {
NSLog(@"is loading conversation list");
cacheCount++;
return ;
}
NSLog(@"get allConversation -- start");
isGetingAllConversation = YES;
[self.addBgView setHidden:YES];
[JMSGConversation allConversations:^(id resultObject, NSError *error) {
JCHATMAINTHREAD(^{
isGetingAllConversation = NO;
if (error == nil) {
_conversationArr = [self sortConversation:resultObject];
_unreadCount = 0;
for (NSInteger i=0; i < [_conversationArr count]; i++) {
JMSGConversation *conversation = [_conversationArr objectAtIndex:i];
_unreadCount = _unreadCount + [conversation.unreadCount integerValue];
}
[self saveBadge:_unreadCount];
} else {
_conversationArr = nil;
}
[self.chatTableView reloadData];
NSLog(@"get allConversation -- end");
isGetingAllConversation = NO;
[self checkCacheGetAllConversationAction];
});
}];
}
- (void)checkCacheGetAllConversationAction {
if (cacheCount > 0) {
NSLog(@"is have cache ,once again get all conversation");
cacheCount = 0;
[self getConversationList];
}
}
3. 聊天界面
3.1 实现监听 <漫游消息> 代理方法
- (void)onSyncRoamingMessageConversation:(JMSGConversation *)conversation {
// do something
// 一般情况这个漫游消息代理,只有才切换设备登录时才会有
// 当有某个会话有漫游消息时,会触发这个代理方法,UI 层需要根据自己逻辑去刷新聊天列表
}
3.2 实现监听 <漫游消息> 代理方法
- (void)onSyncOfflineMessageConversation:(JMSGConversation *)conversation
offlineMessages:(NSArray<__kindof JMSGMessage *> *)offlineMessages {
// do something
// 这个代理方法,一般是在有离线消息前提下,启动、从后台唤起、长连接断开又重连 这些情况下APP时会触发这个方法
// 当某个会话有离线消息时,会触发这个代理方法,UI 层需要根据自己逻辑去刷新聊天列表
}
3.3 用户信息更新、多媒体消息刷新
- 因为增加了用户信息自动更新功能,所以在聊天界面需要考虑刷新头像的逻辑处理;
- 因为消息同步是以会话为单位下发消息,所以最新的多媒体消息可能还在下载中,而此时聊天界面已经显示了这条消息,所以此时也应该考虑到刷新多媒体消息;
3.4 关于聊天界面UI层刷新的一些建议
JChat 中原来就有的一个 JCHATChatModel ,现在给这个model 增加几个属性
//是否是默认头像
@property (nonatomic, assign) BOOL isDefaultAvatar;
//头像图片的data.length
@property (nonatomic, assign) NSUInteger avatarDataLength;
//多媒体消息data.length(缩略图、语音)
@property (nonatomic, assign) NSUInteger messageMediaDataLength;
然后,在修改下 JCHATChatModel 类里的这个方法[-(void)setChatModelWith:conversationType:],
修改其中的 语音 和 图片 这两个case
case kJMSGContentTypeVoice:
{
[self setupVoiceSize:((JMSGVoiceContent *)message.content).duration];
//原来这里有下载语音的代码,把里面下载语音的方法注释掉,不要在这里下载,直接这样就好
}
break;
case kJMSGContentTypeImage:
{
[self setupImageSize];//修改一下这个方法,如下
}
break;
- (void)setupImageSize {
if (self.message.status == kJMSGMessageStatusReceiveDownloadFailed) {
_contentSize = CGSizeMake(77, 57);
return;
}
JMSGImageContent *imageContent = (JMSGImageContent *)self.message.content;
float imgHeight;
float imgWidth;
if (imageContent.imageSize.height >= imageContent.imageSize.width) {
imgHeight = 135;
imgWidth = (imageContent.imageSize.width/imageContent.imageSize.height) *imgHeight;
} else {
imgWidth = 135;
imgHeight = (imageContent.imageSize.height/imageContent.imageSize.width) *imgWidth;
}
if ((imgWidth > imgHeight?imgHeight/imgWidth:imgWidth/imgHeight)<0.47) {
self.imageSize = imgWidth > imgHeight?CGSizeMake(135, 55):CGSizeMake(55, 135);
_contentSize = imgWidth > imgHeight?CGSizeMake(135, 55):CGSizeMake(55, 135);
return;
}
self.imageSize = CGSizeMake(imgWidth, imgHeight);
_contentSize = CGSizeMake(imgWidth, imgHeight);
}
针对代理方法,下面几个代理方法在 JChat demo 里有做了相应的调整和优化,这里就不一一写出来,直接去Demo里查看
- (void)onSendMessageResponse:(JMSGMessage *)message error:(NSError *)error{
}
- (void)onReceiveMessage:(JMSGMessage *)message error:(NSError *)error{
}
- (void)onSyncOfflineMessageConversation:(JMSGConversation *)conversation offlineMessages:(NSArray<__kindof JMSGMessage *> *)offlineMessages{
}
- (void)onSyncRoamingMessageConversation:(JMSGConversation *)conversation{
}
对于 JCHATMessageContentView 这个类,做了优化
//原来的
- (void)setMessageContentWith:(JMSGMessage *)message
// 换成这个,增加一个block
- (void)setMessageContentWith:(JMSGMessage *)message handler:(void(^)(NSUInteger messageMediaDataLength))block;
对于 JCHATMessageTableViewCell 这个类里也做了优化,具体请查看demo
// 增加了一个刷新多媒体消息的block
@property(strong, nonatomic) JCHATMessageTableViewCellRefreshMediaMessage messageTableViewCellRefreshMediaMessage;
// JCHATMessageTableViewCell.m 里的这个方法里面 contentview 调用的方法要改成上面优化的那个方法
- (void)updateFrameWithContentFrame:(CGSize)contentSize {
/*
····此处省略一些未变动的代码
*/
__weak __typeof__(self) weakSelf = self;
[_messageContent setMessageContentWith:_model.message handler:^(NSUInteger messageMediaDataLength) {
__strong __typeof__(weakSelf) strongSelf = weakSelf;
BOOL isShouldRefresh = NO;
if (weakSelf.model.messageMediaDataLength != messageMediaDataLength) {
isShouldRefresh = YES;
weakSelf.model.messageMediaDataLength = messageMediaDataLength;
if (strongSelf.messageTableViewCellRefreshMediaMessage) {
strongSelf.messageTableViewCellRefreshMediaMessage(strongSelf.model,isShouldRefresh);
}
}
}];
}
在聊天界面的 cellForRowAtIndexPath:函数里的 cellIdentifier = @"MessageCell" 这个里增加了刷新多媒体消息的block,如下:
static NSString *cellIdentifier = @"MessageCell";
JCHATMessageTableViewCell *cell = (JCHATMessageTableViewCell *)[tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (cell == nil) {
cell = [[JCHATMessageTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];
cell.conversation = _conversation;
}
[cell setCellData:model delegate:self indexPath:indexPath];
__weak __typeof(self)weakSelf = self;
cell.messageTableViewCellRefreshMediaMessage = ^(JCHATChatModel *cellModel,BOOL isShouldRefresh){
__strong __typeof(weakSelf)strongSelf = weakSelf;
if (isShouldRefresh) {
[strongSelf refreshCellMessageMediaWithChatModel:cellModel];
}
};
具体的修改,可以到JChat里去查看,有些增加的函数没有写到这里,大家通过上面写的方法点击跟进去函数里查看就可以看到。
以上的修改和优化,可能还存在一定的问题,还需要继续优化,各位开发者可以自己进行修改。
JChat里的代码都是针对JChat这个简单的demo所写的,所以开发者不要完全依赖这里的代码,不要直接简单的复制粘贴拿去去使用,最好要根据自己APP的实际情况来加以分解和优化