在android里面用的smack包其实叫做asmack,该包提供了两种不同的连接方式:socket和httpclient。该并且提供了很多操作xmpp协议的API,也方便各种不同自定义协议的扩展。我们不需要自己重新去定义一套接收机制来扩展新的协议,只需继承然后在类里处理自己的协议就可以了。而本文今天主要说两点,一点就是消息是如何接收的,另一点就是消息是如何通知事件的。
总的思路
1.使用socket连接服务器
2.将XmlPullParser的数据源关联到socket的InputStream
3.启动线程不断循环处理消息
4.将接收到的消息解析xml处理封装好成一个Packet包
5.将包广播给所有注册事件监听的类
逐步击破
(声明在看下面的文章时,最好先理解一下smack的使用,这样才能达到深入的理解)
(谨记:上图只显示本文章解释所要用到的类和方法,减缩了一些跟本文主题无关的代码,只留一条贯穿着从建立连接到接收消息的线。)
解析这块东西打算从最初的调用开始作为入口,抽丝剥茧,逐步揭开。
1.
PacketListener packetListener = new PacketListener() { @Override public void processPacket(Packet packet) { System.out .println("Activity----processPacket" + packet.toXML()); } }; PacketFilter packetFilter = new PacketFilter() { @Override public boolean accept(Packet packet) { System.out.println("Activity----accept" + packet.toXML()); return true; } };
解释:创建包的监听以及包的过滤,当有消息到时就会广播到所有注册的监听,当然前提是要通过packetFilter的过滤。
2.
connection = new XMPPConnection();
XMPPConnection在这构造函数里面主要配置ip地址和端口(super(new ConnectionConfiguration("169.254.141.109", 9991));)
3.
connection.addPacketListener(packetListener, packetFilter);
connection.connect();
注册监听,开始初始化连接。
4.
public void connect() { // Stablishes the connection, readers and writers connectUsingConfiguration(config); }
5.
private void connectUsingConfiguration(ConnectionConfiguration config) { String host = config.getHost(); int port = config.getPort(); try { this.socket = new Socket(host, port); } catch (UnknownHostException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } initConnection(); }
通过之前设置的ip和端口,建立socket对象
6.
protected void initDebugger() { Class<?> debuggerClass = null; try { debuggerClass = Class.forName("com.simualteSmack.ConsoleDebugger"); Constructor<?> constructor = debuggerClass.getConstructor( Connection.class, Writer.class, Reader.class); debugger = (SmackDebugger) constructor.newInstance(this, writer, reader); reader = debugger.getReader(); } catch (ClassNotFoundException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } catch (Exception e) { throw new IllegalArgumentException( "Can't initialize the configured debugger!", e); } }
private void initReaderAndWriter() { try { reader = new BufferedReader(new InputStreamReader(socket .getInputStream(), "UTF-8")); } catch (UnsupportedEncodingException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } initDebugger(); }
private void initConnection() { // Set the reader and writer instance variables initReaderAndWriter(); packetReader = new PacketReader(this); addPacketListener(debugger.getReaderListener(), null); // Start the packet reader. The startup() method will block until we // get an opening stream packet back from server. packetReader.startup(); }
从三个方法可以看出,建立reader和writer的对象关联到socket的InputStream,实例化ConsoleDebugger,该类主要是打印出接收到的消息,给reader设置了一个消息的监听。接着建立PacketReader对象,并启动。PacketReader主要负责消息的处理和通知
7.
public class PacketReader { private ExecutorService listenerExecutor; private boolean done; private XMPPConnection connection; private XmlPullParser parser; private Thread readerThread; protected PacketReader(final XMPPConnection connection) { this.connection = connection; this.init(); } /** * Initializes the reader in order to be used. The reader is initialized * during the first connection and when reconnecting due to an abruptly * disconnection. */ protected void init() { done = false; readerThread = new Thread() { public void run() { parsePackets(this); } }; readerThread.setName("Smack Packet Reader "); readerThread.setDaemon(true); // create an executor to deliver incoming packets to listeners. // we will use a single thread with an unbounded queue. listenerExecutor = Executors .newSingleThreadExecutor(new ThreadFactory() { @Override public Thread newThread(Runnable r) { Thread thread = new Thread(r, "smack listener processor"); thread.setDaemon(true); return thread; } }); resetParser(); } /** * Starts the packet reader thread and returns once a connection to the * server has been established. A connection will be attempted for a maximum * of five seconds. An XMPPException will be thrown if the connection fails. * */ public void startup() { readerThread.start(); } /** * Shuts the packet reader down. */ public void shutdown() { done = true; // Shut down the listener executor. listenerExecutor.shutdown(); } private void resetParser() { try { parser = XmlPullParserFactory.newInstance().newPullParser(); parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); parser.setInput(connection.reader); } catch (XmlPullParserException xppe) { xppe.printStackTrace(); } } /** * Parse top-level packets in order to process them further. * * @param thread * the thread that is being used by the reader to parse incoming * packets. */ private void parsePackets(Thread thread) { try { int eventType = parser.getEventType(); do { if (eventType == XmlPullParser.START_TAG) { if (parser.getName().equals("message")) { processPacket(PacketParserUtils.parseMessage(parser)); } System.out.println("START_TAG"); } else if (eventType == XmlPullParser.END_TAG) { System.out.println("END_TAG"); } eventType = parser.next(); } while (!done && eventType != XmlPullParser.END_DOCUMENT && thread == readerThread); } catch (Exception e) { e.printStackTrace(); if (!done) { } } } private void processPacket(Packet packet) { if (packet == null) { return; } // Loop through all collectors and notify the appropriate ones. for (PacketCollector collector : connection.getPacketCollectors()) { collector.processPacket(packet); } // Deliver the incoming packet to listeners. listenerExecutor.submit(new ListenerNotification(packet)); } /** * A runnable to notify all listeners of a packet. */ private class ListenerNotification implements Runnable { private Packet packet; public ListenerNotification(Packet packet) { this.packet = packet; } public void run() { for (ListenerWrapper listenerWrapper : connection.recvListeners .values()) { listenerWrapper.notifyListener(packet); } } } }
创建该类时就初始化线程和ExecutorService ,接着调用resetParser() 方法为parser设置输入源(这里是重点,parser的数据都是通过这里获取),调用startup启动线程,循环监听parser,如果接收到消息根据消息协议的不同将调用PacketParserUtils类里的不同方法,这里调用parseMessage()该方法主要处理message的消息,在该方法里分析message消息并返回packet包。返回的包将调用processPacket方法,先通知所有注册了PacketCollector的监听,接着消息(listenerExecutor.submit(new ListenerNotification(packet)); )传递给所有注册了PacketListener的监听。这样在activity开始之前注册的那个监听事件就会触发,从而完成了整个流程。
7以上.
剩下的就是一些辅助包,很简单。比如PacketCollector 这个类,它的用处主要用来处理一些需要在发送后需要等待一个答复这样的请求。
protected synchronized void processPacket(Packet packet) { System.out.println("PacketCollector---processPacket"); if (packet == null) { return; } if (packetFilter == null || packetFilter.accept(packet)) { while (!resultQueue.offer(packet)) { resultQueue.poll(); } } }
public Packet nextResult(long timeout) { long endTime = System.currentTimeMillis() + timeout; System.out.println("nextResult"); do { try { return resultQueue.poll(timeout, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { /* ignore */ } } while (System.currentTimeMillis() < endTime); return null; }
该方法就是将获取到的包,先过滤然后放到队列里,最后通过nextResult来获取包,这样就完成一个请求收一个答复。
这样整个流程就完成了,最后总结一下,如图(就这么简单^0^):
项目下载(只有客户端的,服务端的就是一个简单的socket接受,为了锻炼一下大家的编写代码的能力,服务器那个只能自己写咯^0^,其实是懒得上传了,代码很简单的)
相关推荐
android smack源码分析——接收消息以及如何解析消息: http://www.cnblogs.com/not-code/archive/2011/08/01/2124340.html MTQQ http://www.cnblogs.com/charley_yang/archive/2011/03/27/1997938.html ...
Smack是一个用于实现和XMPP服务器即时通讯的类库,功能包括即时通信和群聊。
一个简单的demo用于登陆Openfire 发送消息,接收消息,发送表情等功能
smack源码下载,java版下载,smack源码下载
smack源码
基于xmpp smackAndroid 做的即时通讯 适用于初学者,
Android使用smack连接ejabberd服务器,完成了连接、登录、创建新用户、收发消息的功能
Smack源码,要的拿走,有需要的可以看看,多看源码帮助很大
openfire4.2.1 + smack4.2.2即时通信工具开发(android端登录、发送消息、接收消息),请参见我的博客http://blog.csdn.net/wangearn/article/details/79096347
java android openfire+smack项目源码,已测试,修改ip地址后即可直接运行。
Android smack4.1.4的示例,包括连接,登录,获取好友列表,发送消息等示例
基于smack框架。实现了android客户端的注册、登陆、好友添加、好友之间聊天。会议室聊天等功能。
这一节中我们着力介绍如何基于asmack开发一个Android的客户端,本篇的重点在实践,讲解和原理环节,大家可以参考前面我所发布的OpenFire和Smack的相关文章。今天所讲解的文章只是 :基于Smack3.0.4+ Openfire3.10.2...
基于Smack3.0.4+ Openfire3.10.2开发之Android 客户端之三(用户登入、用户注册与XMPP 服务器交互)。
smack4.1 现在支持android 再也不用asmack去修修改改的啦
smack 在4.1.0以后支持android上使用,demo里集成了smack4.1.9在android的使用
Asmack是smack的android版,是对xmpp协议进行了封装,并提供了丰富的api,简化了操作。Openfire是一个开源的IM服务器,基于Xmpp实现。换句话说使用xmpp开发即时通讯应用的话,服务器端已经开发好了,就是openfire。
Android Smack聊天室 Demo,对应博客http://blog.csdn.net/fkingu007/article/details/23281971
用源码开发的时候,需要把smack文件夹下的org文件夹和smackx文件夹下的com文件夹打成jar包导入,源码在source下
Android smack 相关jar。全,去除了无用的 和 新增官方未提供的