关于即时通讯,项目中要是需要一个收发消息的功能。从开始到写完即时通讯这块儿,花了大约3天时间。但真的想吐槽下bmobIM的服务器,有短板时间都在等待连接:disconnect
或者java.util.concurrent.TimeoutException
。问他们客服,他们说是IM的带宽满了。原话是这样:IM带宽满了。心里也是一万个无奈呀,但是也没办法,项目负责人点名了bmob,那就来吧:
bmob的文档这块儿写得算是不错了,我来整理一下:
IM SDK 版本 | Data SDK 版本 |
---|---|
bmob-im:2.0.1 至 2.0.2 | bmob-sdk:3.4.6-0304 |
bmob-im:2.0.3 至 2.0.4 | bmob-sdk:3.4.6 |
bmob-im:2.0.5 | bmob-sdk:3.4.7-aar |
bmob-im:2.0.6 至 2.0.8 | bmob-sdk:3.5.5 |
bmob-im:2.0.9 | bmob-sdk:3.5.6 |
bmob-im:2.1.1 | bmob-sdk:3.6.3 |
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:1.3.0'
}
}
allprojects {
repositories {
jcenter()
//TODO 集成:1.1、配置Bmob的maven仓库地址
maven { url "https://raw.github.com/bmob/bmob-android-sdk/master" }
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
//TODO 集成:1.2、配置IM SDK(bmob-im)版本和Data SDK(bmob-sdk)版本:特定版本的bmob-im依赖特定版本的bmob-sdk
compile 'cn.bmob.android:bmob-im:2.1.1@aar'
compile 'cn.bmob.android:bmob-sdk:3.6.3'
}
"1.0" encoding="utf-8"?>
//权限
"Bmob_APP_KEY"
android:value="fef642bee9678388a478d8b5b25bafa0" />
...
"android.permission.INTERNET" />
"android.permission.ACCESS_NETWORK_STATE" />
"android.permission.CHANGE_NETWORK_STATE" />
"android.permission.WAKE_LOCK" />
"android.permission.READ_PHONE_STATE" />
"android.permission.WRITE_EXTERNAL_STORAGE" />
"android.permission.READ_EXTERNAL_STORAGE" />
"android.permission.CAMERA" />
"android.permission.RECORD_AUDIO" />
"android.permission.VIBRATE" />
"1.0" encoding="utf-8"?>
//权限
1.5、配置IM SDK需要的广播和服务-->
"cn.bmob.newim.core.ConnectChangeReceiver" >
"cn.bmob.action.RECONNECT" />
"android.net.conn.CONNECTIVITY_CHANGE" />
"android.intent.action.BOOT_COMPLETED" />
"android.intent.action.USER_PRESENT" />
"cn.bmob.newim.core.service.BmobIMService"
android:process=":bmobcore" />
"cn.bmob.newim.core.service.NotifyService"
android:process=":bmobcore" />
"cn.bmob.newim.core.service.ReConnectService" />
"cn.bmob.newim.core.service.HeartBeatService" />
...
项目的集成到这儿就结束了,剩下的就是如何使用这些东西了,简单看下我的流程图。
会话
。会话分为暂态会话
和常态会话
。我对会话
的理解:就好比两个好友打电话时,要有一条连通的电话线一样,要实现IM,也要有连通双方的一根线,而这根线就相当于是会话
。应用在本地都会有数据表,用来存储应用中的数据。暂态会话不会保存在本地数据库中;常态会话会被保存到本地数据库中(暂态会话可以用来加好友,常态会话用来聊天)。好友列表:(好友的添加就不说了,自行添加一些好友就行)
会话列表:(当然你的肯定是空的)
首先看下官方的Demo:
adapter.setOnRecyclerViewListener(new OnRecyclerViewListener() {
@Override
public void onItemClick(int position) {
if (position == 0) {//跳转到新朋友页面
startActivity(NewFriendActivity.class, null);
} else {
Friend friend = adapter.getItem(position);
User user = friend.getFriendUser();
BmobIMUserInfo info = new BmobIMUserInfo(user.getObjectId(), user.getUsername(), user.getAvatar());
//TODO 会话:4.1、创建一个常态会话入口,好友聊天
BmobIMConversation conversationEntrance = BmobIM.getInstance().startPrivateConversation(info, null);
Bundle bundle = new Bundle();
bundle.putSerializable("c", conversationEntrance);
Intent intent = new Intent();
intent.setClass(getActivity(), ChatActivity.class);
if (bundle != null) {
intent.putExtra(getActivity().getPackageName(), bundle);
}
getActivity().startActivity(intent);
}
}
由Demo可知,用BmobIMConversation来创建常态会话,传入参数(info,null)
BmobUser.getCurrentUser()
即可得到自己的信息,自己的信息应该默认已经传进去了,不需要我们管;而另一方,也就是通信对方的信息,就需要info传入会话人口了,info为BmobIMUserInfo
类型,需传入(用户id,用户name,用户头像(Url)),其构造和函数: public BmobIMUserInfo(String var1, String var2, String var3) {
this.userId = var1;
this.name = var2;
this.avatar = var3;
}
startPrivateConversation(info, null)
默认为创建常态会话,若要创建暂态会话,可BmobIMConversation conversationEntrance = BmobIM.getInstance().startPrivateConversation(info, true, null);
conversationEntrance
传给会话界面,我的方法是: BmobIMUserInfo info = new BmobIMUserInfo(list.get(i).getObjectId(), list.get(i).getNickName(), list.get(i).getPicture_head().getFileUrl());
BmobIMConversation conversation = BmobIM.getInstance().startPrivateConversation(info, null);
fragmentManager.beginTransaction()
.hide(fragmentManager.findFragmentByTag("text_button_wodeqiuyou"))
.remove(fragmentManager.findFragmentByTag("text_button_wodeqiuyou"))
.add(R.id.fragment_container, FragmentIM.newInstance(list.get(i).getNickName(), list.get(i).getObjectId(), conversation), "im_Layout")
.commit();
意思就是在构造方法中传个参数conversationEntrance
即可,之后在发送消息时会用到。
public static FragmentIM newInstance(String name, String id, BmobIMConversation conversation) {
FragmentIM fragmentIM = new FragmentIM();
fragmentIM.name = name;
fragmentIM.id = id;
fragmentIM.conversation = conversation;
return fragmentIM;
}
private BmobIMConversation mConversationManager;
...
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_im, container, false);
mConversationManager = BmobIMConversation.obtain(BmobIMClient.getInstance(), conversation);
...
return view;
}
btn_chat_send.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String text = editText.getText().toString();//text即发送的消息
if (BmobIM.getInstance().getCurrentStatus().getCode() != ConnectionStatus.CONNECTED.getCode()) {
Toast.makeText(getContext(), "尚未连接IM服务器", Toast.LENGTH_SHORT).show();
} else if (text.trim().equals("")) {
Toast.makeText(getContext(), "请输入内容", Toast.LENGTH_SHORT).show();
} else {
final BmobIMTextMessage message = new BmobIMTextMessage();
message.setContent(text);
//可随意设置额外信息
Map map = new HashMap<>();
map.put("level", "1");
message.setExtraMap(map);
message.setExtra("OK");
mConversationManager.sendMessage(message, listener);
}
}
});
/**
* 消息发送监听器
*/
public MessageSendListener listener = new MessageSendListener() {
@Override
public void onProgress(int value) {
super.onProgress(value);
//文件类型的消息才有进度值
Log.e(TAG, "onProgress: " + value);
}
@Override
public void onStart(BmobIMMessage msg) {
super.onStart(msg);
chatAdapter.addMessage(msg);
editText.setText("");
scrollToBottom();
}
@Override
public void done(BmobIMMessage msg, BmobException e) {
Toast.makeText(getContext(), "发送成功", Toast.LENGTH_SHORT).show();
chatAdapter.notifyDataSetChanged();
editText.setText("");
scrollToBottom();
if (e != null) {
Toast.makeText(getContext(), e.getMessage(), Toast.LENGTH_SHORT).show();
Log.e(TAG, "done: " + e.getMessage());
}
}
};
//TODO 集成:1.6、自定义消息接收器处理在线消息和离线消息
public class DemoMessageHandler extends BmobIMMessageHandler {
@SuppressLint("SimpleDateFormat")
SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
private static final String TAG = "DemoMessageHandler";
@Override
public void onMessageReceive(final MessageEvent event) {
//在线消息
Log.e(TAG, "bindView: getFromId "+event.getMessage().getFromId() );
Log.e(TAG, "bindView: getContent "+event.getMessage().getContent() );
Log.e(TAG, "bindView: getExtra "+event.getMessage().getExtra() );
Log.e(TAG, "bindView: getToId "+event.getMessage().getToId() );
Log.e(TAG, "bindView: getCreateTime "+df.format(event.getMessage().getCreateTime()));
Log.e(TAG, "bindView: getReceiveStatus "+event.getMessage().getReceiveStatus() );
FragmentIM.chatAdapter.addMessage(event.getMessage());
}
@Override
public void onOfflineReceive(final OfflineMessageEvent event) {
//离线消息,每次connect的时候会查询离线消息,如果有,此方法会被调用
Map> map = event.getEventMap();
//挨个检测下离线消息所属的用户的信息是否需要更新
Toast.makeText(MainActivity.context, "有" + map.size() + "个用户发来离线消息", Toast.LENGTH_SHORT).show();
for (Map.Entry> entry : map.entrySet()) {
List list = entry.getValue();
int size = list.size();
Log.e(TAG, "onOfflineReceive: "+"用户" + entry.getKey() + "发来" + size + "条消息" );
for (int i = 0; i < size; i++) {
Log.e(TAG, "bindView: 离线消息: "+i+ " getFromId "+list.get(i).getMessage().getFromId() );
Log.e(TAG, "bindView: 离线消息: "+i+ " getContent "+list.get(i).getMessage().getContent() );
Log.e(TAG, "bindView: 离线消息: "+i+ " getExtra "+list.get(i).getMessage().getExtra() );
Log.e(TAG, "bindView: 离线消息: "+i+ " getToId "+list.get(i).getMessage().getToId() );
Log.e(TAG, "bindView: 离线消息: "+i+ " getCreateTime "+df.format(list.get(i).getMessage().getCreateTime()));
Log.e(TAG, "bindView: 离线消息: "+i+ " getReceiveStatus "+list.get(i).getMessage().getReceiveStatus() );
}
}
}
}
public void addMessage(BmobIMMessage message) {
messageList.addAll(Arrays.asList(message));
notifyDataSetChanged();
FragmentIM.scrollToBottom();//将RecycleView滑动到最低端
}
public void addMessages(List messages) {
messageList.addAll(0, messages);
notifyDataSetChanged();
FragmentIM.scrollToBottom();//将RecycleView滑动到最低端
}
public static void scrollToBottom() {
layoutManager.scrollToPositionWithOffset(chatAdapter.getItemCount() - 1, 0);
}
如此,一个简单的即时通讯Demo就完成了。