图文无关一起娱乐:
这一篇我们开始写Android端的Smack版主类,后面Android的IM功能都是通过这个帮助类实现的
引用类库:
因为我用的是IDE是Android Studio,所以我通过gradle进行jar包管理了,非常方便,jar包如下:
compile ‘org.igniterealtime.smack:smack-core:4.1.4‘ compile ‘org.igniterealtime.smack:smack-tcp:4.1.4‘ compile ‘org.igniterealtime.smack:smack-extensions:4.1.4‘ compile ‘org.igniterealtime.smack:smack-android:4.1.4‘ compile ‘org.igniterealtime.smack:smack-android-extensions:4.1.4‘ compile ‘org.igniterealtime.smack:smack-experimental:4.1.4‘ compile ‘org.igniterealtime.smack:smack-bosh:4.1.4‘ compile ‘org.igniterealtime.smack:smack-resolver-dnsjava:4.1.4‘ compile ‘org.igniterealtime.smack:smack-legacy:4.1.4‘
加上这些jar包后我们就可以使用Smack库了,大家很容易看到,我这个是4.1.4的包。它的最新包是4.2.0不过是alpha版本,所以我就用这个4.1.4最新正式包了。它和它的一些老版本差别还是比较大的。所以如果你是第一次使用还是和我一样,防止出现一些问题。更新gradle后就可以使用了。
写Smack帮助类:
我这里先定义一个ISmack接口,这种命名方式是我从C#那边带过来的,别纠结。本人很久之前是.NET平台的,.NET平台还是有很多东西是非常不错的。这个接口就是一些约束协议。然后再写了一个Smack 类,它继承并且实现了ISmack接口。
ISmack接口代码如下:
/** * * @备注:samck操作接口协议 * @作者:高露 * @时间:2015-10-24 * @QQ:408365330 * */ public interface ISmack { /** * 登录 * @param name 账号 * @param pwd 密码 * @return */ public boolean login(String name,String pwd); /** * 消息发送 * @param from 谁发送 * @param to 发送给谁 * @param content 发送内容 */ public void sendMessage(String from,String to,String content) throws SmackException.NotConnectedException; /** *添加好友 * @param user 用户jid * @param name 添加好友备注名称 * @param groups 好友添加到的分组,可以过个组 */ public void addRosterItem(String user,String name,String[] groups) throws Exception; /** * 删除好友 * @param user 好友jid */ public void removeRoster(String user); }
可以通过这份协议知道我这里定义了 登录,消息发送,添加好友,删除好友 这些方法,后面还是会添加的,每个方法备注还是比较详细的,看看就知道是什么意思了。
然后我们再看看和分析Smack代码
Smack代码分析:
静态构造函数:
首先定义了一个java静态构造函数
static { if (connection == null) { XMPPTCPConnectionConfiguration configuration = XMPPTCPConnectionConfiguration.builder() .setConnectTimeout(Constant.IM_TIMEOUT) .setSecurityMode(ConnectionConfiguration.SecurityMode.disabled) .setHost(Constant.IM_SERVER)//ip .setPort(Constant.IM_SERVER_PORT)//端口号设置一般式5222 .setServiceName(Constant.IM_SERVER_DOMAIN)//服务器名称 .setDebuggerEnabled(true)//设置开启调试 .setSendPresence(true)//设置是否发送Presece信息 .build(); connection = new XMPPTCPConnection(configuration); connection.addConnectionListener(new ConnectionListener() { @Override public void connected(XMPPConnection connection) { LogHelper.i(TAG, "connected"); } @Override public void authenticated(XMPPConnection connection, boolean resumed) { LogHelper.i(TAG, "authenticated"); } @Override public void connectionClosed() { LogHelper.i(TAG, "connectionClosed"); } @Override public void connectionClosedOnError(Exception e) { LogHelper.i(TAG, "connectionClosedOnError"); } @Override public void reconnectionSuccessful() { LogHelper.i(TAG, "reconnectionSuccessful"); } @Override public void reconnectingIn(int seconds) { LogHelper.i(TAG, "reconnectingIn"); } @Override public void reconnectionFailed(Exception e) { LogHelper.i(TAG, "reconnectionFailed"); } }); ReconnectionManager reconnectionManager = ReconnectionManager.getInstanceFor(connection); reconnectionManager.setFixedDelay(Constant.IM_RE_CONNET_TIME);//重联间隔 reconnectionManager.enableAutomaticReconnection();//开启重联机制 try { connection.connect(); } catch (SmackException | IOException | XMPPException ex) { ex.printStackTrace(); } } }
XMPPTCPConnectionConfiguration类是连接的配置,连接相关配置都是通过这个类传递给XMPPTCPConnection对象的。配置信息的设置请看上面的代码注释,connection.addConnectionListener是给连接添加监听,这里面我们可以监听连接的各种状态。然后做相应的处理。ReconnectionManager这个类是重联管理设置连接断开后是否允许重新连接。这样实现自动重联最后通过connection.connect()连接XMPP服务器登录:
/** * 登录 * @param name 账号 * @param pwd 密码 * @return */ @Override public boolean login(String name, String pwd) { try { if (connection.isConnected()) { connection.login(name, pwd); } else { connection.connect(); connection.login(name, pwd); } } catch (XMPPException | SmackException | IOException ex) { ex.printStackTrace(); } /** * 消息监听 */ registerMessageListener(); /** * 通讯录监听 */ registerRosterListener(); return false; }
通过用户名和密码进行登录。登录之前判断是否连接了XMPP服务器。登录后添加消息监听处理消息,通讯录监听处理通讯录。下面再看看这两个监听方法
通讯录监听:
/** * 通讯录监听 */ private void registerRosterListener() { Roster roster = Roster.getInstanceFor(connection); roster.setSubscriptionMode(Roster.SubscriptionMode.manual);//设置添加好友,需要对方确认 roster.addRosterListener(new RosterListener() { @Override public void entriesAdded(Collection<String> collection) { LogHelper.i(TAG, "通讯录用户添加"); } @Override public void entriesUpdated(Collection<String> collection) { LogHelper.i(TAG, "通讯录用户变更"); } @Override public void entriesDeleted(Collection<String> collection) { LogHelper.i(TAG, "通讯录用户删除"); } @Override public void presenceChanged(Presence presence) { LogHelper.i(TAG, "通讯录用户presence变化"); } }); }
Roster类管理这通讯录。给通讯录添加了监听这样可以处理通讯里,请看上面的代码。这里重点讲解roster.setSubscriptionMode(Roster.SubscriptionMode.manual);这个设置通讯里添加好友的模式。这里设置了添加好友需要确认,而不是直接成为好友,一般都是这样,但是这里也支持直接成为好友。我们通过源码来看看SubscriptionMode这个枚举。
/** * 好友请求订阅的模式枚举. */ public enum SubscriptionMode { /** * 自动接收所有好友请求. This is * 这是默认的模式,适合简单的客户端. 更复杂的客户端希望手动处理好友添加请求. */ accept_all, /** * 自动拒绝所有请求 */ reject_all, /** * 好友请求被忽略,意味着必须手动注册和处理presence监听,然后处理类型是 * Presence.Type.SUBSCRIBE类型或者是Presence.Type.UNSUBSCRIBE类型的 presence包 */ manual }
以上是我通过源码中的英文转译的,鄙人英文就这水平见谅,见谅。这里说的presence包不懂的话看看第一篇XMPP协议简析,这样就懂了。我的原则是任何东西先懂原理(先修炼好《易筋经》然后再学招式,否则累也记不住,不懂硬记很累,有些学编程,语言没学好然后直接上平台性的东西,发现容易出问题,同样一个道理)。
消息监听:
/** * 各种消息包监听 */ private void registerMessageListener() { connection.addSyncStanzaListener(new StanzaListener() { @Override public void processPacket(Stanza stanza) throws SmackException.NotConnectedException { if (stanza instanceof Message) {//表示接收到是消息包 Message message = (Message) stanza; if (message.getType() == Message.Type.chat) {//表示单聊 } if (message.getType() == Message.Type.groupchat) {//表示群聊 } if (message.getType() == Message.Type.error) {//表示错误信息 } } if (stanza instanceof Presence) {//表示接收到的是Presence包 } if (stanza instanceof IQ) {//表示接收到的是IQ包 } } }, new StanzaFilter() { @Override public boolean accept(Stanza stanza) { return true; } });
虽然还有其它监听消息的方法,但是我选择这种了,因为这种可以监听所有各种消息包,看上面注释能理解。如果不懂这些包神马意思还是推荐你看前面第一篇XMPP协议简析。那么久知道神马是IQ,神马是Message,什么是Presence包了
new StanzaFilter() { @Override public boolean accept(Stanza stanza) { return true; } }
这个是过滤包,我这里返回ture就是不过滤直接接受所有XMPP包。你还可以过滤特定的包比如IQ包 StanzaFilter filter=new StanzaTypeFilter(IQ.class) ,然后把这个作为 addSyncStanzaListener方法的第二个参数这样就只能接受IQ消息了。
消息发送:
/** * 消息发送 * * @param from 谁发送 * @param to 发送给谁 * @param content 发送内容 */ @Override public void sendMessage(String from, String to, String content) throws SmackException.NotConnectedException { Message msg = new Message(to, content); msg.setFrom(from); connection.sendStanza(msg); // ChatManager chatmanager = ChatManager.getInstanceFor(connection); // Chat newChat = chatmanager.createChat(to, new ChatMessageListener() { // @Override // public void processMessage(Chat chat, Message message) { // LogHelper.i(TAG, "接收到消息:" + message); // // } // }); // newChat.sendMessage(content); }
我这里可以看到有两种方式,一种直接原始的发送Message包,另一种就是被我注释的代码段,通过ChatManager来发送消息
添加好友:
/** * 添加好友 * @param user 用户jid * @param name 添加好友备注名称 * @param groups 好友添加到的分组,可以过个组 * @throws Exception */ @Override public void addRosterItem(String user, String name, String[] groups) throws Exception{ Roster roster=Roster.getInstanceFor(connection); roster.createEntry(user,name,null); }
这里没神马好讲的,因为上面设置了 roster.setSubscriptionMode(Roster.SubscriptionMode.manual);//设置添加好友,需要对方确认 这种模式,添加好友需要对方确认
最后:
今天就此结束,虽然只有这些方法,如果你结合前面第一篇XMPP协议简介弄懂原理,我们就共同进步了:)。希望我们一起每天进步一点。
合肥程序员群:49313181。 合肥实名程序员群:128131462 (不愿透露姓名和信息者勿加入)
Q Q:408365330 E-Mail:[email protected]