XMPP协议简介
XMPP协议(Extensible Messaging and PresenceProtocol,可扩展消息处理现场协议)是一种基于XML的协议,目的是为了解决及时通信标准而提出来的,最早是在Jabber上实现的。它继承了在XML环境中灵活的发展性。因此,基于XMPP的应用具有超强的可扩展性。并且XML很易穿过防火墙,所以用XMPP构建的应用不易受到防火墙的阻碍。利用XMPP作为通用的传输机制,不同组织内的不同应用都可以进行有效的通信。
这篇文章有基本的介绍,http://blog.csdn.net/xutaozero21/article/details/4873439
IM
Instant Messenger,及时通信软件,就是大家使用的QQ、MSN Messenger和Gtalk等等。其中Gtalk 就是基于XMPP 协议的一个实现,其他的则不是。当前IM 几乎作为每个上网者必然使用的工具,在国外的大型企业中有一些企业级的IM应用,但是其商业价值还没完全发挥出来。设想既然XMPP 协议是一个公开的协议,那么每个企业都可以利用它来开发适合本身企业工作,提高自身生产效率的IM;甚至,你还可以在网络游戏中集成这种通信软件,不但让你可以边游戏边聊天,也可以开发出适合游戏本身的IM 应用,比如说一些游戏关键场景提醒功能,团队语音交流等等都可以基于IM来实现。
本文主要讲解在android使用xmpp协议进行即时通信,所涉及3个主要的东西,它们是openfire、smack和spark,这个三个东东结合起来就是完整的xmpp IM实现,这里简单介绍一下这3个东东在下文的作用:
openfire主要是作为服务器,负责管理客户端的通信连接,以及提供客户端一些通信信息和连接信息。
Smack主要是xmpp协议的实现,提供了一套很好的api,所以下面操作xmpp都是通过使用smack的api来实现,当然因为是在android里,所以使用的是asmack这个包,里面方法跟smack包差不多。
Spark 是IM客户端的实现,其实就是使用了smack 的api实现的。
下图展示了三者之间的关系:(很明显这个图是偷别人的,具体是哪里我忘了,因为资料都是复制到文档后慢慢研究看的)
从图上可以了解到,client 端和server端都可以通过插件的方式来进行扩展,smack是二者传递数据的媒介。
配置openfire服务器
具体步骤请移步:http://javatech.blog.163.com/blog/static/1766322992010111725339587/
配置成功如果以后ip地址变了,那肯定又是开不了,解决办法请移步:http://blog.csdn.net/HappySheepherder/article/details/4707124
配置成功后,在服务器创建一个简单的用户来测试,然后安装spark,设置好服务器的ip与端口,使用刚才创建的用户登录,登录OK说明服务器成功搭建。
Android IM功能(因为是测试demo,因此界面超级简陋,代码都是给出重要的一部分,剩余的可以在最后下面项目查看)
配置要求
android 2.2、 asmack-jse.jar、myeclipse
连接服务器
在打开软件后会开始初始化,完成与openfire服务器的连接,设置一些配置
static {
XMPPConnection.DEBUG_ENABLED = true;
final ConnectionConfiguration connectionConfig = new ConnectionConfiguration(
host, 5222, "");
// Google talk
// ConnectionConfiguration connectionConfig = new
// ConnectionConfiguration(
// "talk.google.com", 5222, "gmail.com");注册模块
注册有两种方法:一种是用createAccount ,不过我测试了一下发现不能创建用户,具体原因不详,下面介绍第二种。
如上图:注册成功后服务器将多了ggg用户。
具体实现如下:
Registration reg = new Registration();
reg.setType(IQ.Type.SET);
reg.setTo(ConnectionSingleton.getInstance().getServiceName());
reg.setUsername(username.getText().toString());
reg.setPassword(password.getText().toString());
reg.addAttribute("android", "geolo_createUser_android");
System.out.println("reg:" + reg);
PacketFilter filter = new AndFilter(new PacketIDFilter(reg
.getPacketID()), new PacketTypeFilter(IQ.class));
PacketCollector collector = ConnectionSingleton.getInstance()
.createPacketCollector(filter);
ConnectionSingleton.getInstance().sendPacket(reg);
result = (IQ) collector.nextResult(SmackConfiguration
.getPacketReplyTimeout());
// Stop queuing results
collector.cancel();
if (result == null) {
Toast.makeText(getApplicationContext(), "服,
Toast.LENGTH_SHORT).show();
} else if (result.getType() == IQ.Type.ERROR) {
if (result.getError().toString().equalsIgnoreCase(
"conflict(409)")) {
Toast.makeText(getApplicationContext(), "这,
Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(getApplicationContext(), "注,
Toast.LENGTH_SHORT).show();
}
} else if (result.getType() == IQ.Type.RESULT) {
Toast.makeText(getApplicationContext(), "恭,
Toast.LENGTH_SHORT).show();
}
使用注册类,设置好注册的用户名密码和一些属性字段,直接设置包过滤,根据这个过滤创建一个结果集合,发送注册信息包,等待获取结果,剩余就是判断结果内容.
登录模块
登录比较简单
ConnectionSingleton.getInstance().connect();// connect
String account = etUsername.getText().toString();
String password = etPassword.getText().toString();
// 保存用户和密码
ActivityLogin.util.saveString(ACCOUNT_KEY, account);
ActivityLogin.util.saveString(PASSWORD_KEY, password);
ConnectionSingleton.getInstance().login(account, password);// login
// login success
System.out.println("login success");
ActivityLogin.mCurrentAccount = account;
System.out.println(ConnectionSingleton.getInstance()
.getUser());
// 登录成功后发现在线状态
Presence presence = new Presence(Presence.Type.available);
ConnectionSingleton.getInstance().sendPacket(presence);
// 开始主界面
Intent intent = new Intent(ActivityLogin.this,
ActivityMain.class);
startActivity(intent);
获取联系人模块(ActivityMain 主界面)
获取联系人并将相关信息保存到一个list数组里,最后通知listview更新界面
roster = ActivityMain.connection.getRoster();
public void updateRoster() {
Collection<RosterEntry> entries = roster.getEntries();
for (RosterEntry entry : entries) {
System.out.print(entry.getName() + " - " + entry.getUser() + " - "
+ entry.getType() + " - " + entry.getGroups().size());
Presence presence = roster.getPresence(entry.getUser());
System.out.println(" - " + presence.getStatus() + " - "
+ presence.getFrom());
User user = new User();
user.setName(entry.getName());
user.setUser(entry.getUser());
user.setType(entry.getType());
user.setSize(entry.getGroups().size());
user.setStatus(presence.getStatus());
user.setFrom(presence.getFrom());
userinfos.add(user);
}
rosterAdapter.notifyDataSetChanged();
}
单人聊天模块
第一次修改:
在主界面点击选择一个用户,进入聊天Activity,ActivityChat先获取传过来的用户,创建聊天类并对该用户设置消息监听
ChatManager chatmanager = ConnectionSingleton.getInstance()
.getChatManager();
// get user
Intent intent = getIntent();
String user = intent.getStringExtra("user");
System.out.println("user:" + user);
// new a session
newChat = chatmanager.createChat(user, null);
// 监听聊天消息
chatmanager.addChatListener(new ChatManagerListenerEx());
// send message
try {
newChat.sendMessage("im bird man");
} catch (XMPPException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
监听类
public class ChatManagerListenerEx implements ChatManagerListener {
@Override
public void chatCreated(Chat chat, boolean arg1) {
// TODO Auto-generated method stub
chat.addMessageListener(ml);
}
}
public class MessageListenerEx implements MessageListener {
@Override
public void processMessage(Chat arg0, Message message) {
String result = message.getFrom() + ":" + message.getBody();
System.out.println(result);
android.os.Message msg = new android.os.Message();
msg.what = 0;
Bundle bd = new Bundle();
bd.putString("msg", result);
msg.setData(bd);
handler.sendMessage(msg);
}
}
所获取到的消息都是通过handler来更新UI
public Handler handler = new Handler() {
@Override
public void handleMessage(android.os.Message msg) {
switch (msg.what) {
case 0: {
String result = msg.getData().getString("msg");
record.setText(record.getText() + "\n" + result);
}
break;
default:
break;
}
}
};
aaa跟bbb 的聊天
第二次修改:
第一次的测试,发现如果多个人之间都成为好友,那么他们之间的聊天就出现了接收不到的信息,当然在跟spark测试时,spark可以收到android端的信息,不过android客户端是收到这个信息,不过却没有显示出来,具体原因还不太清楚。因此在第二次修改我改成监听所有聊天信息包,然后再分析包的归属,分发到对应的聊天窗口。
这里就是监听到包后打印的消息,打印出了jid和消息内容
public class XmppMessageManager implements ChatManagerListener {
private XMPPConnection _connection;
private ChatManager manager = null;
public void initialize(XMPPConnection connection) {
_connection = connection;
manager = _connection.getChatManager();
manager.addChatListener(this);
}
@Override
public void chatCreated(Chat chat, boolean arg1) {
// TODO Auto-generated method stub
chat.addMessageListener(new MessageListener() {
public void processMessage(Chat newchat, Message message) {
// 若是聊天窗口存在,将消息转往目前窗口
// 若窗口不存在,创建新的窗口
System.out
.println(message.getFrom() + ":" + message.getBody());
if (!ActivityMain.chats.containsKey(message.getFrom())) {
ActivityMain.chats.put(message.getFrom(), newchat);
} else {
}
}
});
}
}
主要就是重写了ChatManagerListener类的监听,分发处理暂时没有想好怎么写。接着在后台启动service就可以开始监听,行了第一次修改那些可以去掉了^0^。
多人聊天模块
也是在主界面的菜单进入ActivityMultiChat,该界面显示所创建的房间,点击就跳转到ActivityMultiRoom 。
获取所有房间比较简单,只需执行下面这段代码
hostrooms = MultiUserChat.getHostedRooms(ActivityMain.connection,
"conference.zhanghaitao-pc");
跳转到后获取要加入的房间的jid,并创建监听。
jid = getIntent().getStringExtra("jid");
//后面服务名称必需是创建房间的那个服务
String multiUserRoom = jid;
try {
muc = new MultiUserChat(ActivityMain.connection, multiUserRoom);
// 创建聊天室,进入房间后的nickname
muc.join(ActivityLogin.mCurrentAccount);
Log.v(TAG, "join success");
} catch (XMPPException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
ChatPacketListener chatListener = new ChatPacketListener(muc);
muc.addMessageListener(chatListener);
监听大概的流程跟单人聊天差不多,都是handler来操作。不过多人聊天是重写了PacketListener。具体如下(不过该方法是监听房间的信息,也就是说显示的是以房间为名字的消息):
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.w(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);
}
}
}
下载模块
在主界面对着用户名长按,进入下载activity。进入activityFileTransfer,点击传输按钮即可将文件传输给之前选择的用户,当然这里做得比较简单,并没有拒绝功能,一旦发现有文件就接受。
FileTransferManager transfer = new FileTransferManager(
ActivityMain.connection);
String destination = user;
OutgoingFileTransfer out = transfer
.createOutgoingFileTransfer(destination + "/Smack");
那用户是如何监听到有文件并且接受呢?在进入主界面的时候就已经开始了一个service(fileListenerService),该服务创建文件的监听类(XmppFileManager),监听类主要继承FileTransferListener 重写里面的fileTransferRequest方法。
File saveTo;
// set answerTo for replies and send()
answerTo = request.getRequestor();
if (!Environment.MEDIA_MOUNTED.equals(Environment
.getExternalStorageState())) {
send("External Media not mounted read/write");
return;
} else if (!landingDir.isDirectory()) {
send("The directory " + landingDir.getAbsolutePath()
+ " is not a directory");
return;
}
saveTo = new File(landingDir, request.getFileName());
if (saveTo.exists()) {
send("The file " + saveTo.getAbsolutePath() + " already exists");
// delete
saveTo.delete();
// return;
}
IncomingFileTransfer transfer = request.accept();
send("File transfer: " + saveTo.getName() + " - "
+ request.getFileSize() / 1024 + " KB");
try {
transfer.recieveFile(saveTo);
send("File transfer: " + saveTo.getName() + " - "
+ transfer.getStatus());
double percents = 0.0;
while (!transfer.isDone()) {
if (transfer.getStatus().equals(Status.in_progress)) {
percents = ((int) (transfer.getProgress() * 10000)) / 100.0;
send("File transfer: " + saveTo.getName() + " - "
+ percents + "%");
} else if (transfer.getStatus().equals(Status.error)) {
send(returnAndLogError(transfer));
return;
}
Thread.sleep(1000);
}
if (transfer.getStatus().equals(Status.complete)) {
send("File transfer complete. File saved as "
+ saveTo.getAbsolutePath());
} else {
send(returnAndLogError(transfer));
}
} catch (Exception ex) {
String message = "Cannot receive the file because an error occured during the process."
+ ex;
Log.e(TAG, message, ex);
send(message);
}
// connectionConfig.setSASLAuthenticationEnabled(false);
ActivityMain.connection = new XMPPConnection(connectionConfig);
ActivityMain.connection.DEBUG_ENABLED = true;
ProviderManager pm = ProviderManager.getInstance();
configure(pm);
}
分享到:
相关推荐
本文将深入探讨如何在Android平台上使用ASmack库来实现XMPP聊天通讯,包括用户注册、登录、多人聊天室以及文件传输等核心功能。 首先,ASmack是Smack库的一个分支,专门为Android平台优化,它允许开发者轻松地集成...
在Android平台上实现即时通讯(Instant Messaging,IM)功能,开发者经常使用开源库ASMACK来集成XMPP协议。本文将深入探讨ASMACK如何帮助开发者完成注册、登录、聊天、创建多人聊天室以及文件传输等核心功能。 **...
ASmack即时聊天是一款基于Android平台的即时通讯(IM)应用示例,它利用了XMPP(Extensible Messaging and Presence Protocol)协议来实现用户之间的实时通信。XMPP是一种开放的标准,广泛用于构建即时消息和在线...
【基于asmack的聊天demo】是一个专为Android平台设计的即时通讯示例项目,它利用了asmack库来实现XMPP(Extensible Messaging and Presence Protocol)协议,这是一种开放标准的即时通讯协议,广泛用于实现聊天、...
【标题】基于asmack的在线聊天源代码——调通可运行 在移动应用开发中,实现即时通讯(Instant Messaging, IM)功能是一项常见的需求。Asmack是一个Android平台上的XMPP(Extensible Messaging and Presence ...
android_asmack_注册_登陆_聊天_多人聊天室_文件传输
**aSmack测试代码详解——即时会话与群组聊天** aSmack是Android平台上一个流行的XMPP(Extensible Messaging and Presence Protocol)库,用于实现即时通讯功能。它基于Java的Smack库,并针对Android环境进行了...
asmack asmack asmack asmack asmack
ASMack提供了`XMPPConnection`接口,用于管理XMPP协议中的实体,如用户(JID)、群组和多用户聊天室。`Roster`类用于处理好友列表,`ChatManager`处理一对一聊天,而`MUCManager`则处理多用户聊天室。 **4. 消息与...
**asmack 4.0.5 帮助文档** ASMack是一款开源的Java库,专为Android平台设计,用于实现XMPP(Extensible Messaging and Presence Protocol)协议。XMPP是一种基于XML的实时通信协议,广泛应用于即时消息、在线状态...
【asmack-4.0.7源码】是一款专为XMPP(Extensible Messaging and Presence Protocol)协议设计的开源库,用于实现即时通讯和在线状态功能。ASMACK是Smack库的一个增强版本,针对Android平台进行了优化,解决了在...
Android asmack 注册 登陆 聊天 多人聊天室 文件传输 本文主要讲解在 Android 平台上使用 XMPP 协议实现即时通信,并详细介绍了 openfire、smack 和 spark 三个组件的作用。 一、XMPP 协议简介 XMPP 协议...
标题 "android聊天程序 XMPP-asmack" 描述了一个基于Android平台的即时通讯应用,它利用了XMPP(Extensible Messaging and Presence Protocol)协议,并采用了asmack库来实现。ASMACK是XMPP的一个轻量级Java库,专为...
【asmack-master.zip】是一个包含asmack库源代码的压缩包,主要用于Android平台上的即时通讯(IM)开发,尤其适用于基于XMPP(Extensible Messaging and Presence Protocol)协议的应用。XMPP是一种开放标准,广泛...
标题中的"asmack-android"指的是一个专为Android平台设计的XMPP(Extensible Messaging and Presence Protocol)客户端库。...对于希望构建聊天、社交或协作应用的开发者来说,理解并掌握ASMACK的使用是必不可少的。
XMPP(Extensible Messaging and ...综上所述,通过理解XMPP协议,利用ASMACK库,开发者可以在Android平台上构建强大的即时通讯功能,包括聊天和文件传输。同时,不断优化和调整,以适应移动设备的特性和用户需求。
【asmack连接Openfire测试】是针对XMPP(Extensible Messaging and Presence Protocol)协议实现的一种客户端连接示例,主要用于教初学者如何使用asmack库与Openfire服务器进行交互。Openfire是一款开源、基于XMPP...
它不仅用于即时消息,还可以支持文件传输、视频通话、多用户聊天等多种功能。XMPP通过服务器进行中继,使得用户可以与其他网络上的用户进行交互,类似于电子邮件的工作方式。 **2. asmack的特性** - **Android兼容...