最近在做个项目,需要用到即时通信的功能,选定了环信的IM云来实现,关于环信IM的SDK接口,自己研究了一天,基本的功能实现了一下,这里PO出来demo。
因为只是为了验证sdk的实现,所以界面设计的比较丑,还请谅解。还有,只用到了文本消息,所以选取的sdk版本是2.X。
具体的下载链接见文末。
先上图
首先是一张消息列表,列表中的项目包括用户头像、用户名和最后一条消息显示。
第二张是会话,简单点来写,每一个项的list view的adapter都没有设置,只是简单的显示消息。点击button向对方发送“呵呵呵”的消息。
下面来阐述详细的实现。
step 1: 在manifest文件中添加users-permission 和appkey的service
这里的permission内容可以从环信文档中获取,大致如下
<!-- Required --> <uses-permission android:name="android.permission.VIBRATE" /> <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.RECORD_AUDIO" /> <uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.GET_TASKS" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" /> <uses-permission android:name="android.permission.WAKE_LOCK" /> <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" /> <uses-permission android:name="android.permission.READ_PHONE_STATE" />
<!-- 设置环信应用的appkey --> <meta-data android:name="EASEMOB_APPKEY" android:value="easemob-demo#chatdemoui" /> <!-- 声明sdk所需的service SDK核心功能--> <service android:name="com.easemob.chat.EMChatService" />
step 2: 环信账号登录
在登录之前,要在主的application文件中加上一条语句:
EMChat.getInstance().init(this);保证app在首次打开时就初始化EMChat实例
然后,登录环信账号,这里需要用到当前的appkey注册到的账号和密码,我自己注册的测试用例的登录过程如下:
package com.example.demo; import com.easemob.EMCallBack; import com.easemob.chat.EMChatManager; import android.app.Activity; import android.content.Intent; import android.os.Bundle; import android.widget.Toast; public class MainActivity extends Activity { public static final String UA = "blyang"; public static final String PA = "bing"; public static final String UB = "yangbingliang"; public static final String PB = "bing"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); login(UA, PA); } private void login(String userName, String password){ EMChatManager.getInstance().login(userName, password, new EMCallBack(){ @Override public void onError(int arg0, String arg1) { Toast.makeText(MainActivity.this, "error", Toast.LENGTH_LONG).show(); } @Override public void onProgress(int arg0, String arg1) { // TODO Auto-generated method stub } @Override public void onSuccess() { // TODO Auto-generated method stub runOnUiThread(new Runnable(){ @Override public void run() { Toast.makeText(MainActivity.this, "success", Toast.LENGTH_LONG).show(); } }); //从本地DB加载到程序中 EMChatManager.getInstance().loadAllConversations(); startActivity(new Intent(MainActivity.this, Container.class)); finish(); } }); } }
//从本地DB加载到程序中 EMChatManager.getInstance().loadAllConversations();是为了保证从本地数据库中取出历史所有的conversation对象并加载出来
step 3: 将历史对话数据加载成消息列表的形式
首先创建一个conversationList 的List对象,然后将最近的所有会话添加到这个list中,最后,调用消息列表展示到adapter,将数据传入并展示出来。具体到代码和注释如下:
package com.example.demo; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.Hashtable; import java.util.List; import com.easemob.chat.EMChatManager; import com.easemob.chat.EMConversation; import android.app.Fragment; import android.content.Intent; import android.os.Bundle; import android.util.Log; import android.util.Pair; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.ListView; public class ChatHistoryFragment extends Fragment{ //所有的会话列表 private List<EMConversation> conversationList = new ArrayList<EMConversation>(); ChatHistoryAdapter adapter ; ListView chatHistoryListView; View view; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { view = inflater.inflate(R.layout.fragment_conversation_history, container, false); return view; } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); conversationList.addAll(loadConversationWithRecentChat()); chatHistoryListView = (ListView) view.findViewById(R.id.list); adapter = new ChatHistoryAdapter(getActivity(), conversationList); chatHistoryListView.setAdapter(adapter); chatHistoryListView.setOnItemClickListener(new OnItemClickListener(){ @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { EMConversation conversation = (EMConversation) adapter.getItem(position); Intent intent = new Intent(getActivity(), Conversation.class); intent.putExtra("userName", conversation.getUserName()); startActivity(intent); } }); int count = 0; for(int i=0; i<conversationList.size(); i++){ count += conversationList.get(i).getUnreadMsgCount(); } Log.v("count total", count+""); } /** * 获取所有会话 * @return */ private Collection<? extends EMConversation> loadConversationWithRecentChat() { Hashtable<String, EMConversation> conversations = EMChatManager .getInstance().getAllConversations(); List<Pair<Long, EMConversation>> sortList = new ArrayList<Pair<Long, EMConversation>>(); synchronized(conversations){ for(EMConversation conversation : conversations.values()){ if(conversation.getAllMessages().size() != 0){ sortList.add(new Pair<Long, EMConversation> (conversation.getLastMessage().getMsgTime(), conversation) ); } } } try{ sortConversationByLastChatTime(sortList); }catch(Exception e){ e.printStackTrace(); } List<EMConversation> list = new ArrayList<EMConversation>(); for(Pair<Long, EMConversation> sortItem : sortList){ list.add(sortItem.second); } return list; } /** * 根据最后一条消息的时间排序 * @param sortList */ private void sortConversationByLastChatTime( List<Pair<Long, EMConversation>> sortList) { Collections.sort(sortList, new Comparator<Pair<Long, EMConversation>>(){ @Override public int compare(Pair<Long, EMConversation> con1, Pair<Long, EMConversation> con2) { if(con1.first == con2.first){ return 0; }else if(con2.first > con1.first){ return 1; }else{ return -1; } } }); } public void refresh(){ conversationList.clear(); conversationList.addAll(loadConversationWithRecentChat()); if(adapter != null){ adapter.notifyDataSetChanged(); } } @Override public void onResume() { super.onResume(); refresh(); } }
step 4:进入会话列表
当消息列表展示之后,要在每一个listitem被点击之后进入具体的会话展示区,也就是上图二。
在会话区,首先要注册一个广播监听器,用于接收信息(注意,如果不注册,是无法实时接收信息的)。然后当接收到信息的时候,刷新页面。在离开会话页面的时候,销毁这个广播注册即可。具体的代码和注释如下:
package com.example.demo; import com.easemob.EMCallBack; import com.easemob.chat.EMChat; import com.easemob.chat.EMChatManager; import com.easemob.chat.EMConversation; import com.easemob.chat.EMMessage; import com.easemob.chat.EMMessage.ChatType; import com.easemob.chat.TextMessageBody; import android.app.Activity; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.Bundle; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.Button; import android.widget.ListView; import android.widget.TextView; public class Conversation extends Activity{ ListView listView; Button button; private EMConversation conversation; private ConversationAdapter adapter; NewMessageBroadcastReceiver msgReceiver; //广播监听器,用于接收消息 @Override protected void onCreate(Bundle savedInstanceState) { // TODO Auto-generated method stub super.onCreate(savedInstanceState); setContentView(R.layout.conversation); listView = (ListView) findViewById(R.id.conversation_list); button = (Button) findViewById(R.id.conversation_btn); conversation = EMChatManager.getInstance() .getConversation(getIntent().getExtras().get("userName").toString()); adapter = new ConversationAdapter(Conversation.this, conversation); listView.setAdapter(adapter); listView.setSelection(listView.getCount() - 1 ); registerBroadcast(); //注册广播接收消息 EMChat.getInstance().setAppInited(); //注册完广播之后,一定要声明这一句 sendMsg(); //发送文本消息 } /** * 注册一个接收消息的广播 */ private void registerBroadcast(){ //只有注册了广播才能接收到新消息,目前离线消息,在线消息都是走接收消息的广播(离线消息目前无法监听,在登录以后,接收消息广播会执行一次拿到所有的离线消息) msgReceiver = new NewMessageBroadcastReceiver(); IntentFilter intentFilter = new IntentFilter(EMChatManager.getInstance().getNewMessageBroadcastAction()); intentFilter.setPriority(3); registerReceiver(msgReceiver, intentFilter); // this.unregisterReceiver(msgReceiver); } private class NewMessageBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { // 注销广播 abortBroadcast(); // 消息id(每条消息都会生成唯一的一个id,目前是SDK生成) String msgId = intent.getStringExtra("msgid"); //发送方 String username = intent.getStringExtra("from"); // 收到这个广播的时候,message已经在db和内存里了,可以通过id获取mesage对象 EMMessage message = EMChatManager.getInstance().getMessage(msgId); if (!username.equals(username)) { // 消息不是发给当前会话,return return; } conversation.addMessage(message); adapter.notifyDataSetChanged(); listView.setAdapter(adapter); listView.setSelection(listView.getCount() - 1); } } /** * 发送文本消息 */ private void sendMsg(){ button.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { //获取到与聊天人的会话对象。参数username为聊天人的userid或者groupid,后文中的username皆是如此 EMConversation conversation = EMChatManager.getInstance().getConversation(MainActivity.UB); //创建一条文本消息 EMMessage message = EMMessage.createSendMessage(EMMessage.Type.TXT); //设置消息body TextMessageBody txtBody = new TextMessageBody("呵呵呵"); message.addBody(txtBody); //设置接收人 message.setReceipt(MainActivity.UB); //把消息加入到此会话对象中 conversation.addMessage(message); adapter.notifyDataSetChanged(); listView.setAdapter(adapter); listView.setSelection(listView.getCount() - 1); //发送消息 EMChatManager.getInstance().sendMessage(message, new EMCallBack(){ @Override public void onError(int arg0, String arg1) { } @Override public void onProgress(int arg0, String arg1) { } @Override public void onSuccess() { }}); } }); } @Override protected void onDestroy() { super.onDestroy(); /** * 当前Activity销毁的时候,标记所有信息为已读 * 销毁广播 */ conversation.markAllMessagesAsRead(); this.unregisterReceiver(msgReceiver); } }
http://download.csdn.net/detail/sinat_22013331/9471469