无论是IOS还是安卓,集成环信SDK遇到的第一个问题,就是如何显示自有用户体系中的昵称和头像。运行环信的demo app,注册用户是直接使用环信ID(username)作为用户名,但是在我们实际应用中,需要将自有用户体系的UserId生成GUID作为环信ID(username)
【参考:http://docs.easemob.com/im/100serverintegration/20users】,
这时候如果不经过处理,则会显示如下界面:
那么如何处理,才能显示正确的用户昵称和头像呢?
其实官方已经提供有解决方案了,只不过没有给出示例代码而已。
http://docs.easemob.com/im/490integrationcases/10nickname
引用一下关键文字:
方法二:从消息扩展中获取昵称和头像
昵称和头像的获取:把用户基本的昵称和头像的URL放到消息的扩展中,通过消息传递给接收方,当收到一条消息时,则能通过消息的扩展得到发送者的昵称和头像URL,然后保存到本地数据库和缓存。当显示昵称和头像时,请从本地或者缓存中读取,不要直接从消息中把赋值拿给界面(否则当用户昵称改变后,同一个人会显示不同的昵称)。
昵称和头像的更新:当扩展消息中的昵称和头像 URI 与当前本地数据库和缓存中的相应数据不同的时候,需要把新的昵称保存到本地数据库和缓存,并下载新的头像并保存到本地数据库和缓存。
没错,官方提供了两种思路,鉴于项目的实际情况,我选择了【方法二】。
于是,无论安卓,还是IOS,我们都是这样处理的:
【1】.APP间传递用户属性信息:发送(文本、图片…)消息时,要在消息扩展(message.ext)中附带当前用户的属性信息;
【2】.本地缓存用户信息:在接收消息的回调函数里,读取消息扩展(message.ext)里用户属性(键值对)信息;如果本地缓存(sqlite)不存在该用户,则新增缓存记录,如果存在,则更新记录;(用户登录或注册成功后,也要更新用户缓存信息)
【3】.获取用户属性信息:在需要显示昵称的地方,根据环信ID,读取sqlite缓存数据,获取用户昵称和头像;
用户属性信息:ChatUserId(环信ID),ChatUserNick(用户昵称), ChatUserPic(用户头像,完整的url地址);
IOS关键代码:
// 环信聊天用的昵称和头像(发送聊天消息时,要附带这3个属性)
#define kChatUserId @"ChatUserId"// 环信账号
#define kChatUserNick @"ChatUserNick"
#define kChatUserPic @"ChatUserPic"
ChatUserCacheInfo是环信用户信息缓存管理类
ChatUserCacheInfo.h
#import <Foundation/Foundation.h>
@interface ChatUserCacheInfo : NSObject
@property(nonatomic,copy)NSString* Id;
@property(nonatomic,copy)NSString* NickName;
@property(nonatomic,copy)NSString* AvatarUrl;
@end
@interface ChatUserCacheUtil : NSObject
+(void)saveInfo:(NSString *)openId
imgId:(NSString*)imgId
nickName:(NSString*)nickName;
+(void)saveDict:(NSDictionary *)userinfo;
+(void)saveModel:(UserApiModel*)user;
+(ChatUserCacheInfo*)queryById:(NSString *)userid;
@end
ChatUserCacheUtil.m
#import "ChatUserCacheUtil.h"
#import "FMDB.h"
#define DBNAME @"cache_data.db"
@implementation ChatUserCacheInfo
@end
@implementation ChatUserCacheUtil
+(void)createTable:(FMDatabase *)db
{
if ([db open]) {
if (![db tableExists :@"userinfo"]) {
if ([db executeUpdate:@"create table userinfo (userid text, username text, userimage text)"]) {
NSLog(@"create table success");
}else{
NSLog(@"fail to create table");
}
}else {
NSLog(@"table is already exist");
}
}else{
NSLog(@"fail to open");
}
}
+ (void)clearTableData:(FMDatabase *)db
{
if ([db executeUpdate:@"DELETE FROM userinfo"]) {
NSLog(@"clear successed");
}else{
NSLog(@"fail to clear");
}
}
+(FMDatabase*)getDB{
NSString *docsPath = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
NSString *dbPath = [docsPath stringByAppendingPathComponent:DBNAME];
FMDatabase *db = [FMDatabase databaseWithPath:dbPath];
[self createTable:db];
return db;
}
+(void)saveModel:(UserApiModel*)user{
[ChatUserCacheUtil saveInfo:user.EaseMobUserName imgId:user.HeadImg nickName:user.Username];
}
+(void)saveInfo:(NSString *)openId
imgId:(NSString*)imgId
nickName:(NSString*)nickName{
NSMutableDictionary *extDic = [NSMutableDictionary dictionary];
[extDic setValue:openId forKey:kChatUserId];
[extDic setValue:@"[url=http://img.baidu.com"]http://img.baidu.com/"[/url]+imgId forKey:kChatUserPic];//完整图片路径"http://img.baidu.com/1234"。如果imgId是相对路径,那完整路径就是类似"http://img.baidu.com/abc.jpg"
[extDic setValue:nickName forKey:kChatUserNick];
[ChatUserCacheUtil saveDict:extDic];
}
+(void)saveDict:(NSDictionary *)userinfo{
FMDatabase *db = [self getDB];
NSString *userid = [userinfo objectForKey:kChatUserId];
if ([db executeUpdate:@"DELETE FROM userinfo where userid = ?", userid]) {
DLog(@"删除成功");
}else{
DLog(@"删除失败");
}
NSString *username = [userinfo objectForKey:kChatUserNick];
NSString *userimage = [userinfo objectForKey:kChatUserPic];
if ([db executeUpdate:@"INSERT INTO userinfo (userid, username, userimage) VALUES (?, ?, ?)", userid,username,userimage]) {
DLog(@"插入成功");
}else{
DLog(@"插入失败");
}
// NSLog(@"%d: %@", [db lastErrorCode], [db lastErrorMessage]);
FMResultSet *rs = [db executeQuery:@"SELECT userid, username, userimage FROM userinfo where userid = ?",userid];
if ([rs next]) {
NSString *userid = [rs stringForColumn:@"userid"];
NSString *username = [rs stringForColumn:@"username"];
NSString *userimage = [rs stringForColumn:@"userimage"];
DLog(@"查询一个 %@ %@ %@",userid,username,userimage);
}
rs = [db executeQuery:@"SELECT userid, username, userimage FROM userinfo"];
while ([rs next]) {
NSString *userid = [rs stringForColumn:@"userid"];
NSString *username = [rs stringForColumn:@"username"];
NSString *userimage = [rs stringForColumn:@"userimage"];
DLog(@"查询所有 %@ %@ %@",userid,username,userimage);
}
[rs close];
// NSLog(@"%d: %@", [db lastErrorCode], [db lastErrorMessage]);
[db close];
}
+(ChatUserCacheInfo*)queryById:(NSString *)userid{
FMDatabase *db = [self getDB];
if ([db open]) {
FMResultSet *rs = [db executeQuery:@"SELECT userid, username, userimage FROM userinfo where userid = ?",userid];
if ([rs next]) {
ChatUserCacheInfo *userInfo = [[ChatUserCacheInfo alloc] init];
userInfo.Id = [rs stringForColumn:@"userid"];
userInfo.NickName = [rs stringForColumn:@"username"];
userInfo.AvatarUrl = [rs stringForColumn:@"userimage"];
DLog(@"查询一个 %@",userInfo);
return userInfo;
}else{
return nil;
}
}else{
return nil;
}
}
@end
首先要在用户登录或注册成功后,返回用户登录信息时,缓存一下用户信息:
@protocol UserApiModel <NSObject>
@end
@interface UserApiModel : BaseJSONModel
@property(nonatomic, assign)int Id;
@property(nonatomic, copy)NSString *Username;
@property(nonatomic, copy)NSString *Email;
@property(nonatomic, copy)NSString *HeadImg;
@property(nonatomic, copy)NSString *EaseMobUserName;
@property(nonatomic, copy)NSString *EaseMobPassword;
@end
// 登录成功后返回用户model,需要为环信聊天窗口缓存用户信息
[ChatUserCacheUtil saveModel:user];
然后在接收环信消息的回调函数里保存用户信息,HsMainViewController.m是我们项目的主框架,我们在这里写了回调函数,无论群聊还是单聊消息,都会调用这里:
// 收到消息回调
-(void) didReceiveMessage:(EMMessage *)message
{
[ChatUserCacheUtil saveDict:message.ext];
BOOL needShowNotification = (message.messageType != eMessageTypeChat) ? [self needShowNotification:message.conversationChatter] : YES;
if (needShowNotification) {
#if !TARGET_IPHONE_SIMULATOR
BOOL isAppActivity = [[UIApplication sharedApplication] applicationState] == UIApplicationStateActive;
if (!isAppActivity) {
[self showNotificationWithMessage:message];
}else {
[self playSoundAndVibration];
}
#endif
}
}
环信页面主要是在ChatViewController和ConversationListViewController里显示用户对话,所以我们要在这两个页面里从缓存取用户头像和昵称,先从ChatViewController开始:
- (id<IMessageModel>)messageViewController:(EaseMessageViewController *)viewController
modelForMessage:(EMMessage *)message
{
id<IMessageModel> model = [[EaseMessageModel alloc] initWithMessage:message];
ChatUserCacheInfo *userinfo = [ChatUserCacheUtil queryById:model.nickname];
if (userinfo != nil) {
model.nickname = userinfo.NickName;
model.avatarURLPath = userinfo.AvatarUrl;
}
model.avatarImage = [UIImage imageNamed:@"EaseUIResource.bundle/user"];
model.failImageName = @"imageDownloadFail";
return model;
}
然后是ConversationListViewController:
#pragma mark - EaseConversationListViewControllerDataSource
- (id<IConversationModel>)conversationListViewController:(EaseConversationListViewController *)conversationListViewController
modelForConversation:(EMConversation *)conversation
{
EaseConversationModel *model = [[EaseConversationModel alloc] initWithConversation:conversation];
if (model.conversation.conversationType == eConversationTypeChat) {
ChatUserCacheInfo *userinfo = [ChatUserCacheUtil queryById:model.conversation.chatter];
if (userinfo != nil) {
model.title = userinfo.NickName;
model.avatarURLPath = userinfo.AvatarUrl;
}
model.avatarImage = PlaceholderImgChatUser;
} else if (model.conversation.conversationType == eConversationTypeGroupChat) {
// 此处省略100行代码........
}
}
最后,为了让另外一个客户端也能正确显示头像和昵称,app发送消息时,要在消息扩展里附带用户信息,代码写在EaseSDKHelper.m里:
// 重新消息扩展组织
+(NSMutableDictionary*)reGetMessageExt:(NSDictionary *)messageExt{
NSMutableDictionary *extDic = [NSMutableDictionary dictionaryWithDictionary:messageExt];
[extDic setValue:[SettingData share].UserChatId forKey:kChatUserId];
[extDic setValue:[SettingData share].UserHeadImg.ServerThumbUrlStr forKey:kChatUserPic];
[extDic setValue:[SettingData share].UserName forKey:kChatUserNick];
return extDic;
}
+ (EMMessage *)sendTextMessage:(NSString *)text
to:(NSString *)toUser
messageType:(EMMessageType)messageType
requireEncryption:(BOOL)requireEncryption
messageExt:(NSDictionary *)messageExt
{
// 表情映射。
NSString *willSendText = [EaseConvertToCommonEmoticonsHelper convertToCommonEmoticons:text];
EMChatText *textChat = [[EMChatText alloc] initWithText:willSendText];
EMTextMessageBody *body = [[EMTextMessageBody alloc] initWithChatObject:textChat];
EMMessage *message = [[EMMessage alloc] initWithReceiver:toUser bodies:[NSArray arrayWithObject:body]];
message.requireEncryption = requireEncryption;
message.messageType = messageType;
message.ext = [self reGetMessageExt:messageExt];
EMMessage *retMessage = [[EaseMob sharedInstance].chatManager asyncSendMessage:message
progress:nil];
return retMessage;
}
+ (EMMessage *)sendImageMessageWithImage:(UIImage *)image
to:(NSString *)to
messageType:(EMMessageType)messageType
requireEncryption:(BOOL)requireEncryption
messageExt:(NSDictionary *)messageExt
progress:(id<IEMChatProgressDelegate>)progress
{
return [self sendImageMessageWithImage:image to:to messageType:messageType requireEncryption:requireEncryption messageExt:messageExt quality:0.6 progress:progress];
}
+ (EMMessage *)sendImageMessageWithImage:(UIImage *)image
to:(NSString *)to
messageType:(EMMessageType)messageType
requireEncryption:(BOOL)requireEncryption
messageExt:(NSDictionary *)messageExt
quality:(float)quality
progress:(id<IEMChatProgressDelegate>)progress
{
// 此处省略9行代码....
message.ext = [self reGetMessageExt:messageExt];
EMMessage *retMessage = [[EaseMob sharedInstance].chatManager asyncSendMessage:message
progress:progress];
return retMessage;
}
+ (EMMessage *)sendVoiceMessageWithLocalPath:(NSString *)localPath
duration:(NSInteger)duration
to:(NSString *)to
messageType:(EMMessageType)messageType
requireEncryption:(BOOL)requireEncryption
messageExt:(NSDictionary *)messageExt
progress:(id<IEMChatProgressDelegate>)progress
{
// 此处省略4行代码....
message.ext = [self reGetMessageExt:messageExt];
EMMessage *retMessage = [[EaseMob sharedInstance].chatManager asyncSendMessage:message
progress:progress];
return retMessage;
}
+ (EMMessage *)sendVideoMessageWithURL:(NSURL *)url
to:(NSString *)to
messageType:(EMMessageType)messageType
requireEncryption:(BOOL)requireEncryption
messageExt:(NSDictionary *)messageExt
progress:(id<IEMChatProgressDelegate>)progress
{
// 此处省略4行代码....
message.ext = [self reGetMessageExt:messageExt];
EMMessage *retMessage = [[EaseMob sharedInstance].chatManager asyncSendMessage:message
progress:progress];
return retMessage;
}
+ (EMMessage *)sendFileMessage:(EMChatFile *)chatFile
to:(NSString *)to
messageType:(EMMessageType)messageType
requireEncryption:(BOOL)requireEncryption
messageExt:(NSDictionary *)messageExt
progress:(id<IEMChatProgressDelegate>)progress
{
// 此处省略4行代码....
message.ext = [self reGetMessageExt:messageExt];
EMMessage *retMessage = [[EaseMob sharedInstance].chatManager asyncSendMessage:message
progress:progress];
return retMessage;
}
以上代码为SDK V2版本,如果集成的是V3版本,请移步源码:
思路其实跟V2差不多,最大区别V3的回调方法didReceiveMessages比V2多了个【s】。
ChatUserCacheUtil我已经重命名为:UserCacheManager