最近一直挺忙,使用了一些第三方的东西,也遇见了不少的问题,前面使用的一些还好,比如推送,分享这些第三方都比较容易集成,这次集成环信的即时通讯还是费了一些周折,刚弄完,赶紧记录一下。
一.前期需要注意的地方
总的来说环信的开发文档和SDK做的已经很简单了,但是毕竟是第一次集成,使用别人的东西难免有点摸不着头。这次使用环信3.0版本来集成。基本上前面的操作都可以按着文档的说明进行了,这里感觉添加sdk前需要注意几个地方。
1.先对文档进行一个全面的阅读
我刚开始在做其他的第三方集成时也很着急,想着怎么赶紧找到一个入口。最后发现着急没用,所以先阅读以下文档,知道自己需要的功能大体怎么处理,需要集成哪一些jar包等。这次我开始就没有注意,阅读文档时就把独立的sdk的jar和so包都复制到了项目中,最后发现,有一个更好用一些的东西EaseUI 这个module,直接导入这个module就可以不用再去添加各种jar和so文件了。所以,我们有一个整体的把握很重要。
2.了解easeUI包
easeUI这个包下包含了好些jar还有百度地图的包,在【Android SDK 介绍和导入】这个地方说明的包基本都是这里的。
了解这些方便我们处理后边的冲突等问题。比如我的项目之前放入了百度sdk的jar,这里也有,但是版本不同可能引发问题,也可能由于重复引入jar导致问题。
二.添加集成
1.集成easeUI
我使用了easeUI这个module直接集成的,这样我们可以使用环信提供的一些UI,比如EaseChatFragment ,这是一个聊天界面,一般来说自己处理这个页面还是有一些困难或者问题。集成出现的问题。
这个也是官方提出的,首先我们要处理这个,我觉得还是把这个module的v4包版本号增加大这个方式好一些,例如使用:把 v4 包的版本号加大,譬如 compile 'com.android.support:support-v4:23.2.1这样编译就可以了。下面就是按照文档找到自己需要的API集成了。从注册,到登录,然后获取好友列等操作。
2.API集成出现的问题
1).一些异常比如出现:unknow server exception(异常信息没有截图),这个问题其实是我们没有按照文档说明去做,
//群主加人调用此方法
EMClient.getInstance().groupManager().addUsersToGroup(groupId, newmembers);//需异步处理
上面接口说了使用异步处理,我没有使用异步也就出现了异常。
2).
3.使用easeUI
我们在环信注册了用户之后就会有一个环信的id,通过这个id我们就可以和对方进行简答的通信了。使用EaseChatFragment。
按照文档说明创建一个Activity,然后写一下的代码:
EaseChatFragment chatFragment = new EaseChatFragment();
Bundle args = new Bundle();
args.putInt(EaseConstant.EXTRA_CHAT_TYPE, EaseConstant.CHATTYPE_SINGLE);
args.putString(EaseConstant.EXTRA_USER_ID, getIntent().getStringExtra("userID"));
chatFragment.setArguments(args);
FragmentManager supportFragmentManager = getSupportFragmentManager();
getSupportFragmentManager().beginTransaction().add(R.id.container, chatFragment).commit();
//container是你创建的activity的根布局的id
/**
* 发起聊天 这个是从MainActivty或者其他页面发起聊天,传入一个你注册的环信id
* @param input
*/
private void startChat(String input) {
//new出EaseChatFragment或其子类的实例
if(!TextUtils.isEmpty(input)){
Intent intent = new Intent(MainActivity.this, ChatActivity.class);
intent.putExtra("userID",input);
startActivity(intent);
}else{
Toast.makeText(this, "请输入一个用户id", Toast.LENGTH_SHORT).show();
}
}
完成上述的注册,登录和EaseChatFragment后通信功能基本上就可以实现了。如图:
到此环信的集成主要功能也就是完成的,其他的可能会用到聊天列表,通讯录这些按照文档集成就可以,不过环信不对通讯录进行维护,也就是说要是保存用户信息什么的东西,需要我们自己的服务器来保存,然后在使用时根据环信id去服务器获取这个用户的其他信息。
4.修改用户头像和用户名
上面的那个图我们看到了用户头像是默认的没有图片,这个怎么处理?环信只是给出了一个思路,demo中有实现的逻辑,但是找出来还是不太容易。
环信给出的两个方案:
1).从app服务器获取用户数据头像
2).从消息扩展中获取用户数据
最后我选了第二重方案,我也不知道怎么就选了第二种方法,刚开始对这个问题很晕,没有一点思路,在网上找了几个相关的,所以两个方法弄的有点晕,然后就没办法,按照一个方式尝试,最后逐渐明白,其实这个方式相对来听容易实现,就是要知道从什么地方下手。看看最后结果如图:
图片中已经有了头像了。
我们在使用集成的过程中可能需要对EaseChatFragment进行一个重写,就像上图图右上角是一个垃圾桶图标,我们自己需要一个点击进入用户信息的页面,所以从写这个EaseChatFragment的一个方法:
@Override
protected void setUpView() {
super.setUpView();
//messageList.init(toChatUsername, chatType, new CustomChatRowProvider());//messageList初始化??
titleBar.setRightImageResource(R.mipmap.user);
titleBar.getRightLayout().setOnClickListener(new View.OnClickListener() {//处理点击
@Override
public void onClick(View v) {
if (chatType == EaseConstant.CHATTYPE_GROUP ) {//群聊,传递当前的 emGroup
Intent intent = new Intent(getActivity(), IMDiscussInfoActivity.class);//群组,需要一个id
intent.putExtra("conversationID", toChatUsername);
startActivity(intent);
} else{// 单聊EaseConstant.CHATTYPE_SINGLE
Intent intent = new Intent(getActivity(), IMPersonalInfoActivity.class);//个人
intent.putExtra("conversationID", toChatUsername);//回话id就是手机号码
startActivity(intent);
}
}
});
}
我们在 EaseChatFragment找上述方法重写的过程可能会发现有一个接口:EaseChatFragmentHelper,如果你有了这个印象后边可能就容易了,网上的其他资料让你能快速想起来然后处理他。这个接口就是文档说的发送扩扩展消息要使用的,所以按照文档说明从写接口的方法:
/**
* 实现EaseChatFragmentHelper接口方法
* 希望通过设置附加消息实现头像传递
* @param message
*/
@Override
public void onSetMessageAttributes(EMMessage message) {
//发送消息时附加参数p
LogUtils.e("TAG","onSetMessageAttributes");
Preferences preferences = new Preferences(getActivity());
message.setAttribute(ConstantValue.IM_USER_ID, preferences.getUserPhoneNumber());//标识用户的手机号
message.setAttribute(ConstantValue.IM_NICK_NAME, preferences.getUserName());
message.setAttribute(ConstantValue.IM_USER_IMAGE, preferences.getString("userImage")) ;
//这里用是图片的完整链接地址,如果要取缩略图,需要服务端配合;
LogUtils.e("TAG","fasong用户头像:"+preferences.getString("userImage"));
}
写完不要忘记了在你的这个Fragment中在重写一个方法比如:onCreate().因为我们要注册这个接口的实现。
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setChatFragmentListener(this);//注册接口,EaseChatFragment提供的方法
}
这样看,基本上完成了扩展消息的发送。接下来就是消息的接收和保存了。
在最初阅读文档按照步骤做的时候有没有注意到demo的Application中注册了这样一句:
//init demo helper
DemoHelper.getInstance().init(applicationContext);
文档说我们要这样注册:
EMOptions options = new EMOptions();
// 默认添加好友时,是不需要验证的,改成需要验证
options.setAcceptInvitationAlways(false);
...
//初始化
EMClient.getInstance().init(applicationContext, options);
//在做打包混淆时,关闭debug模式,避免消耗不必要的资源
EMClient.getInstance().setDebugMode(true);
这就导致了我当初没有看清楚demo中的那句话。现在我们点击进去看看:
/** * init helper * @param context application context */ public void init(Context context) { demoModel = new DemoModel(context); EMOptions options = initChatOptions(); //use default options if options is null if (EaseUI.getInstance().init(context, options)) { appContext = context; //debug mode, you'd better set it to false, if you want release your App officially. EMClient.getInstance().setDebugMode(true); //get easeui instance easeUI = EaseUI.getInstance(); //to set user's profile and avatar setEaseUIProviders(); //initialize preference manager PreferenceManager.init(context); //initialize profile manager getUserProfileManager().init(context); // TODO: set Call options 此处省略中间若干刚... setGlobalListeners(); //broadcastManager = LocalBroadcastManager.getInstance(appContext); // initDbDao(); } }
这里完成了我们的注册和其他的操作。
这里我们需要知道的就是除了EaseUI注册的那几行之外的代码:
setEaseUIProviders();
setGlobalListeners();
下面是我修改后的项目中的代码:
*
* @param context application context
*/
public void init(Context context) {
//demoModel = new DemoModel(context);
EMOptions options = new EMOptions();
options.setAcceptInvitationAlways(false);//添加好友不需要验证
//use default options if options is null
if (EaseUI.getInstance().init(context, options)) {
appContext = context;
//debug mode, you'd better set it to false, if you want release your App officially.
EMClient.getInstance().setDebugMode(true);
//get easeui instance
easeUI = EaseUI.getInstance();
//to set user's profile and avatar
preferences = new Preferences(context);
setEaseUIProviders();
}
}
对比发现除了注册的之外关键的就是
setEaseUIProviders();这句话
你可以自己在demo中看看这,其实最主要的就是他。进入这个方法发现有一段代码:
easeUI.setUserProfileProvider(new EaseUserProfileProvider() { @Override public EaseUser getUser(String username) { return getUserInfo(username); } });突然明白了 getUserInfo(username)就是返回最后用户消息的那个方法,他返回一个EaseUser对象。我重写后的getUserInfo(username)如下:
private EaseUser getUserInfo(String username) {
EaseUser user = null;
if (username.equals(EMClient.getInstance().getCurrentUser())) {
//如果用户是本人,就设置自己的头像
user = new EaseUser(username);
user.setAvatar(preferences.getString(ConstantValue.USER_IMAGE));
user.setNick(preferences.getUserName());
return user;
}else if(!username.equals(EMClient.getInstance().getCurrentUser())){
DbManager dbManager = x.getDb(MainApplication.getInstance().initDB());
try {
IMContract imContract = dbManager.findById(IMContract.class, username);
if(imContract!=null){
LogUtils.e(TAG,"数据库查询结果:"+imContract.getContraactPhone());
user = new EaseUser(username);
user.setAvatar(imContract.getImageUrl());
user.setNick(imContract.getNickName());
return user;
}
} catch (DbException e) {
e.printStackTrace();
}
}
这个方法给EaseUser对象赋值了。这里还有一点没有说明。前面在
EaseChatFragmentHelper 接口中添加了扩展消息,我们还没有取出来呢。
现在看看环信demo中和我写的不一样的地方:就是有一个
setGlobalListeners();
这个方法,这个就是环信接收消息的地方。当时点击进去发现一大片。我们在这个方法的最后发现了这么一句:
registerMessageListener();
这才是真正接收消息的地方。在回忆一下当初看文档的时候,在“消息”这个模块中有这个借口:
EMClient.getInstance().chatManager().addMessageListener(msgListener);
EMMessageListener msgListener = new EMMessageListener(){...}
现在需要做的就是在这里实现接口方法,我研究后重写的:
@Override
public void onMessageReceived(List
LogUtils.e(TAG, "onMessageReceived执行消息监听..");
receivedListener.onMessageReceived(list);
for (EMMessage message : list) {//保存下数据
String chatUserId = message.getStringAttribute(ConstantValue.IM_USER_ID, "");
String avatarUrl = message.getStringAttribute(ConstantValue.IM_USER_IMAGE, "");
String nickName = message.getStringAttribute(ConstantValue.IM_NICK_NAME, "");
LogUtils.e("TAG", "appjieshou消息获取的数据是chatUserId:" + chatUserId);
DbManager db = x.getDb(initDB());
IMContract imContract = new IMContract();
imContract.setContraactPhone(chatUserId);
imContract.setImageUrl(avatarUrl);
imContract.setNickName(nickName);
try {
db.saveOrUpdate(imContract);
} catch (DbException e) {
e.printStackTrace();
LogUtils.e(TAG,"数据库操作异常:"+e.getMessage());
}
ChatHelper.getInstance().getNotifier().onNewMsg(message);
}
}
这里获取当初发送的消息并保存在数据库中,就好了,我们在使用的时候根据环信的id去查数据库就可以拿到用户信息了。这里我使用的是xUtils3中的数据库操作。
ChatHelper.getInstance().getNotifier().onNewMsg(message);//ChatHelper就是demo中的DemoHelper这个是接收到消息后发送一个通知的.
现在回到前面的getUserInfo(username)方法中,可以看到这里只是根据username进行了数据库查询,然后返回一个EsaseUser对象。这样我们就完成了用户头像和其他信息的传递。在一次聊天就会发现有头像和用户名信息了。
这里需要注意,头像这里直接传递一个图片的url就好了,环信已经封装好了对他的处理。
如果你在研究的过程发现了EaseUserUtils这个你就知道了:
/** * set user avatar * @param username */ public static void setUserAvatar(Context context, String username, ImageView imageView){ Log.e("TAG","setUserAvatar设置头像返回:"+username); EaseUser user = getUserInfo(username); if(user != null && user.getAvatar() != null){ try { int avatarResId = Integer.parseInt(user.getAvatar()); Glide.with(context).load(avatarResId).into(imageView); } catch (Exception e) { //use default avatar Glide.with(context).load(user.getAvatar()).diskCacheStrategy(DiskCacheStrategy.ALL).placeholder(R.drawable.ease_default_avatar).into(imageView); } }else{ Glide.with(context).load(R.drawable.ease_default_avatar).into(imageView); } }
可以看到在这里使用了Glide来加载图片,没有图片的url时使用本地的这个替代。
参考资料:
1. .http://www.cnblogs.com/zhang-cb/p/6292254.html2.http://www.imgeek.org/article/825307856