zoukankan      html  css  js  c++  java
  • Android 高仿微信实时聊天 基于百度云推送

    转载请标明出处:http://blog.csdn.net/lmj623565791/article/details/38799363 ,本文出自:【张鸿洋的博客】

    一直在仿微信界面,今天终于有幸利用百度云推送仿一仿微信聊天了~~~

    首先特别感谢:weidi1989分享的Android之基于百度云推送IM ,大家可以直接下载;省了很多事哈,本例中也使用了weidi的部分代码,凡是@author way的就是weidi1989的代码~~

    1、效果图

    核心功能也就上面的两张图了~~~我拿着手机和模拟器聊天,同时感谢群里的兄弟姐妹帮忙测试(好友列表中)。

    2、原理

    下面通过几个问题来说明下实现的原理:

    1、如何实现给某个用户发送消息呢?

    其实就是利用百度云提供的REST API,直接通过发送Http请求的形式给某个用户推送一条消息;

    网址:http://developer.baidu.com/wiki/index.php?title=docs/cplat/push/api/list

    实例:

    push_msg
    功能
    推送消息,该接口可用于推送单个人、一群人、所有人以及固定设备的使用场景。
    HTTP请求方式
    POST
    URL
    http[s]://channel.api.duapp.com/rest/2.0/channel/channel

    ....

    2、的确可以实现给某个用户或者一群用户推送消息,那么用户的昵称神马的咋获取的呢,我印象中百度云中没法存这样的数据?

    嗯,其实用了一个比较巧妙的方式;在用户首次安装软件的时候,会要求用户填写注册信息,也就是昵称等;当用户填写完毕时,点击登录的时候:

    1、发送一条比较特殊的消息,比如这个消息携带一个hello的字段,广播给所有用户;

    2、其他用户在收到消息时,首先判断hello这个字段是否有值,有值则说明新用户加入,把该新用户存入本地数据库,然后更新用户列表;

    3、其他用户给这个新用户回一条消息,附带一个特殊字段,比如welcome,当新用户收到携带welcome字段的消息时,表明这是对新用户hello的答复,然后将已存在用户添加到该新用户的用户列表。

    基本原理就这样了,大家甚至可以利用上述的原理添加一些删除好友的功能;比如当用户点击删除好友,则发送一条特殊消息给被删除的对象,然后对方收到该消息,也将发送发删除。

    相信大家在了解原理之后,这个例子瞬间从高大尚变成矮矬穷了,这尼玛,so easy,谁不会啊~~~

    3、核心代码解析

    由于代码量还是相当大的,也不想拆成几篇博客,所以准备将核心代码进行讲解下,其他的大家自己看源码~

    关于这个例子的主界面,可以参考: 高仿微信5.2.1主界面架构 包含消息通知

    关于百度云推送的入门,可以参考:Android推送 百度云推送 入门篇

    好了,最主要就是收到百度云推送的Receiver

    1、onBind

    首先是用户第一次登录时候绑定的回调

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. @Override  
    2.     public void onBind(final Context context, int errorCode, String appid,  
    3.             String userId, String channelId, String requestId)  
    4.     {  
    5.         String responseString = "onBind errorCode=" + errorCode + " appid="  
    6.                 + appid + " userId=" + userId + " channelId=" + channelId  
    7.                 + " requestId=" + requestId;  
    8.         Log.e(TAG, responseString);  
    9.   
    10.         if (errorCode == 0)  
    11.         {  
    12.             SharePreferenceUtil util = PushApplication.getInstance()  
    13.                     .getSpUtil();  
    14.             util.setAppId(appid);  
    15.             util.setChannelId(channelId);  
    16.             util.setUserId(userId);  
    17.               
    18.         } else  
    19.         // 如果网络正常,则重试  
    20.         {  
    21.             if (NetUtil.isNetConnected(context))  
    22.             {  
    23.   
    24.                 T.showLong(context, "启动失败,正在重试...");  
    25.                 new Handler().postDelayed(new Runnable()  
    26.                 {  
    27.                     @Override  
    28.                     public void run()  
    29.                     {  
    30.                         PushManager.startWork(context,  
    31.                                 PushConstants.LOGIN_TYPE_API_KEY,  
    32.                                 PushApplication.API_KEY);  
    33.                     }  
    34.                 }, 2000);// 两秒后重新开始验证  
    35.             } else  
    36.             {  
    37.                 T.showLong(context, R.string.net_error_tip);  
    38.             }  
    39.         }  
    40.         // 回调函数  
    41.         for (int i = 0; i < bindListeners.size(); i++)  
    42.             bindListeners.get(i).onBind(userId, errorCode);  
    43.     }  

    初次绑定的时候,如果绑定成功则使用SharedPreferences存储userId,channelId等数据

    然后回调:bindListeners.get(i).onBind(userId, errorCode);给所有注册该事件的发出通知

    下面看listener.onBind

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. /** 
    2.      * 收到通知 
    3.      */  
    4.     @Override  
    5.     public void onBind(String userId, int errorCode)  
    6.     {  
    7.         Log.e("TAG", "Login Activity onBind ");  
    8.         if (errorCode == 0)  
    9.         {  
    10.             Log.e("TAG", "Login Activity onBind success ");  
    11.             // 如果绑定账号成功,由于第一次运行,给同一tag的人推送一条新人消息  
    12.             User u = new User(mSpUtil.getUserId(), mSpUtil.getChannelId(),  
    13.                     mSpUtil.getNick(), mSpUtil.getHeadIcon(), 0);  
    14.             mUserDB.addUser(u);// 把自己添加到数据库  
    15.             Message firstSendMsg = new Message(System.currentTimeMillis(), "");  
    16.             firstSendMsg.setHello("hello");  
    17.             task = new SendMsgAsyncTask(mGson.toJson(firstSendMsg), "");  
    18.             task.setOnSendScuessListener(new OnSendScuessListener()  
    19.             {  
    20.                 @Override  
    21.                 public void sendScuess()  
    22.                 {  
    23.                     if (mLoadingDialog != null && mLoadingDialog.isVisible())  
    24.                         mLoadingDialog.dismiss();  
    25.                     mHandler.removeCallbacks(mConnTimeoutCallback);  
    26.                     finish();  
    27.                     Intent intent = new Intent(LoginActivity.this,  
    28.                             MainActivity.class);  
    29.                     startActivity(intent);  
    30.                 }  
    31.             });  
    32.             task.send();  
    33.         }  
    34.     }  

    首先将自己保存到本地数据库,然后发送一个Message给所有的用户,设置一个特殊字段hello;也就是上述的原理分析中的部分~

    2、onMessage

    收到百度云推送的Receiver中的onMessage

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. @Override  
    2.     public void onMessage(Context context, String message,  
    3.             String customContentString)  
    4.     {  
    5.         String messageString = "收到消息 message="" + message  
    6.                 + "" customContentString=" + customContentString;  
    7.         Log.e(TAG, messageString);  
    8.         Message receivedMsg = PushApplication.getInstance().getGson()  
    9.                 .fromJson(message, Message.class);  
    10.         // 对接收到的消息进行处理  
    11.         parseMessage(receivedMsg);  
    12.   
    13.     }  
    14.   
    15.     /** 
    16.      * 消息:1、携带hello,表示新人加入,应该自动回复一个world 消息; 2、普通消息; 
    17.      *  
    18.      * @param msg 
    19.      */  
    20.     private void parseMessage(Message msg)  
    21.     {  
    22.         String userId = msg.getUserId();  
    23.         // 自己的消息  
    24.         if (userId  
    25.                 .equals(PushApplication.getInstance().getSpUtil().getUserId()))  
    26.             return;  
    27.         if (checkHasNewFriend(msg) || checkAutoResponse(msg))  
    28.             return;  
    29.         // 普通消息  
    30.         UserDB userDB = PushApplication.getInstance().getUserDB();  
    31.         User user = userDB.selectInfo(userId);  
    32.         // 漏网之鱼  
    33.         if (user == null)  
    34.         {  
    35.             user = new User(userId, msg.getChannelId(), msg.getNickname(), 0, 0);  
    36.             userDB.addUser(user);  
    37.             // 通知监听的面板  
    38.             for (onNewFriendListener listener : friendListeners)  
    39.                 listener.onNewFriend(user);  
    40.         }  
    41.         if (msgListeners.size() > 0)  
    42.         {// 有监听的时候,传递下去  
    43.             for (int i = 0; i < msgListeners.size(); i++)  
    44.                 msgListeners.get(i).onNewMessage(msg);  
    45.         } else  
    46.         // 当前没有任何监听,即处理后台状态  
    47.         {  
    48.             // 将新来的消息进行存储  
    49.             ChatMessage chatMessage = new ChatMessage(msg.getMessage(), true,  
    50.                     userId, msg.getHeadIcon(), msg.getNickname(), false,  
    51.                     TimeUtil.getTime(msg.getTimeSamp()));  
    52.             PushApplication.getInstance().getMessageDB()  
    53.                     .add(userId, chatMessage);  
    54.             showNotify(msg);  
    55.         }  
    56.     }  
    57.   
    58.     /** 
    59.      * 检测是否是自动回复 
    60.      *  
    61.      * @param msg 
    62.      */  
    63.     private boolean checkAutoResponse(Message msg)  
    64.     {  
    65.         String world = msg.getWorld();  
    66.         String userId = msg.getUserId();  
    67.         if (!TextUtils.isEmpty(world))  
    68.         {  
    69.             User u = new User(userId, msg.getChannelId(), msg.getNickname(),  
    70.                     msg.getHeadIcon(), 0);  
    71.             PushApplication.getInstance().getUserDB().addUser(u);// 存入或更新好友  
    72.             // 通知监听的面板  
    73.             for (onNewFriendListener listener : friendListeners)  
    74.                 listener.onNewFriend(u);  
    75.   
    76.             return true;  
    77.         }  
    78.         return false;  
    79.     }  
    80.   
    81.     /** 
    82.      * 检测是否是新人加入 
    83.      *  
    84.      * @param msg 
    85.      */  
    86.     private boolean checkHasNewFriend(Message msg)  
    87.     {  
    88.         String userId = msg.getUserId();  
    89.         String hello = msg.getHello();  
    90.         // 新人发送的消息  
    91.         if (!TextUtils.isEmpty(hello))  
    92.         {  
    93.             Log.e("checkHasNewFriend", msg.getUserId());  
    94.             // 新人  
    95.             User u = new User(userId, msg.getChannelId(), msg.getNickname(),  
    96.                     msg.getHeadIcon(), 0);  
    97.             PushApplication.getInstance().getUserDB().addUser(u);// 存入或更新好友  
    98.             T.showShort(PushApplication.getInstance(), u.getNick() + "加入");  
    99.   
    100.             // 给新人回复一个应答  
    101.             Message message = new Message(System.currentTimeMillis(), "");  
    102.             message.setWorld("world");  
    103.             new SendMsgAsyncTask(PushApplication.getInstance().getGson()  
    104.                     .toJson(message), userId);  
    105.             // 通知监听的面板  
    106.             for (onNewFriendListener listener : friendListeners)  
    107.                 listener.onNewFriend(u);  
    108.   
    109.             return true;  
    110.         }  
    111.   
    112.         return false;  
    113.     }  


    当收到一个新的消息:

    1、首先判断是不是自己发的,是,忽略;

    2、判断是否携带hello字段,代表新人加入,携带,则保存到好友列表,且回复一个携带welcome字段的消息;

    3、判断是否包含welcome字段,包含,存入好友列表

    4、不是1、2、3则肯定是普通消息,将消息保存到本地数据库,然后为所有设置消息监听的发送回调即可~

    MainTabFriends.java用户列表中收到新消息的回调:

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. /** 
    2.      * 收到新消息时 
    3.      */  
    4.     @Override  
    5.     public void onNewMessage(Message message)  
    6.     {  
    7.         // 如果是自己发送的,则直接返回  
    8.         if (message.getUserId() == mSpUtils.getUserId())  
    9.             return;  
    10.         // 如果该用户已经有未读消息,更新未读消息的个数,并通知更新未读消息接口,最后notifyDataSetChanged  
    11.         String userId = message.getUserId();  
    12.         if (mUserMessages.containsKey(userId))  
    13.         {  
    14.             mUserMessages.put(userId, mUserMessages.get(userId) + 1);  
    15.         } else  
    16.         {  
    17.             mUserMessages.put(userId, 1);  
    18.         }  
    19.         mUnReadedMsgs++;  
    20.         notifyUnReadedMsg();  
    21.         // 将新来的消息进行存储  
    22.         ChatMessage chatMessage = new ChatMessage(message.getMessage(), true,  
    23.                 userId, message.getHeadIcon(), message.getNickname(), false,  
    24.                 TimeUtil.getTime(message.getTimeSamp()));  
    25.         mApplication.getMessageDB().add(userId, chatMessage);  
    26.         // 通知listview数据改变  
    27.         mAdapter.notifyDataSetChanged();  
    28.     }  


    1、如果是自己发的不做任何处理

    2、如果当前消息发送用户已有未读消息,则更新该用户未读消息个数(用户Item上显示未读消息通知数),更新所有未读消息总和(朋友Tab上显示未读消息总和),然后存储该消息

    3、刷新界面

    ChattingActivity.java聊天界面的新消息回调

    [java] view plaincopy在CODE上查看代码片派生到我的代码片
     
    1. @Override  
    2.     public void onNewMessage(Message message)  
    3.     {  
    4.   
    5.         // 获得回复的消息  
    6.         if (mFromUser.getUserId().equals(message.getUserId()))  
    7.         {  
    8.             ChatMessage chatMessage = new ChatMessage();  
    9.             chatMessage.setComing(true);  
    10.             chatMessage.setDate(new Date(message.getTimeSamp()));  
    11.             chatMessage.setMessage(message.getMessage());  
    12.             chatMessage.setUserId(message.getUserId());  
    13.             chatMessage.setNickname(message.getNickname());  
    14.             chatMessage.setReaded(true);  
    15.             mDatas.add(chatMessage);  
    16.             mAdapter.notifyDataSetChanged();  
    17.             mChatMessagesListView.setSelection(mDatas.size() - 1);  
    18.             // 存入数据库,当前聊天记录  
    19.             mApplication.getMessageDB().add(mFromUser.getUserId(), chatMessage);  
    20.         }  
    21.     }  


    首先判断是否是正在聊天的用户发的消息,如果是,直接存入数据库,刷新聊天界面;

    好了,细节也不展开描述了,大家自己看源码,bug肯定有,发现bug可以直接留言,方便其他学习的童鞋,也方便我了,多谢~~~~~~~~~~~~~~

    ps:最近状态不好,我要去喝脉动~~~~

    源码点击下载  大家可以先安装压缩文件里面的apk测试下,这样就可以发现别的伙伴了~~~

  • 相关阅读:
    网络编程之即时通信程序(聊天室)(一)通信流程简介及通信协议定制
    C#常用加密方法解析
    ASP.NET常用数据绑定控件优劣总结
    使用XPO开发时可以参考使用的架构
    渠道会上的体会
    如何利用第三方SDK开发MSN机器人以及实现语音视频?
    对 XPO 的一些问题的解答
    c++ 参数传递 之引用形参 GIS
    指针与引用的区别 GIS
    c++ const 修饰数组 GIS
  • 原文地址:https://www.cnblogs.com/dongweiq/p/3934304.html
Copyright © 2011-2022 走看看