鉴于xmpp协议都是以xml格式来传输,因此源码中解析协议都是用到XmpPullParser来解析xml
XmpPullParser很简单,先简单介绍几个比较常用的方法
//定义一个事件采用回调方式,直到xml完毕 public int getEventType() throws XmlPullParserException ; //遍历下一个事件,返回一个事件类型 public int next() throws XmlPullParserException, IOException //得到当前tag的名字 public String getName(); //获得当前文本 public String getText(); //当前tag下的属性数量 public int getAttributeCount() ; //获得当前指定属性位置的名称 public String getAttributeName(int index); //获得当前指定属性位置的值 public String getAttributeValue(int index);
为了更好的理解后面的源码,加一段代码来分析:
public PacketExtension parseExtension(XmlPullParser parser) throws Exception { MUCUser mucUser = new MUCUser(); boolean done = false; while (!done) { int eventType = parser.next(); if (eventType == XmlPullParser.START_TAG) { if (parser.getName().equals("invite")) { mucUser.setInvite(parseInvite(parser)); } if (parser.getName().equals("item")) { mucUser.setItem(parseItem(parser)); } if (parser.getName().equals("password")) { mucUser.setPassword(parser.nextText()); } if (parser.getName().equals("status")) { mucUser.setStatus(new MUCUser.Status(parser.getAttributeValue("", "code"))); } if (parser.getName().equals("decline")) { mucUser.setDecline(parseDecline(parser)); } if (parser.getName().equals("destroy")) { mucUser.setDestroy(parseDestroy(parser)); } } else if (eventType == XmlPullParser.END_TAG) { if (parser.getName().equals("x")) { done = true; } } } return mucUser; }
里面的对象先不用理它,只需看他是如何分析这段xml的:
//协议解释,从123456789发送一段协议给12345678这个用户,邀请用户123456789进入房间,理由hi join us。
<message id="WEzG6-11" to="123456789@xxx-pc/Smack" from="12345678@xxx-pc/Smack" type="get"> <x xmlns="http://jabber.org/protocol/muc#user"> <invite to="123456789@xxx-pc"> <reason>hi join us</reason> </invite> </x> </message>
parser.next();
//取xmlpullparse对象
//设置关联数据源
parser.setInput(reader);
先理解一下smack的使用,看下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.在这构造函数里面主要配置ip地址和端口
super(new ConnectionConfiguration("169.254.141.109", 9991));
3.注册监听,开始初始化连接。
connection.addPacketListener(packetListener, packetFilter);
connection.connect();
4.通过之前设置的ip和端口,建立socket对象
public void connect() { // Stablishes the connection, readers and writers connectUsingConfiguration(config); } 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(); }
5.建立reader和writer的对象关联到socket的InputStream
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(); }
6.实例化ConsoleDebugger,该类主要是打印出接收到的消息,给reader设置了一个消息的监听
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); } }
7.接着建立PacketReader对象,并启动。PacketReader主要负责消息的处理和通知
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(); }
看看PacketReader类
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); } } } }
比如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(); } } }
该方法就是将获取到的包,先过滤然后放到队列里,最后通过nextResult来获取包,这样就完成一个请求收一个答复。
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; }
这样整个流程就完成了,最后总结一下,如图
http://www.cnblogs.com/not-code/archive/2011/08/01/2124340.html