zoukankan      html  css  js  c++  java
  • Android之高仿手机QQ聊天

    源代码下载

    转载请注明出处,谢谢!

             最终版已上传。优化下拉刷新、增加来消息声音提示、主界面改成ViewPager,实现左右滑动、新增群组、最近会话显示条数,开始上班了,不再修改了。谢谢!

            国庆这几天,闲着无聊,仿照QQ2012,做了一个基于socket的聊天工具,由于代码比较多,今天就不在文章中贴出代码,需要的朋友可以点击上面下载,谢谢,后续会详细贴出各模块代码,并解释,敬请期待,O(∩_∩)O,有什么问题或者bug,欢迎给我留言。

            首先说一下我的整体思路:整个聊天是通过服务器转发的,这样处理起来比较简单,但是服务器压力会特别大。建议在真正做项目的时候,服务器只处理用户注册、登录以及判断用户是否掉线等,至于聊天、传文件以及视频等就在用户之间单独建立连接,这样可以大大减少服务器的压力,我这里就没考虑这么多了。

            第一:我们定义一个超级消息对象(记得要序列化),它包含:消息类型、具体的消息对象、发送给谁以及来自谁。服务器和客户端就是通过发送这个超级消息对象来进行通讯的。

            第二:服务器,在接受用户连接之后,马上把socket丢入线程池中,这样可以支持多用户并发访问,然后根据用户的socket对象,分别建立一条读消息线程写消息线程在这里,写消息线程要先建立,我们需要传递给读消息线程,因为我们在读完消息之后会给用户回复消息)。在读消息线程里面根据消息类型处理超级消息对象,分别是:注册、登录、下线、转发消息、文件、刷新好友列表等。后台数据库处理的话,我们通过dao模式,这样很方便,而且会使代码显得简洁、明了、有条理,总之是各种好,哈哈。最后要注意一点:因为我们是转发消息,所以在用户登录成功后,我们需要把该用户的写消息线程根据用户的ID存入一个Map中,以便在转发消息的时候,可以根据用户ID取出对应的写消息线程,从而实现转发消息。

           第三:客户端,跟服务器类似,只是没有线程池,在用户连接上服务器之后,也是根据连接后的socket对象,分别建立一条读消息线程写消息线程然后在代码中哪里需要发消息,就通过get方法获取写消息线程,哪里需要读消息,就通过get方法获取读消息线程

            第四:关于写消息线程处理,因为服务器或者客户端,不可能时时需要写消息,因此我们如果用一个死循环去处理写线程,明显的是不明智的,因此我做了一个简单的处理,在写消息的死循环中先wait(),当我们调用写消息线程的setMessage方法后,就notify唤醒写线程,发送完消息之后,继续wait(),这里我贴出核心代码:

    [java] view plaincopy
    1. <span style="font-size:14px;"public void setMsg(TranObject msg) {  
    2.         this.msg = msg;  
    3.         synchronized (this) {  
    4.             notify();  
    5.         }  
    6.     }  
    7.   
    8.     @Override  
    9.     public void run() {  
    10.         try {  
    11.             while (isStart) {  
    12.                 if (msg != null) {  
    13.                     oos.writeObject(msg);  
    14.                     oos.flush();  
    15.                     if (msg.getType() == TranObjectType.LOGOUT) {// 如果是发送下线的消息,就直接跳出循环  
    16.                         break;  
    17.                     }}  
    18.                     synchronized (this) {  
    19.                         wait();// 发送完消息后,线程进入等待状态  
    20.                     }  
    21.                   
    22.             }  
    23.             oos.close();// 循环结束后,关闭输出流和socket  
    24.             if (socket != null)  
    25.                 socket.close();  
    26.         } catch (InterruptedException e) {  
    27.             e.printStackTrace();  
    28.         } catch (IOException e) {  
    29.             e.printStackTrace();  
    30.         }  
    31.     }</span>  


            第五:具体手机客户端的处理,因为android有自己的特点,也有自己的优势,所以我们要充分利用它的优势,避开他的缺点来处理消息,我说一下我在这个小项目中处理消息的思路:我在用户启动程序的时候,开启一个获取消息的service,在该service中建立连接,然后通过一个接口去监听读消息线程收到的消息,在收到消息的同时,把该超级消息对象通过广播发送出去然后自定义一个抽象的MyActivity继承Activity,在MyActivity里面通过一个广播接收者接收service中发送过来的消息,并通过一个抽象方法传递给子Activity,我们的其他activity如果要处理收消息,就可以继承我们自定义的MyActivity,然后实现那个抽象方法,就可以了这样很好的处理了不同的activity接收消息的缺点,而且后台处理也很方便,我不知道腾讯QQ在这个方面是怎么处理的,这是我个人的想法而已。下面贴出MyActivity的代码:

    [java] view plaincopy
    1. <span style="font-size:14px;">/** 
    2.  * 自定义一个抽象的MyActivity类,每个Activity都继承他,实现消息的接收(优化性能,减少代码重复) 
    3.  *  
    4.  * @author way 
    5.  *  
    6.  */  
    7. public abstract class MyActivity extends Activity {  
    8.     /** 
    9.      * 广播接收者,接收GetMsgService发送过来的消息 
    10.      */  
    11.     private BroadcastReceiver MsgReceiver = new BroadcastReceiver() {  
    12.   
    13.         @Override  
    14.         public void onReceive(Context context, Intent intent) {  
    15.             TranObject msg = (TranObject) intent  
    16.                     .getSerializableExtra(Constants.MSGKEY);  
    17.             if (msg != null) {//如果不是空,说明是消息广播  
    18.                 // System.out.println("MyActivity:" + msg);  
    19.                 getMessage(msg);// 把收到的消息传递给子类  
    20.             } else {//如果是空消息,说明是关闭应用的广播  
    21.                 close();  
    22.             }  
    23.         }  
    24.     };  
    25.   
    26.     /** 
    27.      * 抽象方法,用于子类处理消息, 
    28.      *  
    29.      * @param msg 
    30.      *            传递给子类的消息对象 
    31.      */  
    32.     public abstract void getMessage(TranObject msg);  
    33.   
    34.     /** 
    35.      * 子类直接调用这个方法关闭应用 
    36.      */  
    37.     public void close() {  
    38.         Intent i = new Intent();  
    39.         i.setAction(Constants.ACTION);  
    40.         sendBroadcast(i);  
    41.         finish();  
    42.     }  
    43.   
    44.     @Override  
    45.     public void onStart() {// 在start方法中注册广播接收者  
    46.         super.onStart();  
    47.         IntentFilter intentFilter = new IntentFilter();  
    48.         intentFilter.addAction(Constants.ACTION);  
    49.         registerReceiver(MsgReceiver, intentFilter);// 注册接受消息广播  
    50.   
    51.     }  
    52.   
    53.     @Override  
    54.     protected void onStop() {// 在stop方法中注销广播接收者  
    55.         super.onStop();  
    56.         unregisterReceiver(MsgReceiver);// 注销接受消息广播  
    57.     }  
    58. }  
    59. </span>  

     

           好了,大概思路就是这样的,下面根据具体的测试截图,说说我的思路:

    1.桌面快捷方式                                                                         2.欢迎界面

            

     

    3.正在登陆                                                                                 4.登陆成功后的好友列表,通过ViewPager实现

           

     

    5.好友列表是自定义的ExpandableListView,可以下拉刷新        6.群组聊天功能暂未实现

                      

     

    7.聊天主界面,                                                                           8.ViewPager实现左右滑动

          

     

    9.最近会话显示                                                                        10.未进入聊天界面时,来消息提醒,并保存数据库

           

     

    11.后台运行来消息时提醒,有声音有振动,左图为收到新消息,右图为无新消息时状态,

      

     

    12.后台数据库(上:user表,下:好友列表),密码通过MD5方式加密了,用户注册成功后,即生成一个以用户id命名的表,用来保存好友。

     

     

    13.服务器运行提示

     

    14.注册状态已经成功后的提示

     

     

    最后来几张聊天截图,好了今天就到这里,后续会继续跟大家分享其他各个小模块的具体实现,先休息一下,玩两天,马上要上班了,吼吼....

     

  • 相关阅读:
    函数式宏定义与普通函数
    linux之sort用法
    HDU 4390 Number Sequence 容斥原理
    HDU 4407 Sum 容斥原理
    HDU 4059 The Boss on Mars 容斥原理
    UVA12653 Buses
    UVA 12651 Triangles
    UVA 10892
    HDU 4292 Food
    HDU 4288 Coder
  • 原文地址:https://www.cnblogs.com/lanzhi/p/6469627.html
Copyright © 2011-2022 走看看