Android基于XMPP Smack openfire 开发的聊天室
2015-02-10 10:33
309 查看
Android基于XMPP Smack openfire 开发的聊天室
分类: android2013-03-0717:36 5237人阅读 评论(0) 收藏 举报
聊天smackxmppstringandroid
目录(?)[+]
公司刚好让做即时通讯模块,服务器使用openfire,偶然看到有位仁兄的帖子,拷贝过来细细研究,感谢此仁兄的无私,期待此仁兄的下次更新
转自/article/2813353.html
Android基于XMPP Smack openfire 开发的聊天室(一)【会议服务、聊天室列表、加入】
废话少说,公司没事干,组长叫我写Demo,我狂晕....先把先说说这个什么聊天室吧,服务器就别说了,典型的拿来主义。用的是成品的openfire。说道smack,这个是一个jar文件,库,是用在javae的。要想在android端使用这个,就要用到asmack包,这个可以在官网下。其实里边跟smack包没什么区别,具体差异目前我没发现。
可能有的朋友说这个smack是什么用的,我个人一句话,一个已经包装了XMPP协议的架包。提供了API的操作...好了进入正题
1,首先想进入聊天室,那必须要先知道聊天室所在的会议服务(下图,用spark截图)
[java] view
plaincopy
/**
* 初始化聊服务会议列表
*/
private void initHostRoom() {
Collection<HostedRoom> hostrooms;
try {
hostrooms = MultiUserChat.getHostedRooms(Constants.conn,
Constants.conn.getServiceName());
for (HostedRoom entry : hostrooms) {
roominfos.add(entry);
Log.i(TAG, "名字:" + entry.getName() + " - ID:" + entry.getJid());
}
Log.i(TAG, "服务会议数量:" + roominfos.size());
} catch (XMPPException e) {
e.printStackTrace();
}
}
2,获取了那个服务的JID后才能进入会议列表:
[java] view
plaincopy
/**
* 初始化房间列表
*/
public void init() {
listDiscoverItems = new ArrayList<DiscoverItems.Item>();
// 获得与XMPPConnection相关的ServiceDiscoveryManager
ServiceDiscoveryManager discoManager = ServiceDiscoveryManager
.getInstanceFor(Constants.conn);
// 获得指定XMPP实体的项目
// 这个例子获得与在线目录服务相关的项目
DiscoverItems discoItems;
try {
discoItems = discoManager.discoverItems(jid);
// 获得被查询的XMPP实体的要查看的项目
Iterator it = discoItems.getItems();
// 显示远端XMPP实体的项目
while (it.hasNext()) {
DiscoverItems.Item item = (DiscoverItems.Item) it.next();
System.out.println(item.getEntityID());
System.out.println(item.getName());
listDiscoverItems.add(item);
}
} catch (XMPPException e) {
e.printStackTrace();
}
}
3,同样,要进入会议房间(聊天室)也要获取它的ID,才能进入
[java] view
plaincopy
// 后面服务名称必需是创建房间的那个服务
jid = getIntent().getStringExtra("jid");
try {
muc = new MultiUserChat(Constants.conn, jid);
// 创建聊天室,进入房间后的nickname(昵称)
muc.join("痞子测试");
Log.v(TAG, "join success");
} catch (XMPPException e) {
e.printStackTrace();
}
简单吧!
Android基于XMPP Smack openfire 开发的聊天室(二) 【聊天信息、成员】
上一篇呢说了怎么进入这个聊天室,这次呢,咱就说聊天室里的功能吧,聊天信息、成员变动什么的。还是少说废话,正题:1,说要聊天呢,简单就是一个文本信息,当然我们不能时时去服务器获取信息。要充分发挥即时推送嘛。
(1)首先要添加一个监听,muc.addMessageListener(chatListener); 要是muc不知道是什么请看上一篇吧,这就不多解释。chatListener就是我们的监听器,看代码,其实下边的代码有些啰嗦。我只是懒得改了。最近有点烦躁。这里我们主要获取的就是Packet,这个是一个XMPP包装好的XML流,里边有你想要的东西。有兴趣深入的朋友可以上XMPP中文翻译组去看看,挺犀利的。
[java] view
plaincopy
/**
* PacketListener 通过一个规定的过滤器提供一个机制来监听数据包
*
* @author liaonaibo
*
*/
class ChatPacketListener implements PacketListener {
private String _number;
private Date _lastDate;
private MultiUserChat _muc;
private String _roomName;
public ChatPacketListener(MultiUserChat muc) {
_number = "0";
_lastDate = new Date(0);
_muc = muc;
_roomName = muc.getRoom();
}
@Override
public void processPacket(Packet packet) {
Message message = (Message) packet;
String from = message.getFrom();
if (message.getBody() != null) {
DelayInformation inf = (DelayInformation) message.getExtension(
"x", "jabber:x:delay");
Date sentDate;
if (inf != null) {
sentDate = inf.getStamp();
} else {
sentDate = new Date();
}
Log.i(TAG, "Receive old message: date="
+ sentDate.toLocaleString() + " ; message="
+ message.getBody());
android.os.Message msg = new android.os.Message();
msg.what = RECEIVE;
Bundle bd = new Bundle();
bd.putString("from", from);
bd.putString("body", message.getBody());
msg.setData(bd);
handler.sendMessage(msg);
}
}
}
2,下边就是成员了,一个聊天室没成员多离谱的事情啊,我们主要做的是把成员列表逃出来。其实有几种方法可以弄出来的,我只是简单的获取成员的昵称。可能有朋友会问为什么不获取成员的信息呢。这个下一篇我会告诉你。
[java] view
plaincopy
/**
* 获取聊天室的所有成员
*/
private void getAllMember() {
Log.i(TAG, "获取聊天室的所有成员");
affiliates.clear();
new Thread(new Runnable() {
@Override
public void run() {
try {
Iterator<String> it = muc.getOccupants();
while (it.hasNext()) {
String name = it.next();
name = name.substring(name.indexOf("/") + 1);
affiliates.add(name);
Log.i(TAG, "成员名字;" + name);
}
} catch (Exception e) {
e.printStackTrace();
}
android.os.Message msg = new android.os.Message();
msg.what = MEMBER;
handler.sendMessage(msg);
}
}).start();
}
这篇结束,下篇写写聊天室的一些权限的变更、成员的变更、主题变更什么的。
Android基于XMPP Smack openfire 开发的聊天室(三) 【新旧记录、踢人】
1,聊天室新旧消息的记录,先看看一段服务器返回的XML吧[html] view
plaincopy
<message
from='darkcave@chat.shakespeare.lit/firstwitch'
to='hecate@shakespeare.lit/broom'
type='groupchat'>
<body>Thrice the brinded cat hath mew'd.</body>
<delay xmlns='urn:xmpp:delay'
from='crone1@shakespeare.lit/desktop'
stamp='2002-10-13T23:58:37Z'/>
</message>
<message
from='darkcave@chat.shakespeare.lit/secondwitch'
to='hecate@shakespeare.lit/broom'
type='groupchat'>
<body>Thrice and once the hedge-pig whined.</body>
<delay xmlns='urn:xmpp:delay'
from='wiccarocks@shakespeare.lit/laptop'
stamp='2002-10-13T23:58:43Z'/>
</message>
<message
from='darkcave@chat.shakespeare.lit/thirdwitch'
to='hecate@shakespeare.lit/broom'
type='groupchat'>
<body>Harpier cries 'Tis time, 'tis time.</body>
<delay xmlns='urn:xmpp:delay'
from='hag66@shakespeare.lit/pda'
stamp='2002-10-13T23:58:49Z'/>
</message>
上边这段是一个聊天室返回的XML数据,是历史消息。就在这里要告诉大家的是,如果是新消息的话就没有<delay>节点。所以我们以这个为根据来判断。下边贴些我的代码,因为服务器被我们给改了,所以返回的有些出入。但八九不离十。原理一样。
[java] view
plaincopy
DelayInformation inf = (DelayInformation) message.getExtension(
"x", "jabber:x:delay");
System.out.println("判断消息");
if (inf == null && count >= 1) {
System.out.println("新消息来了");
isHistory = true;
} else {
System.out.println("这是旧的消息");
}
这段代码写哪里呢,就是写在消息的监听里头
[java] view
plaincopy
/**
* PacketListener 通过一个规定的过滤器提供一个机制来监听数据包
*
* @author liaonaibo
*
*/
class ChatPacketListener implements PacketListener
重写它的方法
[java] view
plaincopy
@Override
public void processPacket(Packet packet)
好了,下边讲踢人吧
2,踢人:
踢人很简单,只要你是房主,或管理员什么的,主持人也行。这些高权限的才能有踢人的权限。
[java] view
plaincopy
// 踢人
try {
String nickName = affiliates.get(id);
//得到的昵称包含的房间名和服务名的一段很长数据。好像是吧。哈哈,忘记了。所以截取吧
muc.kickParticipant(nickName
.substring(nickName.indexOf("]") + 1), "看你不爽就 踢了你");
android.os.Message msg = new android.os.Message();
msg.what = MEMBER;
handler.sendMessage(msg);
Toast.makeText(this, "哈哈,踹了你", Toast.LENGTH_LONG).show();
} catch (XMPPException e) {
e.printStackTrace();
Toast.makeText(this, "你没有权利踢人", Toast.LENGTH_LONG).show();
}
有的哥们会疑惑,说如果根据昵称来踢人,那会不会把重名的人都一起踢了。这个问题我也想过,这个也是测试的一个弊端。但是如果通过spark来看,即便你用同样的名字加入了房间,要么会报错,要么就是系统自动在你的名字后边加个2,呵呵,够2吧。如果有人坚持要用自己的昵称,这个可以的,具体怎么样做,下次再阐述。
Android基于XMPP Smack openfire 开发的聊天室(四) 【创建房间、表单;报文】
这篇就主要讲创建房间和报文吧。1,创建房间:
其实创建房间很简单,两句话就搞定了。但你要知道,简单的同时,服务器可没帮你配置房间的信息什么的。所以一般用下边的方法来创建房间别人是进不去的。
[java] view
plaincopy
// 使用XMPPConnection创建一个MultiUserChat
MultiUserChat muc = new MultiUserChat(conn1, "myroom@conference.jabber.org");
//创建聊天室
muc.create("testbot");
// 发送一个空表单配置这显示我们想要一个instant room
muc.sendConfigurationForm(new Form(Form.TYPE_SUBMIT));
所以咱们还是手动配置一些信息吧,免得留后患。不过在这里要给朋友们提个醒,我觉得不论是哪种方法都很狗屎,为什么狗屎。你看看那MUC,本来就是一个聊天室了,还要让你在聊天室创建另个聊天室,老妈生孩子,然后孩子和老妈并排。狗屎啊,不过不排除我理解错误。希望高人能给与指点。
[java] view
plaincopy
try {
// 创建聊天室
muc.create(Constants.vCard.getNickName().toString());
// 获得聊天室的配置表单
Form form = muc.getConfigurationForm();
// 根据原始表单创建一个要提交的新表单。
Form submitForm = form.createAnswerForm();
// 向要提交的表单添加默认答复
for (Iterator fields = form.getFields(); fields.hasNext();) {
FormField field = (FormField) fields.next();
if (!FormField.TYPE_HIDDEN.equals(field.getType())
&& field.getVariable() != null) {
// 设置默认值作为答复
submitForm.setDefaultAnswer(field.getVariable());
}
}
// 设置聊天室的新拥有者
// List owners = new ArrayList();
// owners.add("liaonaibo2\\40slook.cc");
// owners.add("liaonaibo1\\40slook.cc");
// submitForm.setAnswer("muc#roomconfig_roomowners", owners);
// 设置聊天室是持久聊天室,即将要被保存下来
submitForm.setAnswer("muc#roomconfig_persistentroom", true);
// 房间仅对成员开放
submitForm.setAnswer("muc#roomconfig_membersonly", false);
// 允许占有者邀请其他人
submitForm.setAnswer("muc#roomconfig_allowinvites", true);
// 能够发现占有者真实 JID 的角色
// submitForm.setAnswer("muc#roomconfig_whois", "anyone");
// 登录房间对话
submitForm.setAnswer("muc#roomconfig_enablelogging", true);
// 仅允许注册的昵称登录
submitForm.setAnswer("x-muc#roomconfig_reservednick", true);
// 允许使用者修改昵称
submitForm.setAnswer("x-muc#roomconfig_canchangenick", false);
// 允许用户注册房间
submitForm.setAnswer("x-muc#roomconfig_registration", false);
// 发送已完成的表单(有默认值)到服务器来配置聊天室
muc.sendConfigurationForm(submitForm);
} catch (XMPPException e) {
e.printStackTrace();
}
还有个细节,有朋友估计看到我注释的那里有\\40的代码。原因是在XMPP中的唯一标示JID不允许出现@,所以要转义。OK?
2,报文
有朋友说报文干嘛,其实我开始也觉得有点多余,但想想类似QQ群那样,人下线了,头像名字变灰色。如果是openfire服务器的话,你下线了就直接把你去除。所以我们要给服务器发送个广播。具体处理那是以后的是了。看看这段报文:
[html] view
plaincopy
<iq id="902U0-48" type="set">
<query xmlns="jabber:iq:private">
<storage xmlns="storage:bookmarks">
<conference name="ccc" autojoin="true" jid="ccc@conference.xmpp.chaoboo.com" ></conference>
</storage>
</query>
</iq>
报文可以根据需要来改服务器的插件。
下边发送的报文:
[java] view
plaincopy
/**
* 用户加入时向服务器发送的报文
* @return
*/
public IQ joinXml(){
IQ iq = new IQ() {
public String getChildElementXML() {
StringBuilder buf = new StringBuilder();
buf.append("<query xmlns=\"jabber:iq:private\">");
buf.append("<storage xmlns=\"storage:bookmarks\">");
buf.append("<").append("conference").append(" name=\"ccc\"").append(" autojoin=\"false\"").append("");
buf.append("</storage>");
buf.append("</query>");
return buf.toString();
}
};
iq.setType(IQ.Type.SET);
//方法如名,这里是设置这份报文来至那个JID,后边的/smack是这段信息来至哪个端,如spark端就是/spark,android就是/Smack
iq.setFrom(Constants.USERNAME+"@naibo.liao.com/Smack");
return iq;
}
怎么发送呢?可以通过连接,就是XMPPCONNECTION来发送:
[java] view
plaincopy
Constants.conn.sendPacket(leaveXml());
OK,停一会,手酸了
Android基于XMPP Smack openfire 开发的聊天室(五) 【邀请、被邀请】
1,邀请:很简单的一句话,邀请的时候要传入被邀请者的JID,邀请信息可以为空。
[java] view
plaincopy
muc.invite(userjid, "进来我们聊妹子");
简单的同时我们要知道它发生了什么,当这句话执行会向服务器发送一段报文,如下:
[html] view
plaincopy
<message
from='crone1@shakespeare.lit/desktop'
to='darkcave@chat.shakespeare.lit'>
<x xmlns='http://jabber.org/protocol/muc#user'>
<invite to='hecate@shakespeare.lit'>
<reason>
进来我们聊妹子
</reason>
</invite>
</x>
</message>
2,被邀请
被邀请呢,我们可以开个监听吧,要不要进房间先不判断了,我这里如果有邀请就直接进房间。有需要的朋友可以自己琢磨:
[java] view
plaincopy
MultiUserChat.addInvitationListener(Constants.conn,
new InvitationListener() {
// 对应参数:连接、 房间JID、房间名、附带内容、密码、消息
@Override
public void invitationReceived(Connection conn,
String room, String inviter, String reason,
String password, Message message) {
Log.i(TAG, "收到来自 " + inviter + " 的聊天室邀请。邀请附带内容:"
+ reason);
Intent intent = new Intent(MucService.this,
ActivityMultiRoom.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra("jid", room);
intent.putExtra("action", "join");
startActivity(intent);
}
});
Android基于XMPP Smack openfire 开发的聊天室(六) 【加入房间、权限错误】
1,加入房间错误,通常一个就是密码错误。此时服务器会返回以下报文:[html] view
plaincopy
<presence
from='darkcave@chat.shakespeare.lit'
to='hag66@shakespeare.lit/pda'
type='error'>
<x xmlns='http://jabber.org/protocol/muc'/>
<error type='auth'>
<not-authorized xmlns='urn:ietf:params:xml:ns:xmpp-stanzas'/>
</error>
</presence>
官网意思:如果房间要求密码验证而用户不能提供(或密码错误), 服务必须 MUST 拒绝访问这个房间并且通知该用户它们是未被授权的; 具体方法是返回一个类型为"error"的出席信息节并标明 <not-authorized/> 错误
解决如下:
[java] view
plaincopy
// 使用XMPPConnection创建一个MultiUserChat
MultiUserChat muc2 = new MultiUserChat(conn1,
"myroom@chat.shakespeare.lit/thirdwitch");
// 用户2使用密码加入新聊天室
// 聊天室服务将会决定要发送的历史记录数量
muc2.join("testbot2", "password");
报文如下:
[html] view
plaincopy
<presence
from='hag66@shakespeare.lit/pda'
to='myroom@chat.shakespeare.lit/thirdwitch'>
<x xmlns='http://jabber.org/protocol/muc'>
<password>password</password>
</x>
</presence>
2,权限错误:
这个没文本,当然如果你加入一个房间后,要注意的是,如果第一次加入别人的房间,没有被授予成员权限的时候,你仅仅是一个游客。除了说话。也不能检索聊天室成员的信息,仅可以获取成员的昵称,除此别无其他。通常不注意会用这样贸然去获取成员信息是,会照成错误,返回402(进入房间后获取超越权限错误:权限不足)等信息。以下是其他错误信息
401 | Error | Presence | 进入一个房间 | 通知用户需要密码 |
403 | Error | Presence | 进入一个房间 | 通知用户他或她被房间禁止了 |
404 | Error | Presence | 进入一个房间 | 通知用户房间不存在 |
405 | Error | Presence | 进入一个房间 | 通知用户限制创建房间 |
406 | Error | Presence | 进入一个房间 | 通知用户必须使用保留的房间昵称 |
407 | Error | Presence | 进入一个房间 | 通知用户他或她不在成员列表中 |
409 | Error | Presence | 进入一个房间 | 通知用户他或她的房间昵称正在使用或被别的用户注册了 |
503 | Error | Presence | 进入一个房间 | 通知用户已经达到最大用户数 |
Android基于XMPP Smack openfire 开发的聊天室(七) 【成员状态、自身状态】
1,聊天是成员的监听,加到MUC里边就行[java] view
plaincopy
/**
* 聊天室成员的监听器
*
* @author 廖乃波
*
*/
class MyParticipantStatusListener implements ParticipantStatusListener {
@Override
public void adminGranted(String arg0) {
Log.i(TAG, "授予管理员权限" + arg0);
}
@Override
public void adminRevoked(String arg0) {
Log.i(TAG, "移除管理员权限" + arg0);
}
@Override
public void banned(String arg0, String arg1, String arg2) {
Log.i(TAG, "禁止加入房间(拉黑,不知道怎么理解,呵呵)" + arg0);
}
@Override
public void joined(String arg0) {
Log.i(TAG, "执行了joined方法:" + arg0 + "加入了房间");
// 更新成员
getAllMember();
android.os.Message msg = new android.os.Message();
msg.what = MEMBER;
handler.sendMessage(msg);
}
@Override
public void kicked(String arg0, String arg1, String arg2) {
Log.i(TAG, "踢人" + arg0 + "被踢出房间");
}
@Override
public void left(String arg0) {
String lefter = arg0.substring(arg0.indexOf("/") + 1);
Log.i(TAG, "执行了left方法:" + lefter + "离开的房间");
// 更新成员
getAllMember();
android.os.Message msg = new android.os.Message();
msg.what = MEMBER;
handler.sendMessage(msg);
}
@Override
public void membershipGranted(String arg0) {
Log.i(TAG, "授予成员权限" + arg0);
}
@Override
public void membershipRevoked(String arg0) {
Log.i(TAG, "成员权限被移除" + arg0);
}
@Override
public void moderatorGranted(String arg0) {
Log.i(TAG, "授予主持人权限" + arg0);
}
@Override
public void moderatorRevoked(String arg0) {
Log.i(TAG, "移除主持人权限" + arg0);
}
@Override
public void nicknameChanged(String arg0, String arg1) {
Log.i(TAG, "昵称改变了" + arg0);
}
@Override
public void ownershipGranted(String arg0) {
Log.i(TAG, "授予所有者权限" + arg0);
}
@Override
public void ownershipRevoked(String arg0) {
Log.i(TAG, "移除所有者权限" + arg0);
}
@Override
public void voiceGranted(String arg0) {
Log.i(TAG, "给" + arg0+"授权发言");
}
@Override
public void voiceRevoked(String arg0) {
Log.i(TAG, "禁止" + arg0+"发言");
}
}
2,自身状态监听
[java] view
plaincopy
class MyUserStatusListener implements UserStatusListener {
/*这里实现默认方法即可*/
}
相关文章推荐
- Android基于XMPP Smack openfire 开发的聊天室(二) 【聊天信息、成员】
- Android基于XMPP Smack openfire 开发的聊天室(四) 【创建房间、表单;报文】
- Android基于XMPP Smack openfire 开发的聊天室(四) 【创建房间、表单;报文】
- Android基于XMPP Smack openfire 开发的聊天室(二) 【聊天信息、成员】
- Android基于XMPP Smack openfire 开发的聊天室(五) 【邀请、被邀请】
- Android基于XMPP Smack openfire 开发的聊天室(三) 【新旧记录、踢人】
- Android基于XMPP Smack openfire 开发的聊天室
- Android基于XMPP Smack openfire 开发的聊天室(二) 【聊天信息、成员】
- Android基于XMPP Smack openfire 开发的聊天室(三) 【新旧记录、踢人】
- Android基于XMPP Smack openfire 开发的聊天室(六) 【加入房间、权限错误】
- Android基于XMPP Smack openfire 开发的聊天室(五) 【邀请、被邀请】
- Android基于XMPP Smack openfire 开发的聊天室(六) 【加入房间、权限错误】
- Android基于XMPP Smack openfire 开发的聊天室(一)【会议服务、聊天室列表、加入】
- Android基于XMPP Smack openfire 开发的聊天室(三) 【新旧记录、踢人】
- Android基于XMPP Smack openfire 开发的聊天室(七) 【成员状态、自身状态】
- Android基于XMPP Smack openfire 开发的聊天室(二) 【聊天信息、成员】
- Android基于XMPP Smack openfire 开发的聊天室(四) 【创建房间、表单;报文】
- Android基于XMPP Smack openfire 开发的聊天室(一)
- Android基于XMPP Smack openfire 开发的聊天室(七) 【成员状态、自身状态】
- Android基于XMPP Smack openfire 开发的聊天室(五) 【邀请、被邀请】