如下文章引用自:http://www.apkbus.com/android-144433-1-1.html
公司项目要求实现消息推送及即时聊天功能,考虑再三,选择了openfire。因为也是第一次接触XMPP开发,属于现学现用,走了很多弯路,印象比较深的是添加好友的实现。这个还曾在CSDN上留了个脚印,主要是想做个记录(http://blog.csdn.net/ming_light/article/details/11889697)。
但是后来又觉得有必要做个详细的总结,正好借这个活动,发个帖子。不保证完全正确,只是一种思路。
Android XMPP开发要借助asmack这个jar包
这些都是开源的,网上可以下到asmack的源码、openfire的源码以及详细的API文档
主要用的类有:
Roster 可以理解为好友花名册,提供创建删除获取列表等功能Roster.SubscriptionMode 这个在建立链接的时候使用,我的理解就是设置接收请求的模式,有三种,我们项目用的是SubscriptionMode.manual 也就是手动处理。
Presence 有关好友的推送消息,都是Presence包
Presence.Type 有7个Type,包含了好友上线下线、添加删除好友及错误信息的Type
PacketFilter 过滤器,此功能中主要实现过滤出Presence
PacketListener 看名字就知道,Packet监听,可以监听到服务端发来的所有信息。因为xmpp发的包都是它的子类
另外还有一点,就是好友双方对彼此的状态,有网友总结如下:
none:是用户和自己roster中的好友彼此不关心,既不想把自己的presence状态告诉对方,也不愿意收到对方presence更新消息
to:是关心roster中好友的presence状态消息,而不将自己的消息告诉对方
from:是只关心,接受对方的状态消息,而不将自己的消息告诉对方
both:即收取对方状态更新,又将自己的更新告知对方
Remove :将对方干掉,不再关心他的任何信息。
可能第一次看上面的东西,不太理解,比如说A B两个用户(SubscriptionMode.manual这种模式)
A主动请求加B B如果只是同意但并没有主动请求加A,此时B的状态是对A开放的,而B看不到A的状态。也就是说对于A来说,B的状态是to。对于B来说,A的状态是from。
因此要想AB双方互成好友,则B必须再发一个好友请求,并且A同意。
总结:用户只能看到对于他的状态是to和both的好友
走一边流程:
A+B A调用Roster的createEntry(String user,String name,String[] groups) (参数在API里都有解释,注意第一个参数user是当前登陆用户的jid,也就是注册名加域名,这个可以通过XMPPConnection的getUser方法得到)方法,此时A会发一个Presence包给B,且type="subscribe"(实际上都是服务器发的,这里可以不考虑中间过程,便于理解)
因为是手动添加好友,这里就需要在程序中对Presence包进行捕获处理,并且做出响应。所谓响应也就是发一个Presence包给A,告知是否同意。如下:
- Presence presence = new Presence(
- Presence.Type.subscribed);//同意是
- subscribed 拒绝是unsubscribe
- presence.setTo(...);//接收方jid
- presence.setFrom(...);//发送方jid
- connection.sendPacket(presence);//connection是你自己的XMPPConnection链接
但是比较麻烦的一点是本地缓存信息的判断。包括你的好友列表、添加好友消息信息不可能每次都去服务端获取,肯定是要做本地缓存,网络总规是有不稳定性的。
因为情况不同,这里只想说可以根据发送方jid、接收方jid、登陆用户jid以及type进行比较去重。这四个变量对于每一条Presence数据包来讲,肯定不会是完全一样的,大家可以自己琢磨一下。
注意:A主动加B,B同意的话发的状态是subscribed,此时如果B在加A,A同意的话也是subscribed。看上去没什么,但是做本地缓存的时候要做好判断。
下面是我的Presence包的监听方法,供大家参考
- public class PresenceService extends Service {
-
- private _ConnectionControl cc = _ConnectionControl.getCC();// 保存了当前的链接 XMPPConnection
-
- @Override
- public IBinder onBind(Intent arg0) {
- return null;
- }
-
- @Override
- public void onCreate() {
- super.onCreate();
- }
-
- @Override
- public void onDestroy() {
- super.onDestroy();
- }
-
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
- Log.i("Presence", "PresenceService-----" + (cc.connection == null));
- if (cc.connection != null && cc.connection.isConnected()
- && cc.connection.isAuthenticated()) {//已经认证的情况下,才能正确收到Presence包(也就是登陆)
- final String loginuser = cc.connection.getUser().substring(0, //这里要解释一下,这是要去除系统在登陆用户尾部添加的域名信息,例如 xxx<span id="kM0.021647081011906266">@域名.....</span>
- cc.connection.getUser().lastIndexOf("@"));
- //理解为条件过滤器 过滤出Presence包
- PacketFilter filter = new AndFilter(new PacketTypeFilter(
- Presence.class));
- PacketListener listener = new PacketListener() {
-
- @Override
- public void processPacket(Packet packet) {
- Log.i("Presence", "PresenceService------" + packet.toXML());
- //看API可知道 Presence是Packet的子类
- if (packet instanceof Presence) {
- Log.i("Presence", packet.toXML());
- Presence presence = (Presence) packet;
- //Presence还有很多方法,可查看API
- String from = presence.getFrom();//发送方
- String to = presence.getTo();//接收方
- //Presence.Type有7中状态
- if (presence.getType().equals(Presence.Type.subscribe)) {//好友申请
-
- } else if (presence.getType().equals(
- Presence.Type.subscribed)) {//同意添加好友
-
- } else if (presence.getType().equals(
- Presence.Type.unsubscribe)) {//拒绝添加好友 和 删除好友
-
- } else if (presence.getType().equals(
- Presence.Type.unsubscribed))
- } else if (presence.getType().equals(
- Presence.Type.unavailable)) {//好友下线 要更新好友列表,可以在这收到包后,发广播到指定页面 更新列表
-
- } else {//好友上线
-
- }
- }
- }
- };
- cc.connection.addPacketListener(listener, filter); //注册监听
- }
- return super.onStartCommand(intent, flags, startId);
- }
- }
再加点东西吧,就是断线自动重连。
XMPP是提供了这个功能的:
configuration.setReconnectionAllowed( true );
connection.addConnectionListener(connectionListener); //这个监听接口的实现就不写了,基本没什么,我只做了登陆冲突的处理。
开始是上面这么写的,测试发现没有效果,之后各种百度谷歌,才知道必须提前加载 ReconnectionManager这个类,写个静态代码块就行了:
- try {
- Class.forName("org.jivesoftware.smack.ReconnectionManager");
- } catch (Exception e1) {
- e1.printStackTrace();
- }
个人浅见,技术的提升在于交流。