Android asmack使用介绍
文章最后附上Demo地址
一、简介
1.什么是Xmpp协议?
XMPP协议(Extensible Messaging and PresenceProtocol,可扩展消息处理现场协议)是一种基于XML的协议,目的是为了解决及时通信标准而提出来的,最早是在Jabber上实现的。它继承了在XML环境中灵活的发展性。因此,基于XMPP的应用具有超强的可扩展性。并且XML很易穿过防火墙,所以用XMPP构建的应用不易受到防火墙的阻碍。利用XMPP作为通用的传输机制,不同组织内的不同应用都可以进行有效的通信。
2.什么是IM?
IM(Instant Messenger,即时通信软件),比如QQ、微信、Gtalk(谷歌推出的一款IM软件),其中Gtalk 就是基于XMPP 协议的一个实现。
3.什么是asmack?
smack是一套很好的开源即时通讯api,是基于Xmpp协议的实现,而在Android,则提供了一个asmack的jar包,其功能以及用法跟smack一致。
4.如何搭建一个Android平台完整的XMPP IM 实现?
服务器端——Openfire,Android客户端——基于asmack的使用
(注:openfire是基于XMPP 协议的IM 的服务器端的一个实现。搭建OpenFire服务器请看另外篇文章OpenFire服务器搭建)
5.asmack优点
aSmack是一个简单的,功能强大的类库。给用户发送信息只需三行代码便可完成
XMPPConnection connection = new XMPPTCPConnection(”jabber.org“)connection.login(”mtucker”, “password”)connection.createChat(”jsmith@jivesoftware.com“).sendMessage(”Howdy!”)
不会强迫你向其他类库那样,在信息包层面进行编码。它提供了更加智能化的类比如Chat,能使你的工作更富效率。
不需要你熟悉XMPP,甚至是XML格式。
易于实现机-机对话。
Apace License下的开源软件。你可以把它用于你的商业或非商业程序。
二、准备工作
使用asmack之前本地或者远程必须有个服务器openFire支持,如何搭建请看另外篇文章OpenFire服务器搭建
搭建完开启openfire服务器(进入Openfire安装目录,点击bin目录下的openfired.exe)
测试是否开启服务器 在浏览器输入 127.0.0.1:9090
- 在项目中添加 asmack的jar包
三、在Android中使用asmack
在Android客户端需要实现IM及时通讯的功能点有:
- 保持与服务器的通信
- 用户注册
- 用户登录
- 好友添加与申请、删除好友
好友列表、群列表
好友聊天:消息发送与接收,图片发送与接收
- 群聊天:消息发送与接收
下面将会介绍如何去使用asmack去实现各个功能点
1.保持与服务器的通信
在两人进行聊天时,需要实时的拿到消息,必须保证对服务器的实时访问,不断地拿数据,所有需要保持一个与服务器的长连接,进行数据交互。
所以asmack提供了一个XMPPConnection的类来保持一个与服务器的长连接(建议采用单例模式)。
具体使用如下:
XMPPConnection.DEBUG_ENABLED = true;ConnectionConfiguration conConfig = new ConnectionConfiguration( SERVER_HOST, SERVER_PORT, SERVER_NAME);conConfig.setReconnectionAllowed(true);conConfig.setSendPresence(true);conConfig.setSASLAuthenticationEnabled(true);XMPPConnection connection = new XMPPConnection(conConfig);connection.connect();configureConnection();
2.用户注册
用户必须注册一个账户,作为一个用户身份识别的作用。
asmack提供了一个Registration的类使用(XmppConnection是自定义的一个XMPPconnection配置工具类,下文将会经常用到),注册操作是一个网络耗时操作,要放在子线程中去执行。
Registration reg = new Registration();reg.setType(IQ.Type.SET);reg.setTo(XmppConnection.getConnection().getServiceName());reg.setUsername(account);reg.setPassword(pass);reg.addAttribute("name", name);reg.addAttribute("email", email);reg.addAttribute("android", "geolo_createUser_android");PacketFilter filter = new AndFilter(new PacketIDFilter(reg .getPacketID()), new PacketTypeFilter(IQ.class));PacketCollector collector = XmppConnection.getConnection() .createPacketCollector(filter);XmppConnection.getConnection().sendPacket(reg);IQ result = (IQ) collector.nextResult(SmackConfiguration .getPacketReplyTimeout());collector.cancel();if (result == null) { } else if (result.getType() == IQ.Type.ERROR) { if (result.getError().toString() .equalsIgnoreCase("conflict(409)")) { } else { }} else if (result.getType() == IQ.Type.RESULT) {}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
在OpenFire网页端上查看是否注册成功
3.用户登录
asmack提供了Presence的一个类
Presence介绍:
Presence是继承自XMPP的基类Packet信息包,Presence主要有两个用途:
① 告诉服务器所有客户端当前所处的状态,
② 发出添加/删除好友请求;每个Presence信息包都有一个类型属性Presence.Type 如下:
available: 表示处于在线状态
unavailable: 表示处于离线状态
subscribe: 表示发出添加好友的申请
unsubscribe: 表示发出删除好友的申请
unsubscribed: 表示拒绝添加对方为好友
error: 表示presence信息报中包含了一个错误消息。
// 连接服务器,用户登录XmppConnection.getConnection().login(userStr, passStr)// 连接服务器成功,更改在线状态Presence presence = new Presence(Presence.Type.available)XmppConnection.getConnection().sendPacket(presence)
4.好友添加与申请
分为三部分:①自己发送好友添加请求,②监听发送过来的好友请求,③处理添加好友请求(拒绝和允许)。
1) 发起用户添加申请
使用Presence类,通过Presence.Type.subscribe表示发出添加好友的申请的状态,让服务器知道发送的数据是一个好友添加申请。
String name = et_name.getText().toString()//设置添加好友请求Presence subscription = new Presence(Presence.Type.subscribe)//拼接好友全称subscription.setTo(name + "@" + XmppConnection.SERVER_NAME)//发送请求XmppConnection.getConnection().sendPacket(subscription)
2) 监听添加好友申请
需要解决的是如何知道服务器发送过来的是好友添加的一个请求?通过asmack提供的一个PacketFilter筛选器和PacketListener数据监听类,发起一个addPacketListener(PacketListener,PacketListener),开启数据监听,通过PacketFilter筛选器筛选packet,监听是否是Presence.Type.subscribe添加好友申请的状态。
/** * 添加一个监听,监听好友添加请求。 */private void addSubscriptionListener() { PacketFilter filter = new PacketFilter() { @Override public boolean accept(Packet packet) { if (packet instanceof Presence) { Presence presence = (Presence) packet; if (presence.getType().equals(Presence.Type.subscribe)) { return true; } } return false; } }; XmppConnection.getConnection().addPacketListener(subscriptionPacketListener, filter);}/** * 好友监听 */private PacketListener subscriptionPacketListener = new PacketListener() { @Override public void processPacket(final Packet packet) { if (packet.getFrom().contains(((AppGlobal) getApplication()).getName() + "@" + XmppConnection.SERVER_NAME)) return; Message msg = new Message(); msg.obj = packet; msg.what = ADD_FRIEND; handler.sendMessage(msg); }};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
3)处理好友添加申请
asmack提供一个Roster类
Roster:表示一个用户的所有好友清单以及申请加好友的用户清单的一个类
①允许添加:添加好友进入自己的好友列表
Roster roster = XmppConnection.getConnection().getRoster();try { roster.createEntry(packet.getFrom(), name, null);} catch (XMPPException e) { e.printStackTrace();}
②拒绝好友申请(注:虽然拒绝好友添加。但是对方仍可以把你添加进对方列表。asmack添加好友的方式是通过订阅的方式,只要订阅你,对方就可以添加进他的好友列表)
Presence presenceRes = new Presence(Presence.Type.unsubscribe)presenceRes.setTo(packet.getFrom())XmppConnection.getConnection().sendPacket(presenceRes)
5.删除好友
通过Rooster中的removeEntry方法将好友移除
RosterEntry类:表示Roster中的每条记录.它包含了用户的JID,用户名,或用户分配的昵称.
XMPPConnection conn = XmppConnection.getConnection()Roster roster = conn.getRoster()RosterEntry entry = roster.getEntry(friendList.get(position).get("User"))try { roster.removeEntry(entry)} catch (XMPPException e) { e.printStackTrace()}
6.获取好友列表
RosterEntry类:表示Roster中的每条记录.它包含了用户的JID,用户名,或用户分配的昵称.
Roster roster = XmppConnection.getConnection().getRoster()Collection<RosterEntry> entries = roster.getEntries()//循环拿到所有好友信息for (RosterEntry entry : entries) {}
7.进行好友聊天
针对消息发送与接受的数据,asmack单独提供了一个ChatManager的聊天室工具类和Chat消息对象
1)获取好友消息
通过获取到ChatManager类,然后开启聊天室监听,通过获取到的chat对象,开启消息监听 MessageListener
ChatManager chatMan = XmppConnection.getConnection().getChatManager();chatMan.addChatListener(new ChatManagerListener() { @Override public void chatCreated(Chat chat, boolean able) { chat.addMessageListener(new MessageListener() { @Override public void processMessage(Chat chat, Message message) { if (message.getFrom().contains(toUserID)) { } } }); } });
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
2)发送消息
ChatManager chatMan = XmppConnection.getConnection().getChatManager();Chat newchat = chatMan.createChat(toUserID, null);try { Message msg = new Message(); String content = "消息内容" msg.setBody(content); newchat.sendMessage(msg);} catch (XMPPException e) { e.printStackTrace();}
8.收发图片
针对发送图片,asmack提供了FileTransferManager文件传输的工具类,文件接收类IncomingFileTransfe
文件发送类OutgoingFileTransfer。我们可以通过把图片转换成数据的格式进行传输。
1)接收图片
文件消息监听,注册一个文件消息监听类FileTransferListener,接收到文件后先保存到本地,然后通过Handler传递保存路径的字符串,然后去刷新adapter,在adapter中再去加载下载到的本地图片。(只贴重要代码)
FileTransferManager manager = XmppConnection.getFileTransferManager(); manager.addFileTransferListener(new FileTransferListener() { @Override public void fileTransferRequest(final FileTransferRequest request) { new Thread() { @Override public void run() { IncomingFileTransfer transfer = request.accept(); String fileName = transfer.getFileName(); File sdCardDir = new File(ALBUM_PATH); if (!sdCardDir.exists()) { sdCardDir.mkdir(); } String save_path = ALBUM_PATH + fileName; File file = new File(save_path); try { transfer.recieveFile(file); while (!transfer.isDone()) { if (transfer.getStatus().equals(FileTransfer.Status.error)) { System.out.println("ERROR!!! " + transfer.getError()); } else { System.out.println(transfer.getStatus()); System.out.println(transfer.getProgress()); } try { Thread.sleep(1000L); } catch (Exception e) { } } if (transfer.isDone()) { String[] args = new String[]{toUserName, fileName}; android.os.Message msg = handler.obtainMessage(); msg.what = 2; msg.obj = args; msg.sendToTarget(); } } catch (XMPPException e) { e.printStackTrace(); } } ; }.start(); } }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
2)发送图片
①添加一个点击方法,打开本地图库
Intent intent = new Intent()intent.setType("image/*")intent.setAction(Intent.ACTION_GET_CONTENT)startActivityForResult(intent, TUPIAN_RESULT)
②选择完图片在onActivityResult拿到图片路径
protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); String picPath; if (data != null) { Uri uri = data.getData(); if (!TextUtils.isEmpty(uri.getAuthority())) { Cursor cursor = getContentResolver().query(uri, new String[]{MediaStore.Images.Media.DATA}, null, null, null); if (null == cursor) { Toast.makeText(this, "图片没找到", Toast.LENGTH_SHORT).show(); return; } cursor.moveToFirst(); picPath = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA)); cursor.close(); } else { picPath = uri.getPath(); } } else { Toast.makeText(this, "图片没找到", Toast.LENGTH_SHORT).show(); return; } new SendFileTask().execute(picPath, toUserID);}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
③通过异步的方式AsyncTask去发送图片
class SendFileTask extends AsyncTask<String, Integer, Integer> { protected Integer doInBackground(String... params) { if (params.length < 2) { return Integer.valueOf(-1); } String img_path = params[0]; String toId = params[1] + "/Smack"; FileTransferManager fileTransferManager = XmppConnection.getFileTransferManager(); File filetosend = new File(img_path); if (filetosend.exists() == false) { return -1; } OutgoingFileTransfer transfer = fileTransferManager .createOutgoingFileTransfer(toId); try { transfer.sendFile(filetosend, "recv img"); while (!transfer.isDone()) { if (transfer.getStatus().equals(FileTransfer.Status.error)) { System.out.println("ERROR!!! " + transfer.getError()); } else { System.out.println(transfer.getStatus()); System.out.println(transfer.getProgress()); } try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } if (transfer.isDone()) { String[] args = new String[]{mAppGlobal.getName(), img_path}; android.os.Message msg = handler.obtainMessage(); msg.what = 3; msg.obj = args; msg.sendToTarget(); } } catch (XMPPException e1) { e1.printStackTrace(); } return 0; }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
10.获取群列表
针对群聊功能,asmack提供了创建聊天室以及获取聊天群信息的一个类——MultiUserChat
MultiUserChat.getHostedRooms方法遍历获取到整个服务器上的所有群聊列表。
try { //遍历每个人所创建的群 for (HostedRoom host :MultiUserChat.getHostedRooms(XmppConnection.getConnection(), XmppConnection.getConnection().getServiceName())) { //遍历某个人所创建的群 for (HostedRoom singleHost : MultiUserChat.getHostedRooms(XmppConnection.getConnection(), host.getJid())) { if (singleHost.getJid().indexOf("@") > 0) { Map<String, String> map = new HashMap<>() map.put("Gname", singleHost.getName()) groupList.add(map) } } } mHandler.sendEmptyMessage(0)} catch (XMPPException e) { e.printStackTrace()}
9.创建群组
public static boolean createRoom(String user, String roomName, String password) { if (getConnection() == null) return false; MultiUserChat muc = null; try { muc = new MultiUserChat(getConnection(), roomName + "@conference." + getConnection().getServiceName()); muc.create(roomName); Form form = muc.getConfigurationForm(); Form submitForm = form.createAnswerForm(); for (Iterator<FormField> 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<String> owners = new ArrayList<>(); owners.add(getConnection().getUser()); submitForm.setAnswer("muc#roomconfig_roomowners", owners); submitForm.setAnswer("muc#roomconfig_persistentroom", true); submitForm.setAnswer("muc#roomconfig_membersonly", false); submitForm.setAnswer("muc#roomconfig_allowinvites", true); if (!password.equals("")) { submitForm.setAnswer("muc#roomconfig_passwordprotectedroom", true); submitForm.setAnswer("muc#roomconfig_roomsecret", password); } 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(); return false; } return true;}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
11.进入聊天室
加入聊天室,得知道这个房间的名称,通过群列表那里可以获取到,然后创建MultiUserChat对象,调用join方法
public static void joinMultiUserChat(String user, String password, String roomsName) { try { MultiUserChat muc = new MultiUserChat(connection, roomsName + "@conference." + connection.getServiceName()); DiscussionHistory history = new DiscussionHistory(); history.setMaxStanzas(0); muc.join(user, password, history, SmackConfiguration.getPacketReplyTimeout()); multiUserChat = muc; System.out.println("会议室加入成功........"); } catch (XMPPException e) { e.printStackTrace(); System.out.println("会议室加入失败........"); muc = null; }}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
12.进行群聊天
1)消息监听
MultiUserChat muc = XmppConnection.getMultiUserChat()muc.addMessageListener(new PacketListener() { @Override public void processPacket(Packet packet) { Message message = (Message) packet // 接收来自聊天室的聊天信息 String groupName = message.getFrom() String[] nameOrGroup = groupName.split("/") //判断是否是本人发出的消息 不是则显示 if (!nameOrGroup[1].equals(getUserName())) { String[] args = new String[]{nameOrGroup[1], message.getBody()} // 在handler里取出来显示消息 android.os.Message msg = handler.obtainMessage() msg.what = 1 msg.obj = args msg.sendToTarget() } }})
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
2)发送群消息
try { muc.sendMessage(content);} catch (XMPPException e) { e.printStackTrace();}
Demo地址