接收消息
接口设计
如果需要感知新消息的通知,可以注册消息通知回调IIMMsgListener。新消息到来时,onNewMsg方法将被调用。当某条消息被撤回时,onMsgRevoked方法将被调用。
/**监听消息事件 */
@protocol IIMMsgListener
/**
* 收到新消息
* @msgList 新消息列表
*/
- (void)onNewMsg:(NSArray *)msgList;
/**
* 消息被撤回
* @msg 被撤回的消息
*/
- (void)onMsgRevoked:(IMBaseMessage *)msg;
@end
在AppMsgManager里面,提供了注册消息监听和移除消息监听的接口
/** 监听全局的消息事件 */
- (void)addMsgListener:(id)listener;
/** 取消监听 */
- (void)removeMsgListener:(id)listener;
为了方便使用,把onNewMsg和onMsgRevoked两个消息回调接口封装成Block的方式使用,因此可直接使用IMMsgListerner作为监听者。
typedef void(^NewMsg)(NSArray *); //收到新消息Block
typedef void(^MsgRevoked)(IMBaseMessage *); // 收到消息撤回通知Block
@interface IMMsgListener : NSObject
- (instancetype)initWithNewMsgBlock:(NewMsg)newMsgBlock revokedMsgBlock:(MsgRevoked)revokedMsgBolck;
@end
接口实现
在AppMsgManager里面进行消息的中转和改装,listenerList维护者监听者列表
onNewMessage:
- (void)onNewMessage:(NSArray *)msgs
{
NSMutableArray *msgList = [NSMutableArray array];
// 消息格式换转
for (TIMMessage *msg in msgs){
IMBaseMessage *baseMsg = [TimMsgUtil convertMsgFromTIMToIM:msg];
[msgList addObject:baseMsg];
}
// 通知注册者数据到来
[_listenerList enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
[obj onNewMsg:msgList];
}];
}
onMessageRevoke
- (void)onMessageRevoke:(TIMMessageLocator*)locator
{
IMSession *session = [IMSession IMSessionWithSessionId:locator.sessId type:locator.sessType];
IMMessageStruct *msgBody = [[IMMessageStruct alloc] initWithType:0 seq:locator.seq elems:nil];
IMBaseMessage *baseMsg = [IMBaseMessage IMBaseMessageWithSenderUid:nil session:session msg:msgBody];
[_listenerList enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
[obj onMsgRevoked:baseMsg];
}];
}
消息转换需要自行去TimMsgUtil添加转换处理:
- IMBaseMessage转TIMMessage(以IMFileMsgElem转TimFileElem为例)
if([msgElem getType] == IM_FILE_TYPE)
{
IMFileMsgElem *fileElem = (IMFileMsgElem *)msgElem;
TIMFileElem *elem = [[TIMFileElem alloc] init];
elem.filename = fileElem.fileName;
elem.fileSize = (int)fileElem.fileSize;
elem.path = fileElem.url;
[timMsg addElem:elem];
}
- TIMMessage转IMBaseMessage(以TimCustomElem转IMBaseMessage为例)
得到IMCustomMsgElem后在根据它得ElemType进行第二层的PB解析,得到需要的IMElem
if ([timElem isKindOfClass:[TIMCustomElem class]])
{
TIMCustomElem *custonElem = (TIMCustomElem *)timElem;
// 两层PB解析
IMCustomMsgElem *customElem = (IMCustomMsgElem *)[PBCoder decodeObjectOfClass:[IMCustomMsgElem class] fromData:custonElem.data];
if(customElem.elemType == IM_FILE_TYPE)
{
NSData *elemData = (NSData *)customElem.elemData;
IMFileMsgElem *fileElem = (IMFileMsgElem *)[PBCoder decodeObjectOfClass:[IMFileMsgElem class] fromData:elemData];
[msgElems addObject:fileElem];
}
}
上层调用
IMMsgListener *msgListener = [[IMMsgListener alloc] initWithNewMsgBlock:^(NSArray * _Nonnull msgList) {
NSLog(@"收到新消息");
} revokedMsgBlock:^(IMBaseMessage * _Nonnull msg) {
NSLog(@"收到消息撤回通知");
}];
id msgService = [[JSObjection defaultInjector] getObject:@protocol(IIMAppMsgService)];
id msgManager = [msgService getMessageMgr];
[msgManager addMsgListener:msgListener];
样例
由于消息转换不完全,因此消息状态会有点问题。
-
收到文本消息
-
收到自定义消息(文件)
自定义消息收发
如果你不满足于发送文字和图片,那么你可以发送自定义的消息。发送自定义消息时,将以IMCustomMsgElem为载体,打包我们要发送的数据。
@interface IMCustomMsgElem : NSObject
/**自定义Elem类型*/
@property(nonatomic, assign) uint32_t elemType;
/**自定义Elem的数据*/
@property(nonatomic, strong) NSData *elemData;
@end
属性 | 类型 | 说明 |
---|---|---|
elemType | uint32_t | 自定义Elem的类型 |
elemData | NSData | 自定义Elem的数据内容 |
使用事例
- 自定义IMFileMsgElem
@interface IMFileMsgElem : NSObject
/** 文件名 */
@property(nonatomic, strong) NSString *fileName;
/** 文件大小 */
@property(nonatomic, assign) uint64_t fileSize;
/** 文件路径 */
@property(nonatomic, strong) NSString *url;
@end
- 使用PB协议指定要解析的字段
@implementation IMFileMsgElem
PBCODER_TABLE_BEGIN(IMFileMsgElem)
PBCODER_OBJ_PROPERTY(IMFileMsgElem, fileName, NSString, 1) //解析文件名字段
PBCODER_UINT64_PROPERTY(IMFileMsgElem, fileSize, 2) //解析文件大小字段
PBCODER_OBJ_PROPERTY(IMFileMsgElem, url, NSString, 3) //解析文件路径字段
PBCODER_TABLE_END(IMFileMsgElem)
@end
- 重写toByteArray方法
-(NSData *)toByteArray
{
return [PBCoder encodeDataWithObject:self];
}
- 发送自定义消息,自定义消息类型将通过toByteArray函数转换成NSData,最后将包装成IMCustomMsgElem进行发送。
// 自定义消息类型创建
IMFileMsgElem *fileElem = [[IMFileMsgElem alloc] init];
fileElem.url = filePath;
fileElem.fileName = filePath;
fileElem.fileSize = 5000;
// 将自定义消息类型包装成IMCustonMsgElem
IMCustomMsgElem *customElem = [[IMCustomMsgElem alloc] init];
customElem.elemType = IM_FILE_TYPE;
customElem.elemData = fileElem.toByteArray; // 将数据塞给IMCustomMsgElem的elemData
IMMessageStruct *msgBody = [[IMMessageStruct alloc] initWithType:IM_FILE_TYPE seq:0 elems:@[customElem]];
// 构建通用消息体
IMBaseMessage *imMsg = [[IMBaseMessage alloc] initWithSenderUid:@"" session:nil msg:msgBody];
imMsg.session = [[IMSession alloc] init];
imMsg.session.sessionId = _conversation.receiver;
imMsg.session.type = _conversation.type;
// 获取IIMAppMsgManager接口实例
id msgService = [[JSObjection defaultInjector] getObject:@protocol(IIMAppMsgService)];
id msgMgr = [msgService getMessageMgr];
// 发消息
[msgMgr sendMsg:imMsg callback:nil];
- 自定义消息的解析
if ([timElem isKindOfClass:[TIMCustomElem class]])
{
TIMCustomElem *custonElem = (TIMCustomElem *)timElem;
// 两层PB解析
IMCustomMsgElem *customElem = (IMCustomMsgElem *)[PBCoder decodeObjectOfClass:[IMCustomMsgElem class] fromData:custonElem.data];
if(customElem.elemType == IM_FILE_TYPE)
{
NSData *elemData = (NSData *)customElem.elemData;
IMFileMsgElem *fileElem = (IMFileMsgElem *)[PBCoder decodeObjectOfClass:[IMFileMsgElem class] fromData:elemData];
[msgElems addObject:fileElem];
}
}
// 获取IIMAppMsgManager接口实例
id
id