IM消息收发

接收消息
接口设计

如果需要感知新消息的通知,可以注册消息通知回调IIMMsgListener。新消息到来时,onNewMsg方法将被调用。当某条消息被撤回时,onMsgRevoked方法将被调用。

/**监听消息事件 */
@protocol IIMMsgListener <NSObject>

/**
 * 收到新消息
 * @msgList      新消息列表
 */
- (void)onNewMsg:(NSArray<IMBaseMessage *> *)msgList;

/**
 * 消息被撤回
 * @msg 被撤回的消息
 */
- (void)onMsgRevoked:(IMBaseMessage *)msg;

@end

在AppMsgManager里面,提供了注册消息监听和移除消息监听的接口

/** 监听全局的消息事件 */
- (void)addMsgListener:(id<IIMMsgListener>)listener;
/** 取消监听 */
- (void)removeMsgListener:(id<IIMMsgListener>)listener;

为了方便使用,把onNewMsg和onMsgRevoked两个消息回调接口封装成Block的方式使用,因此可直接使用IMMsgListerner作为监听者。

typedef void(^NewMsg)(NSArray<IMBaseMessage *> *); //收到新消息Block
typedef void(^MsgRevoked)(IMBaseMessage *);  // 收到消息撤回通知Block

@interface IMMsgListener : NSObject <IIMMsgListener>

- (instancetype)initWithNewMsgBlock:(NewMsg)newMsgBlock revokedMsgBlock:(MsgRevoked)revokedMsgBolck;

@end
接口实现

在AppMsgManager里面进行消息的中转和改装,listenerList维护者监听者列表
onNewMessage:

- (void)onNewMessage:(NSArray *)msgs
{
    NSMutableArray<IMBaseMessage *> *msgList =  [NSMutableArray array];
    
    // 消息格式换转
    for (TIMMessage *msg in msgs){
        IMBaseMessage *baseMsg = [TimMsgUtil convertMsgFromTIMToIM:msg];
        [msgList addObject:baseMsg];
    }
    
    // 通知注册者数据到来
    [_listenerList enumerateObjectsUsingBlock:^(id<IIMMsgListener>  _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<IIMMsgListener>  _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<IMBaseMessage *> * _Nonnull msgList) {
        NSLog(@"收到新消息");
    } revokedMsgBlock:^(IMBaseMessage * _Nonnull msg) {
        NSLog(@"收到消息撤回通知");
    }];
    
id<IIMAppMsgService> msgService = [[JSObjection defaultInjector] getObject:@protocol(IIMAppMsgService)];
id<IIMAppMsgManager> msgManager = [msgService getMessageMgr];

[msgManager addMsgListener:msgListener];
样例

由于消息转换不完全,因此消息状态会有点问题。

  1. 收到文本消息

    收到文本消息
  2. 收到自定义消息(文件)

    收到自定义消息
自定义消息收发

如果你不满足于发送文字和图片,那么你可以发送自定义的消息。发送自定义消息时,将以IMCustomMsgElem为载体,打包我们要发送的数据。

@interface IMCustomMsgElem : NSObject<IBaseElem>

/**自定义Elem类型*/
@property(nonatomic, assign) uint32_t elemType;

/**自定义Elem的数据*/
@property(nonatomic, strong) NSData *elemData;

@end
属性 类型 说明
elemType uint32_t 自定义Elem的类型
elemData NSData 自定义Elem的数据内容
使用事例
  1. 自定义IMFileMsgElem
@interface IMFileMsgElem : NSObject <IBaseElem>

/** 文件名 */
@property(nonatomic, strong) NSString *fileName;
/** 文件大小 */
@property(nonatomic, assign) uint64_t fileSize;
/** 文件路径 */
@property(nonatomic, strong) NSString *url;

@end
  1. 使用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
  1. 重写toByteArray方法
-(NSData *)toByteArray
{
    return [PBCoder encodeDataWithObject:self];
}
  1. 发送自定义消息,自定义消息类型将通过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<IIMAppMsgService> msgService = [[JSObjection defaultInjector] getObject:@protocol(IIMAppMsgService)];
id<IIMAppMsgManager> msgMgr = [msgService getMessageMgr];

// 发消息
[msgMgr sendMsg:imMsg callback:nil];
  1. 自定义消息的解析
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<IIMAppMsgService> msgService = [[JSObjection defaultInjector] getObject:@protocol(IIMAppMsgService)];
id<IIMAppMsgManager> msgMgr = [msgService getMessageMgr];

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,793评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,567评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,342评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,825评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,814评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,680评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,033评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,687评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,175评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,668评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,775评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,419评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,020评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,206评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,092评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,510评论 2 343

推荐阅读更多精彩内容