最近在写一个即时通信的项目,用的也是xmpp,asmack和openfire,这个可以算是我做的第一个正式项目吧,所以也是一边查资料一边写的,虽说网上资料很多,但不少逻辑和需要注意的地方还是自己整理发现的,这里就分享一下,这一篇就先说下好友相关的功能吧,其他的再慢慢整理。
好友的相关功能最主要的就是框架中的Presence类了,首先它是Packet类的子类,所以可以通过连接.sendPacket()方法发送出去。
它内嵌两个枚举类型 Presence.Mode和Presence.Type
Presence.Type有七种
available (默认)用户空闲状态,即在线
unavailable 用户没空看消息,即离线
subscribe 请求订阅别人,即请求加对方为好友
subscribed 统一被别人订阅,也就是确认被对方加为好友
unsubscribe 他取消订阅别人,请求删除某好友
unsubscribed 拒绝被别人订阅,即拒绝对放的添加请求
error -- 当前状态packet有错误
Presence.Mode有五种
chat 相当于qq的q我吧
available 默认即在线状态
away 离开
xa 离开一段时间
dnd 请勿打扰
可以使用setStatus(String)自定义用户当前的状态,例如“我去吃饭了”
另外还有一点,就是好友双方对彼此的状态,有网友总结如下:
none:是用户和自己roster中的好友彼此不关心,既不想把自己的presence状态告诉对方,也不愿意收到对方presence更新消息
to:是关心roster中好友的presence状态消息,而不将自己的消息告诉对方
from:是只关心,接受对方的状态消息,而不将自己的消息告诉对方
both:即收取对方状态更新,又将自己的更新告知对方
Remove :将对方干掉,不再关心他的任何信息。
下面就先说说添加好友吧,有一点值得提一下,如果直接调用框架的createEntry()方法,在发送请求之前就已经添加了好友的花名册Roster,具体原因可以看下源码
public void createEntry(String user,String name, String[] groups) throws XMPPException {
// Create and send roster entrycreation packet.
RosterPacket rosterPacket = new RosterPacket();
rosterPacket.setType(IQ.Type.SET);
RosterPacket.Item item = new RosterPacket.Item(user,name);
if (groups !=null) {
for (String group : groups) {
if (group !=null &&group.trim().length() > 0) {
item.addGroupName(group);
}
}
}
rosterPacket.addRosterItem(item);
// Wait up to a certain number of secondsfor a reply from the server.
PacketCollector collector = connection.createPacketCollector(
newPacketIDFilter(rosterPacket.getPacketID()));
connection.sendPacket(rosterPacket);
IQ response = (IQ)collector.nextResult(SmackConfiguration.getPacketReplyTimeout());
collector.cancel();
if (response ==null) {
throw new XMPPException("No response from the server.");
}
// If the server replied with an error,throw an exception.
else if (response.getType() == IQ.Type.ERROR) {
throw new XMPPException(response.getError());
}
// Create a presence subscription packetand send.
// Presence presencePacket =new Presence(Presence.Type.subscribe);
// presencePacket.setTo(user);
// connection.sendPacket(presencePacket);
}
可以看到,框架中的顺序是先添加好友,然后再发送Presence包去请求,为此框架还提供了5种subscribtion来定义双方的状态,这就和我的项目需求不和,所以在我的项目中 添加好友实现流程(同时添加)A给B发送Presence.Type.subscribe
B收到Presence.Type.subscribe给A发送Presence.Type.subscribed(同意)
B给A发送Presence.Type.subscribe
A收到Presence.Type.subscribed调用roster.createEntry方法创建好友花名册
A收到Presence.Type.subscribe给B发送Presence.Type.subscribed(同意)
B收到Presence.Type.subcribed调用roster.createEntry方法创建好友花名册
注:源码中最后三行代码让我注释了,因为通过先用发Presence包的方法添加,这三行就没用了,但是不注释的话还是会再发一遍请求,所以果断注释掉。以上逻辑均在PacketListener中实现,下面是我AllPacketListener类中的大概逻辑
public class AllPacketListener implements PacketListener {
// 服务器返回给客户端的信息
@Override
public void processPacket(Packet packet) {
Log.i("AllPacketListener", packet.toXML());
if (packetinstanceoforg.jivesoftware.smack.packet.Presence) {//好友相关
Presencepresence = (Presence) packet;
//登陆时判断好友是否在线
if(presence.isAvailable()){
//将在线好友添加进集合
}
Type type =presence.getType();
String from =presence.getFrom();
if (type.equals(Presence.Type.subscribe)) {//好友申请
//如果已经发送过好友申请,接收到对方的好友申请,说明对方同意,直接回复同意
//如果没发送过,需要其他界面来判断是否同意
} elseif (type.equals(Presence.Type.unsubscribe)) {//删除好友
//通知当前用户被对方删除========================
} elseif (type.equals(Presence.Type.subscribed)) {//同意添加好友
//加对方为好友
//存入数据库(好友表+分组表)
//存入集合
} elseif (type.equals(Presence.Type.unsubscribed)) {//拒绝添加好友
//发通知告诉用户对方拒绝添加好友========================
} elseif (type.equals(Presence.Type.unavailable)) {//好友下线要更新好友列表,可以在这收到包后,发广播到指定页面更新列表
//更新在线好友集合
//好友头像变灰色
} elseif(type.equals(Presence.Type.available)){//好友上线
//更新在线好友集合
//好友头像变亮
}
}
}
}
上线监听好友在线状态,好友头像显示灰色或正常颜色
通过presence.isAvailable()方法判断,返回boolean类型true为在线,false为不在线,存入集合更新界面
设置自身账号状态(正常,隐身,忙碌……)
正常:Presence presence=new(Presence.Type.available);//设置状态为在线
conn.sendPacket(presence);//发送给服务器
隐身:Presence.Type.unavailable //设置状态为不在线,发送给服务器
忙碌:presence.setMode(Presence.Mode.dnd);//设置当前状态为请勿打扰,发送给服务器
或presence.setStatus(“忙碌”);//自定义状态,发送给服务器(内容可自定义)
……
用户查询
public List<SimpleUser>searchUser(final String username)throws XMPPException {
searchRes = newArrayList<SimpleUser>();
UserSearchManageruserSearchManger;
try {
newServiceDiscoveryManager(GloableParams.conn);
userSearchManger= newUserSearchManager(GloableParams.conn);
Form searchForm= userSearchManger.getSearchForm("search." +SERVER_NAME);
Form answerForm= searchForm.createAnswerForm();
answerForm.setAnswer("Username",true);
answerForm.setAnswer("search", username);
ReportedDataresData = userSearchManger.getSearchResults(answerForm,"search." +conn.getServiceName());
Iterator<Row>it = resData.getRows();
Row row = null;
SimpleUser user= null;
while (it.hasNext()) {
user = new SimpleUser();
row =it.next();
user.setUserAccount(row.getValues("Username").next().toString());
searchRes.add(user);
}
} catch (XMPPException e) {
e.printStackTrace();
}
returnsearchRes;
}
注:username不包括@服务器名
SimpleUser实体类自己定义
代码直接可用
创建好友
通过连接获得Roster类,调用roster.createEntry(String user, String name, String[] groups)方法,user好友用户名(包括@和服务器名);name好友昵称,自定义;groups好友分组,自定义
删除好友
通过roster获得RosterEntry
RosterEntry entry=roster.getEntry(user);//user包括@服务器名
调用roster.removeEntry(entry)方法删除好友
好友移动分组
通过Roster获得RosterEntry
RosterEntry entry=roster.getEntry(user);
通过Roster获得RosterGroup
RosterGroupgroup=roster.getGroup(groupName);
对group进行addEntry(entry)或removeEntry(entry)操作
实现方式,在新组中添加,在老组中删除,如果好友移动后组中没人了,下次登陆时这个组会从服务器消失,这是框架或是服务器的一种机制,无论是创建新的组或是把组里的好友全部删除或移动走后,在下次登陆时服务器会对其进行清理。解决方式,做一下判断,如果组里没人了,自动向组里添加一个好友(createEntry),但是不显示,加入其他好友后可将其删除,可以使特定的账号,如a-z,注册几个1位的账号专门占位用,以后注册时加入正则判断不让用户注册此类账号,还可以限制用户建组的数量。
好友详细信息
需要用到Vcard类实现
构建Vcard对象
Vcard vc=new Vcard();
属性赋值
vc.setFirstName(xxx);
……
保存Vcard信息
vc.save(conn);
Vcard属性:
Map<String, String> homePhones
Map<String, String> workPhones
Map<String, String> homeAddr
Map<Stirng, String> workAddr
String firstName
String lastName
String middleName
String emailHome
String emailWork
String organization
String organizationUnit
String avatar
可以保存你需要的信息,如在middleName中保存性别
通过vc.load(conn, user)方法读取数据 //user包括@服务器名
就先这么多吧,我也在学习中,如果您有什么疑问或者更好的解决方法,欢迎指导,大家共同进步
转载请注明出处 http://blog.csdn.net/tengoku/article/details/40822561 谢谢