Android聊天软件的开发(七)--聊天通信
2014-06-20 22:03
876 查看
聊天通信通过Socket实现,大概的框架如下图:
通信流程:
1.服务器在启动时开启聊天服务线程
可以通过ServletContextListener监听Servlet的初始化和销毁,来开启和关闭聊天服务。
ServiceListener实现ServletContextListener接口
2.客户端发起聊天连接请求(上图步骤1)
客户端通过创建Socket,实现与服务器的聊天通信连接,并且需要通过ChatData.ID类上传用户ID到服务器,用于维护聊天线程。
由于网络不稳定,通信连接容易断开,所以需要在创建连接和发送消息前,判断连接是否断开,如果断开,需要重连。但是Socket的isConnected和isClosed并不能正真判断连接是否断开,因此可通过sendUrgentData发送测试数据,若没有出现异常,则说明连接正常。
ChatConnThread:客户端聊天通信线程
这里有两点需要注意:
a.ObjectOutputStream和ObjectInputStream的创建顺序要与服务器的创建顺序一致。即服务器先创建ObjectInputStream,那么客户端就应该先创建ObjectOutputStream。否则会一直处于阻塞状态。
b.最好将ObjectOutputStream和ObjectInputStream定义成全局,不要多次创建,否则会出现java.io.StreamCorruptedException: invalid
type code: AC异常。
3.服务器接收到连接请求(上图步骤2)
服务器的ServerSocket在监听到客户端的连接请求时,会得到一个Socket对象。服务器首先开启一个聊天线程,来维护该Socket,然后,将来聊天线程以用户ID为标识,进行管理。
ChatConnManager:线程管理器
4.客户端发送消息(上图步骤3,4)
a.客户端通过ChatConnThread的sendMsg方法,将聊天消息(ChatData.MSG)发送到服务器。
ChatConnThread为服务器维护Socket的通信线程。通过in.readObject()获取消息对象后,要判断消息类型。如果是聊天消息,或者添加好友相关的消息,则需要通过ChatConnManager的sendMsg方法将消息发送到目标客户端(上图步骤4);如果是获取离线消息,则使用sendOfflineMsg将离线消息发送给当前Socket对应的客户端。
MsgManager:管理离线消息
5.客户端接收消息(上图步骤5)
客户端ChatConnThread接收到消息后,通过广播进行消息显示或存储。
6.注册广播(以聊天消息为例)
接收聊天消息需要分别在聊天界面和聊天列表界面注册广播。
聊天界面的广播,收到广播后,直接将消息显示在ListView中,属于上述说的情况1.
关于使用Socket实现聊天通信,我之前遇到一个奇怪的问题。当手机连接电脑发出来的共享Wifi,进行聊天通信,服务器可以接收到客户端的消息。但是当服务器将消息发送给目标客户端时,出现了Software caused connection abort: recv failed异常。
如果手机实用移动网络或路由器的Wifi,就不会出现这个问题。
现在还不知道为什么会这样。
首页
Android聊天软件的开发
通信流程:
1.服务器在启动时开启聊天服务线程
可以通过ServletContextListener监听Servlet的初始化和销毁,来开启和关闭聊天服务。
ServiceListener实现ServletContextListener接口
public class ServiceListener implements ServletContextListener { ChatServerThread serverThread = null; public void contextDestroyed(ServletContextEvent arg0) { // 关闭聊天通信线程 if (serverThread != null && !serverThread.isInterrupted()) serverThread.closeServer(); } public void contextInitialized(ServletContextEvent event) { // 开启聊天通信线程 if (serverThread == null) { serverThread = new ChatServerThread(); serverThread.start(); } } }在web.xml中配置监听器
<listener> <listener-class>vaint.wyt.chat.ServiceListener</listener-class> </listener>ChatServerThread:聊天服务线程
public class ChatServerThread extends Thread { static Logger logger = Logger.getLogger(ChatServerThread.class); private ServerSocket ss = null; /** 线程运行终止标识 */ private volatile boolean flag = true; @Override public void run() { logger.info("开启聊天服务"); Socket socket = null; ObjectInputStream in = null; try { ss = new ServerSocket(Constants.SERVER_PORT); } catch (IOException e1) { e1.printStackTrace(); } while (flag) { try { socket = ss.accept(); logger.debug("有一个新的连接"); in = new ObjectInputStream(socket.getInputStream()); // 获得客户端上传的用户ID Object obj = in.readObject(); logger.debug("获取到数据"); if (obj instanceof ChatData.ID) { ChatData.ID id = (ID) obj; String userId = id.getUserId(); logger.debug(userId + "连接到聊天服务器"); // 开启新的线程管理连接 ChatConnThread connThread = new ChatConnThread(userId, socket, in); ChatConnManager.addConnThread(userId, connThread); connThread.start(); } } catch (Exception e) { e.printStackTrace(); // 关闭与客户端的连接。服务器的ServerSocket不要轻易关闭 try { if(in != null) in.close(); if (socket != null) socket.close(); } catch (IOException e1) { e1.printStackTrace(); } } } logger.info("聊天服务结束"); closeSocket(); } /**关闭聊天服务*/ public void closeServer() { flag = false; } private void closeSocket() { if (ss != null) { try { ss.close(); } catch (IOException e) { e.printStackTrace(); } } } }
2.客户端发起聊天连接请求(上图步骤1)
客户端通过创建Socket,实现与服务器的聊天通信连接,并且需要通过ChatData.ID类上传用户ID到服务器,用于维护聊天线程。
由于网络不稳定,通信连接容易断开,所以需要在创建连接和发送消息前,判断连接是否断开,如果断开,需要重连。但是Socket的isConnected和isClosed并不能正真判断连接是否断开,因此可通过sendUrgentData发送测试数据,若没有出现异常,则说明连接正常。
ChatConnThread:客户端聊天通信线程
public class ChatConnThread extends Thread{ private static final String TAG = ChatConnThread.class.getSimpleName(); private Context mContext; private Socket socket; //输入输出流保持唯一,不要多次创建,避免错误 private ObjectOutputStream out; private ObjectInputStream in; /**线程运行终止标识*/ private volatile boolean flag = true; public ChatConnThread(Context ctx) { mContext = ctx; } /**发送消息*/ public void sendMsg(ChatData.MSG msg) { try { //检查是否断开连接 checkConnSocket(); out.writeObject(msg);// 发送消息 out.flush(); } catch (IOException e) { e.printStackTrace(); } } @Override public void run() { //接收消息 while(flag) { try { //如果socket已经关闭,则需要重新连接 checkConnSocket(); Object obj = in.readObject(); if(obj instanceof ChatData.MSG)//聊天消息或添加好友请求 { //通知栏提示 TipsUtils.MsgNotificaction(); ChatData.MSG msg = (ChatData.MSG)obj; switch (msg.getType()) { case CHATTING: //显示消息 MsgUtils.ShowChattingMsg(mContext, msg); break; case ADD_FRIEND: //显示好友请求消息 MsgUtils.ShowAddFriendMsg(mContext, msg); break; case ADD_AGREE: //更新好友列表 MsgUtils.AddNewFriend(mContext, msg); break; } } Thread.sleep(250); } catch (Exception e) { e.printStackTrace(); //关闭Socket,让其重新连接 closeSocket(); } } //线程结束,关闭Socket closeSocket(); Log.d(TAG, " 连接线程已经关闭"); } /**关闭Socket连接*/ private void closeSocket() { try { if(socket != null) { out.close(); in.close(); socket.close(); socket = null; Log.d(TAG, "关闭Socket"); } } catch (IOException e) { e.printStackTrace(); } } /**检查连接,如果断开需要重新连接*/ public synchronized void checkConnSocket() { while( flag && !isOnLine()) { try { Log.d(TAG, CacheUtils.GetUserId() + " 创建连接"); socket = new Socket(Constants.SERVER_IP, Constants.SERVER_PORT); socket.setKeepAlive(true); out = new ObjectOutputStream(socket.getOutputStream()); //通过ID号建立与服务器的连接 ChatData.ID id = new ChatData.ID(); id.setUserId(CacheUtils.GetUserId()); Log.d(TAG, "发送连接聊天服务的请求"); out.writeObject(id); out.flush(); //输入流的创建要在输出流写过数据之后。不然服务器和客户端都会阻塞 in = new ObjectInputStream(socket.getInputStream()); Thread.sleep(250); }catch (Exception e) { e.printStackTrace(); } } } /**关闭连接线程*/ public void closeConn() { flag = false; } /**判断客户端连接*/ public boolean isOnLine() { if(socket==null) return false; boolean ret = true; try{ /* * 发送测试数据 * 往输出流发送一个字节的数据,只要对方Socket的SO_OOBINLINE属性没有打开, * 就会自动舍弃这个字节,而SO_OOBINLINE属性默认情况下就是关闭的 */ //心跳测试 socket.sendUrgentData(0xFF); }catch(Exception e){ Log.d(TAG, CacheUtils.GetUserId()+"连接已经断开了"); ret = false; closeSocket(); } return ret; } }
这里有两点需要注意:
a.ObjectOutputStream和ObjectInputStream的创建顺序要与服务器的创建顺序一致。即服务器先创建ObjectInputStream,那么客户端就应该先创建ObjectOutputStream。否则会一直处于阻塞状态。
b.最好将ObjectOutputStream和ObjectInputStream定义成全局,不要多次创建,否则会出现java.io.StreamCorruptedException: invalid
type code: AC异常。
3.服务器接收到连接请求(上图步骤2)
服务器的ServerSocket在监听到客户端的连接请求时,会得到一个Socket对象。服务器首先开启一个聊天线程,来维护该Socket,然后,将来聊天线程以用户ID为标识,进行管理。
ChatConnManager:线程管理器
public class ChatConnManager { static Logger logger = Logger.getLogger(ChatConnManager.class); // <用户ID,连接线程> private static Map<String, ChatConnThread> connManager = new HashMap<String, ChatConnThread>(); /** 添加客户端通信线程 */ public static void addConnThread(String id, ChatConnThread connThread) { logger.info(id+"创建通信连接"); //如果连接已经存在,则需要断开之前的连接 ChatConnThread conn = connManager.remove(id); if (conn != null)// 如果id不存在则返回null conn.closeConn();// 终止线程 connManager.put(id, connThread); } /** 移除客户端通信线程 */ public static void removeConnThread(String id) { ChatConnThread connThread = connManager.remove(id); if (connThread != null)// 如果id不存在则返回null connThread.closeConn();// 终止线程 } /** 给指定客户端发送消息 */ public static void sendMsg(ChatData.MSG msg) { String toId = msg.getToId(); ChatConnThread connThread = connManager.get(toId); // 判断接收方是否在线 if (connThread != null && connThread.isOnLine())// 接收方在线 { // 发送消息 connThread.sendMsg(msg); } else if (connThread != null && !connThread.isOnLine())// 接收方断开连接 { removeConnThread(toId); // 缓存消息 MsgManager.addMsgCache(toId, msg); }else// 接收方不在线 { // 缓存消息 MsgManager.addMsgCache(toId, msg); } } }
4.客户端发送消息(上图步骤3,4)
a.客户端通过ChatConnThread的sendMsg方法,将聊天消息(ChatData.MSG)发送到服务器。
public void sendMsg(ChatData.MSG msg) { try { //检查是否断开连接 checkConnSocket(); out.writeObject(msg);// 发送消息 out.flush(); } catch (IOException e) { e.printStackTrace(); } }b.服务器对应Socket接收到数据后,先判断消息类型,然后再处理。
ChatConnThread为服务器维护Socket的通信线程。通过in.readObject()获取消息对象后,要判断消息类型。如果是聊天消息,或者添加好友相关的消息,则需要通过ChatConnManager的sendMsg方法将消息发送到目标客户端(上图步骤4);如果是获取离线消息,则使用sendOfflineMsg将离线消息发送给当前Socket对应的客户端。
public class ChatConnThread extends Thread { static Logger logger = Logger.getLogger(ChatConnThread.class); private Socket socket; private ObjectOutputStream out; private ObjectInputStream in; private String userId; /**线程运行终止标识*/ private volatile boolean flag = true; public ChatConnThread(String id, Socket s, ObjectInputStream ois) throws IOException { userId = id; socket = s; in = ois; out = new ObjectOutputStream(socket.getOutputStream()); } /**发送消息给本客户端*/ public void sendMsg(ChatData.MSG msg) { try { out.writeObject(msg);// 发送消息 out.flush(); } catch (IOException e) { e.printStackTrace(); } } /**发送离线消息给本客户端*/ public void sendOfflineMsg(String userId) { // 发送离线消息给客户端 List<MSG> list = MsgManager.getOfflineMsg(userId); if (list == null) return; for (int i = 0; i < list.size(); i++) { sendMsg(list.get(i)); } // 清除对应离线消息 MsgManager.removeMsgCache(userId); } @Override public void run() { while (flag) { try { Object obj = in.readObject(); if (obj instanceof ChatData.MSG) { ChatData.MSG msg = (MSG) obj; // 判断消息类型 switch (msg.getType()) { case CHATTING:// 普通消息 logger.debug(msg.getFromId() + " 发送消息给 " + msg.getToId()); // 发送消息给对应客户端 ChatConnManager.sendMsg(msg); break; case OFFLINE_MSG:// 获取离线消息 logger.debug(msg.getFromId() + "获取离线消息"); sendOfflineMsg(msg.getFromId()); break; case ADD_FRIEND:// 添加好友请求 logger.debug(msg.getFromId() + " 发起添加 " + msg.getToId() + " 的好友请求"); // 发送消息给对应客户端 ChatConnManager.sendMsg(msg); break; case ADD_AGREE:// 同意添加好友 logger.debug(msg.getFromId() + " 同意添加 " + msg.getToId() + " 为好友"); FriendsUtils.AddFriend(msg.getFromId(), msg.getToId()); ChatConnManager.sendMsg(msg); break; case LOGOUT:// 注销登录 logger.debug(msg.getFromId() + " 退出登录"); ChatConnManager.removeConnThread(msg.getFromId()); break; } } } catch (Exception e) { e.printStackTrace(); logger.debug(userId+" 通信线程出现异常"); //出现异常,退出循环。即断开连接线程 flag = false; break; } } closeSocket(); } /**关闭客户端连接线程*/ public void closeConn() { flag = false; } /**关闭Socket连接*/ private void closeSocket() { try { if(socket != null) { out.close(); in.close(); socket.close(); socket = null; } } catch (IOException e) { e.printStackTrace(); } } /**判断客户端连接*/ public boolean isOnLine() { boolean ret = true; try{ /* * 发送测试数据 * 往输出流发送一个字节的数据,只要对方Socket的SO_OOBINLINE属性没有打开, * 就会自动舍弃这个字节,而SO_OOBINLINE属性默认情况下就是关闭的 */ socket.sendUrgentData(0xFF); }catch(Exception e){ logger.debug(userId + " 掉线了"); ret = false; } return ret; } }ChatConnManager的sendMsg方法:发送消息前,需要判断该客户端的连接是否断开,如果断开,则将消息作为离线消息进行缓存。
public static void sendMsg(ChatData.MSG msg) { String toId = msg.getToId(); ChatConnThread connThread = connManager.get(toId); // 判断接收方是否在线 if (connThread != null && connThread.isOnLine())// 接收方在线 { // 发送消息 connThread.sendMsg(msg); } else if (connThread != null && !connThread.isOnLine())// 接收方断开连接 { removeConnThread(toId); // 缓存消息 MsgManager.addMsgCache(toId, msg); }else// 接收方不在线 { // 缓存消息 MsgManager.addMsgCache(toId, msg); } }
MsgManager:管理离线消息
public class MsgManager { static Logger logger = Logger.getLogger(MsgManager.class); // <用户ID,消息序列(旧-新)> private static Map<String, List<ChatData.MSG>> msgManager = new HashMap<String, List<ChatData.MSG>>(); /** 添加离线消息缓存 */ public static void addMsgCache(String id, ChatData.MSG msg) { List<ChatData.MSG> list; if (msgManager.containsKey(id))// 原先存在离线消息 { list = msgManager.get(id); list.add(msg); } else { list = new ArrayList<ChatData.MSG>(); list.add(msg); } logger.debug("ID:"+id+"有一条离线消息:"+msg.getMsg()); msgManager.put(id, list); } /** 获得离线消息序列 */ public static List<ChatData.MSG> getOfflineMsg(String id) { return msgManager.get(id); } /** 移除消息序列 */ public static void removeMsgCache(String id) { msgManager.remove(id); } }
5.客户端接收消息(上图步骤5)
客户端ChatConnThread接收到消息后,通过广播进行消息显示或存储。
while(flag) { try { //如果socket已经关闭,则需要重新连接 checkConnSocket(); Object obj = in.readObject(); if(obj instanceof ChatData.MSG)//聊天消息或添加好友请求 { //通知栏提示 TipsUtils.MsgNotificaction(); ChatData.MSG msg = (ChatData.MSG)obj; switch (msg.getType()) { case CHATTING: //显示消息 MsgUtils.ShowChattingMsg(mContext, msg); break; case ADD_FRIEND: //显示好友请求消息 MsgUtils.ShowAddFriendMsg(mContext, msg); break; case ADD_AGREE: //更新好友列表 MsgUtils.AddNewFriend(mContext, msg); break; } } Thread.sleep(250); } catch (Exception e) { e.printStackTrace(); //关闭Socket,让其重新连接 closeSocket(); } }MsgUtils:发送消息的工具类,也用于广播消息。广播消息时,需要判断当前的状态。比如广播聊天消息有三种状态:1.处于该好友的会话界面:直接显示消息。2.处于聊天列表界面:更新聊天列表和存储消息。3.其他界面:存储消息,回来聊天列表界面进行显示。
public class MsgUtils { private static ChatConnThread connThread; private static String currBroadcast; /**设置连接线程*/ public static void SetConnThread(ChatConnThread thread) { connThread = thread; } /**获得离线消息*/ public static void GetOfflineMsg() { ChatData.MSG msg = new ChatData.MSG(); msg.setType(ChatData.Type.OFFLINE_MSG); msg.setFromId(CacheUtils.GetUserId()); connThread.sendMsg(msg); } /**发送消息给好友,或服务端*/ public static void SendMsg(ChatData.MSG msg) { connThread.sendMsg(msg); } /**关闭连接线程*/ public static void CloseConn() { //只有当连接没有断开的情况下,才通知服务端 if(connThread.isOnLine()) { Log.d("vaint", "通知服务器,关闭连接"); MSG msg = new MSG(); msg.setFromId(CacheUtils.GetUserId()); msg.setType(Type.LOGOUT); SendMsg(msg); } connThread.closeConn(); } /*****************************消息显示********************************/ /* * 显示接收的消息 * 有三种情况 * 1.处于该好友的会话界面:直接显示消息 * 2.处于聊天列表界面:更新聊天列表 * 3.其他界面:存储消息,回来聊天列表界面进行显示 * */ /**显示聊天消息*/ public static void ShowChattingMsg(Context ctx, MSG msg) { //判断当前处于那种情况 if(msg.getFromId().equals(currBroadcast))//情况1,该好友的会话界面 { //发送广播 Intent intent = new Intent(Constants.Actions.CHATTING_PREFIX + msg.getFromId()); intent.putExtra(Constants.Flags.MSG, msg); ctx.sendBroadcast(intent); return; } String friendId = msg.getFromId(); User friend = CacheUtils.GetFriend(friendId); { //聊天记录 ChattingModel model = new ChattingModel(); model.setPhoto(friend.getPhoto()); model.setSend(false); model.setMsg(msg.getMsg()); model.setTime(msg.getTime()); model.setShowTime(true);//设置显示时间 //添加一条聊天记录 DataUtils.AddChattingItem(friendId, model, true); } { //聊天列表 ChatListModel model = new ChatListModel(); model.setPhoto(friend.getPhoto()); model.setTitle(friend.getName()); model.setUnread(1); model.setFriendId(friendId); model.setContent(msg.getMsg()); model.setTime(msg.getTime()); //更新聊天列表 DataUtils.UpdateChatList(friendId, model); } if(Constants.Actions.CHAT_LIST.equals(currBroadcast))//情况2,处于聊天列表界面 { //发送广播 Intent intent = new Intent(Constants.Actions.CHAT_LIST); ctx.sendBroadcast(intent); } //情况3,其他界面 } /* * 显示添加好友的消息 * 有三种情况 * 1.处于新的好友的界面:直接显示消息 * 2.处于好友列表界面:显示提示 * 3.其他界面:存储消息 * */ /**显示好友请求消息*/ public static void ShowAddFriendMsg(Context ctx, MSG msg) { NewFriendsModel model = new NewFriendsModel(); model.setPhoto(msg.getPhoto()); model.setName(msg.getName()); model.setUserId(msg.getFromId()); model.setVerifyMsg(msg.getMsg()); model.setTime(msg.getTime()); model.setAgreed(false);//好友请求,设置为未同意添加 List<NewFriendsModel> list = CacheUtils.GetNewFriendsList(); //判断原来是否有该好友的请求 for(int i=0;i<list.size();i++) { NewFriendsModel m = list.get(i); if(m.getUserId().equals(msg.getFromId()))//如果存在 { //判断是否已经同意其请求 if(!m.isAgreed())//如果之前没有同意,才再次覆盖显示,否则不再更新显示 { list.remove(i); break; } } } list.add(0, model);// 添加到首位 //更新好友请求列表 DataUtils.UpdateNewFriendsList(list); if(Constants.Actions.NEW_FRIEND_LIST.equals(currBroadcast))//情况1.处于新的好友的界面 { //发送广播 Intent intent = new Intent(Constants.Actions.NEW_FRIEND_LIST); intent.putExtra(Constants.Flags.NEW_FRIEND_LIST, model); ctx.sendBroadcast(intent); } else if(Constants.Actions.CONTACTS_LIST.equals(currBroadcast))//情况2.处于好友列表界面 { // 发送广播 Intent intent = new Intent(Constants.Actions.NEW_FRIEND_TIPS); ctx.sendBroadcast(intent); } //情况3.其他界面 } /**添加一个新的好友<BR/>并且产生一个空白的聊天会话*/ public static void AddNewFriend(Context ctx, MSG msg) { //更新好友列表 FriendList friendList = CacheUtils.GetFriendList(); List<User> friends = friendList.getFriends(); //判断该好友是否已经在通讯录中 for(int i=0;i<friends.size();i++) { User user = friends.get(i); if(user.getUserId().equals(msg.getFromId()))//已经存在 { return; } } User user = new User(); user.setPhoto(msg.getPhoto()); user.setName(msg.getName()); user.setUserId(msg.getFromId()); friends.add(user); friendList.setFriends(friends); //更新好友列表 DataUtils.UpdateFriendList(friendList); //聊天列表 ChatListModel model = new ChatListModel(); model.setUnread(1); model.setFriendId(msg.getFromId()); model.setPhoto(msg.getPhoto()); model.setTitle(msg.getName()); model.setTime(MsgUtils.GetFormatTime()); model.setContent(ctx.getString(R.string.txt_say_hi)); //更新聊天列表 DataUtils.UpdateChatList(msg.getFromId(), model); if(Constants.Actions.CONTACTS_LIST.equals(currBroadcast))//处于好友列表界面 { // 发送广播 Intent intent = new Intent(Constants.Actions.CONTACTS_LIST); ctx.sendBroadcast(intent); }else if(Constants.Actions.CHAT_LIST.equals(currBroadcast))//处于聊天列表界面 { // 发送广播 Intent intent = new Intent(Constants.Actions.CHAT_LIST); ctx.sendBroadcast(intent); } } /**存储当前的Broadcast*/ public static void SetCurrBroadCast(String broadcastName) { currBroadcast = broadcastName; } /**清除当前的Broadcast*/ public static void ClearCurrBroadCast() { currBroadcast = null; } /**获得MM-dd HH:mm格式的当前时间*/ public static String GetFormatTime() { long currTime = System.currentTimeMillis(); SimpleDateFormat formatter = new SimpleDateFormat("MM-dd HH:mm"); Date curDate = new Date(currTime);// 获取当前时间 String time = formatter.format(curDate); return time; } }
6.注册广播(以聊天消息为例)
接收聊天消息需要分别在聊天界面和聊天列表界面注册广播。
聊天界面的广播,收到广播后,直接将消息显示在ListView中,属于上述说的情况1.
class MsgBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context ctx, Intent intent) { MSG msg = (MSG) intent.getSerializableExtra(Constants.Flags.MSG); ChattingModel model = new ChattingModel(); User friend = CacheUtils.GetFriend(msg.getFromId()); model.setPhoto(friend.getPhoto()); model.setMsg(msg.getMsg()); model.setSend(false); model.setTime(msg.getTime()); long currTime = System.currentTimeMillis(); model.setShowTime((currTime - lastTime) > 60000);//间隔超过60秒,则显示消息的时间 lastTime = currTime; //记录聊天记录 mNewChattingList.add(model); mChattingList.add(model); //显示消息 mAdapter = new ChattingAdapter(mContext, mChattingList); mListView.setAdapter(mAdapter); } }聊天列表的广播,接收到广播后,以未读消息的形式提示用户,属于上述说的情况2。由于情况2在发送广播前,先存储了聊天记录,以及更新的聊天列表数据,因此这里只需要更新聊天列表的显示即可。
class MsgBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context ctx, Intent intent) { updateChatList(); } } public void updateChatList() { mSourceDateList = DataUtils.GetChatList(); mAdapter.updateListView(mSourceDateList); }
关于使用Socket实现聊天通信,我之前遇到一个奇怪的问题。当手机连接电脑发出来的共享Wifi,进行聊天通信,服务器可以接收到客户端的消息。但是当服务器将消息发送给目标客户端时,出现了Software caused connection abort: recv failed异常。
如果手机实用移动网络或路由器的Wifi,就不会出现这个问题。
现在还不知道为什么会这样。
首页
Android聊天软件的开发
相关文章推荐
- Android聊天软件的开发--聊天通信
- Android软件开发之应用程序之间的通信介绍(十八)
- Android聊天软件的开发(四)--通讯录
- Android聊天软件的开发
- android聊天软件开发,实战篇(2)
- Android聊天软件的开发
- 【代码】android软件开发 Service Binder交互通信实例
- Android聊天软件的开发(五)--头像设置
- Android聊天软件的开发(二)--数据库
- Android软件开发之应用程序之间的通信介绍(十八)
- Android软件开发之应用程序之间的通信介绍
- Android软件开发之应用程序之间的通信介绍(九)
- Android聊天软件的开发(六)--表情
- android聊天软件开发,实战篇(1)
- 适用于Android开发的简单聊天软件
- Android开发之基于MINA框架的聊天通信功能实现
- Android软件开发之应用程序之间的通信介绍(九)
- Android软件开发之应用程序之间的通信介绍
- Android聊天软件的开发(一)--预备知识