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

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

    @Override
    	public void onBind(final Context context, int errorCode, String appid,
    			String userId, String channelId, String requestId)
    	{
    		String responseString = "onBind errorCode=" + errorCode + " appid="
    				+ appid + " userId=" + userId + " channelId=" + channelId
    				+ " requestId=" + requestId;
    		Log.e(TAG, responseString);
    
    		if (errorCode == 0)
    		{
    			SharePreferenceUtil util = PushApplication.getInstance()
    					.getSpUtil();
    			util.setAppId(appid);
    			util.setChannelId(channelId);
    			util.setUserId(userId);
    			
    		} else
    		// 假设网络正常。则重试
    		{
    			if (NetUtil.isNetConnected(context))
    			{
    
    				T.showLong(context, "启动失败,正在重试...");
    				new Handler().postDelayed(new Runnable()
    				{
    					@Override
    					public void run()
    					{
    						PushManager.startWork(context,
    								PushConstants.LOGIN_TYPE_API_KEY,
    								PushApplication.API_KEY);
    					}
    				}, 2000);// 两秒后又一次開始验证
    			} else
    			{
    				T.showLong(context, R.string.net_error_tip);
    			}
    		}
    		// 回调函数
    		for (int i = 0; i < bindListeners.size(); i++)
    			bindListeners.get(i).onBind(userId, errorCode);
    	}
    初次绑定的时候,假设绑定成功则使用SharedPreferences存储userId,channelId等数据

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

    以下看listener.onBind

    /**
    	 * 收到通知
    	 */
    	@Override
    	public void onBind(String userId, int errorCode)
    	{
    		Log.e("TAG", "Login Activity onBind ");
    		if (errorCode == 0)
    		{
    			Log.e("TAG", "Login Activity onBind success ");
    			// 假设绑定账号成功,因为第一次执行,给同一tag的人推送一条新人消息
    			User u = new User(mSpUtil.getUserId(), mSpUtil.getChannelId(),
    					mSpUtil.getNick(), mSpUtil.getHeadIcon(), 0);
    			mUserDB.addUser(u);// 把自己增加到数据库
    			Message firstSendMsg = new Message(System.currentTimeMillis(), "");
    			firstSendMsg.setHello("hello");
    			task = new SendMsgAsyncTask(mGson.toJson(firstSendMsg), "");
    			task.setOnSendScuessListener(new OnSendScuessListener()
    			{
    				@Override
    				public void sendScuess()
    				{
    					if (mLoadingDialog != null && mLoadingDialog.isVisible())
    						mLoadingDialog.dismiss();
    					mHandler.removeCallbacks(mConnTimeoutCallback);
    					finish();
    					Intent intent = new Intent(LoginActivity.this,
    							MainActivity.class);
    					startActivity(intent);
    				}
    			});
    			task.send();
    		}
    	}
    首先将自己保存到本地数据库,然后发送一个Message给全部的用户,设置一个特殊字段hello;也就是上述的原理分析中的部分~


    2、onMessage

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

    @Override
    	public void onMessage(Context context, String message,
    			String customContentString)
    	{
    		String messageString = "收到消息 message="" + message
    				+ "" customContentString=" + customContentString;
    		Log.e(TAG, messageString);
    		Message receivedMsg = PushApplication.getInstance().getGson()
    				.fromJson(message, Message.class);
    		// 对接收到的消息进行处理
    		parseMessage(receivedMsg);
    
    	}
    
    	/**
    	 * 消息:1、携带hello,表示新人增加。应该自己主动回复一个world 消息; 2、普通消息。
    	 * 
    	 * @param msg
    	 */
    	private void parseMessage(Message msg)
    	{
    		String userId = msg.getUserId();
    		// 自己的消息
    		if (userId
    				.equals(PushApplication.getInstance().getSpUtil().getUserId()))
    			return;
    		if (checkHasNewFriend(msg) || checkAutoResponse(msg))
    			return;
    		// 普通消息
    		UserDB userDB = PushApplication.getInstance().getUserDB();
    		User user = userDB.selectInfo(userId);
    		// 漏网之鱼
    		if (user == null)
    		{
    			user = new User(userId, msg.getChannelId(), msg.getNickname(), 0, 0);
    			userDB.addUser(user);
    			// 通知监听的面板
    			for (onNewFriendListener listener : friendListeners)
    				listener.onNewFriend(user);
    		}
    		if (msgListeners.size() > 0)
    		{// 有监听的时候,传递下去
    			for (int i = 0; i < msgListeners.size(); i++)
    				msgListeners.get(i).onNewMessage(msg);
    		} else
    		// 当前没有不论什么监听。即处理后台状态
    		{
    			// 将新来的消息进行存储
    			ChatMessage chatMessage = new ChatMessage(msg.getMessage(), true,
    					userId, msg.getHeadIcon(), msg.getNickname(), false,
    					TimeUtil.getTime(msg.getTimeSamp()));
    			PushApplication.getInstance().getMessageDB()
    					.add(userId, chatMessage);
    			showNotify(msg);
    		}
    	}
    
    	/**
    	 * 检測是否是自己主动回复
    	 * 
    	 * @param msg
    	 */
    	private boolean checkAutoResponse(Message msg)
    	{
    		String world = msg.getWorld();
    		String userId = msg.getUserId();
    		if (!TextUtils.isEmpty(world))
    		{
    			User u = new User(userId, msg.getChannelId(), msg.getNickname(),
    					msg.getHeadIcon(), 0);
    			PushApplication.getInstance().getUserDB().addUser(u);// 存入或更新好友
    			// 通知监听的面板
    			for (onNewFriendListener listener : friendListeners)
    				listener.onNewFriend(u);
    
    			return true;
    		}
    		return false;
    	}
    
    	/**
    	 * 检測是否是新人增加
    	 * 
    	 * @param msg
    	 */
    	private boolean checkHasNewFriend(Message msg)
    	{
    		String userId = msg.getUserId();
    		String hello = msg.getHello();
    		// 新人发送的消息
    		if (!TextUtils.isEmpty(hello))
    		{
    			Log.e("checkHasNewFriend", msg.getUserId());
    			// 新人
    			User u = new User(userId, msg.getChannelId(), msg.getNickname(),
    					msg.getHeadIcon(), 0);
    			PushApplication.getInstance().getUserDB().addUser(u);// 存入或更新好友
    			T.showShort(PushApplication.getInstance(), u.getNick() + "增加");
    
    			// 给新人回复一个应答
    			Message message = new Message(System.currentTimeMillis(), "");
    			message.setWorld("world");
    			new SendMsgAsyncTask(PushApplication.getInstance().getGson()
    					.toJson(message), userId);
    			// 通知监听的面板
    			for (onNewFriendListener listener : friendListeners)
    				listener.onNewFriend(u);
    
    			return true;
    		}
    
    		return false;
    	}

    当收到一个新的消息:

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

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

    3、推断是否包括welcome字段。包括,存入好友列表

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


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

    /**
    	 * 收到新消息时
    	 */
    	@Override
    	public void onNewMessage(Message message)
    	{
    		// 假设是自己发送的。则直接返回
    		if (message.getUserId() == mSpUtils.getUserId())
    			return;
    		// 假设该用户已经有未读消息。更新未读消息的个数。并通知更新未读消息接口。最后notifyDataSetChanged
    		String userId = message.getUserId();
    		if (mUserMessages.containsKey(userId))
    		{
    			mUserMessages.put(userId, mUserMessages.get(userId) + 1);
    		} else
    		{
    			mUserMessages.put(userId, 1);
    		}
    		mUnReadedMsgs++;
    		notifyUnReadedMsg();
    		// 将新来的消息进行存储
    		ChatMessage chatMessage = new ChatMessage(message.getMessage(), true,
    				userId, message.getHeadIcon(), message.getNickname(), false,
    				TimeUtil.getTime(message.getTimeSamp()));
    		mApplication.getMessageDB().add(userId, chatMessage);
    		// 通知listview数据改变
    		mAdapter.notifyDataSetChanged();
    	}

    1、假设是自己发的不做不论什么处理

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

    3、刷新界面


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

    @Override
    	public void onNewMessage(Message message)
    	{
    
    		// 获得回复的消息
    		if (mFromUser.getUserId().equals(message.getUserId()))
    		{
    			ChatMessage chatMessage = new ChatMessage();
    			chatMessage.setComing(true);
    			chatMessage.setDate(new Date(message.getTimeSamp()));
    			chatMessage.setMessage(message.getMessage());
    			chatMessage.setUserId(message.getUserId());
    			chatMessage.setNickname(message.getNickname());
    			chatMessage.setReaded(true);
    			mDatas.add(chatMessage);
    			mAdapter.notifyDataSetChanged();
    			mChatMessagesListView.setSelection(mDatas.size() - 1);
    			// 存入数据库,当前聊天记录
    			mApplication.getMessageDB().add(mFromUser.getUserId(), chatMessage);
    		}
    	}

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



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

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


    源代码点击下载  大家能够先安装压缩文件中面的apk測试下,这样就能够发现别的伙伴了~~~


    源代码点击下载 补充了一下那个BadgeView的项目~~~



    版权声明:本文博主原创文章。博客,未经同意不得转载。

  • 相关阅读:
    C++元编程和Boost编程库 (C++ Metaprogramming and Boost MPL )中部
    支持插件的消息中间件【msg broker with plugin】 知然 博客园
    sync date
    Rubular: a Ruby regular expression editor and tester
    当爬虫被拒绝时(Access Denied) 风中之炎 博客园
    quartz scheduler
    C++ 使用STL string 实现的split,trim,replace修订
    java脚本编程 语言、框架与模式
    C++标准转换运算符const_cast
    http://jsoneditoronline.org/
  • 原文地址:https://www.cnblogs.com/yxwkf/p/4859113.html
Copyright © 2011-2022 走看看