天天看点

java 解析xmpp消息_【XMPP】Smack源码之消息接收与解析

XmpPullParser

鉴于xmpp协议都是以xml格式来传输,因此源码中解析协议都是用到XmpPullParser来解析xml

XmpPullParser很简单,先简单介绍几个比较常用的方法

java 解析xmpp消息_【XMPP】Smack源码之消息接收与解析
java 解析xmpp消息_【XMPP】Smack源码之消息接收与解析

//定义一个事件采用回调方式,直到xml完毕

public int getEventType() throwsXmlPullParserException ;//遍历下一个事件,返回一个事件类型

public int next() throwsXmlPullParserException, IOException//得到当前tag的名字

publicString getName();//获得当前文本

publicString getText();//当前tag下的属性数量

public intgetAttributeCount() ;//获得当前指定属性位置的名称

public String getAttributeName(intindex);//获得当前指定属性位置的值

public String getAttributeValue(int index);

View Code

为了更好的理解后面的源码,加一段代码来分析:

java 解析xmpp消息_【XMPP】Smack源码之消息接收与解析
java 解析xmpp消息_【XMPP】Smack源码之消息接收与解析

public PacketExtension parseExtension(XmlPullParser parser) throwsException {

MUCUser mucUser= newMUCUser();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;

}

}

}returnmucUser;

}

View Code

里面的对象先不用理它,只需看他是如何分析这段xml的:

//协议解释,从123456789发送一段协议给12345678这个用户,邀请用户123456789进入房间,理由hi join us。

java 解析xmpp消息_【XMPP】Smack源码之消息接收与解析
java 解析xmpp消息_【XMPP】Smack源码之消息接收与解析

hi join us

View Code

parser.next();

获得第一个事件,判断是否开始标签(XmlPullParser.START_TAG)

然后再里面判断每个标签的名字

处理完后判断结尾标签(XmlPullParser.END_TAG)是否需要结束本次循环。

//取xmlpullparse对象

XmlPullParserFactory factory = XmlPullParserFactory.newInstance();

XmlPullParserparser = factory.newPullParser();

//设置关联数据源

parser.setInput(reader);

接收消息以及如何解析消息

在android里面用的smack包其实叫做asmack,该包提供了两种不同的连接方式:socket和httpclient。

该包并且提供了很多操作xmpp协议的API,也方便各种不同自定义协议的扩展。

我们不需要自己重新去定义一套接收机制来扩展新的协议,只需继承然后在类里处理自己的协议就可以了。

总的思路

使用socket连接服务器

将XmlPullParser的数据源关联到socket的InputStream

启动线程不断循环处理消息

将接收到的消息解析xml处理封装好成一个Packet包

将包广播给所有注册事件监听的类

逐步击破

先理解一下smack的使用,看下smack类图

下图只显示解释所要用到的类和方法,减缩了一些跟本文主题无关的代码,只留一条贯穿着从建立连接到接收消息的线。

java 解析xmpp消息_【XMPP】Smack源码之消息接收与解析

1.解析这块东西打算从最初的调用开始作为入口,抽丝剥茧,逐步揭开。

java 解析xmpp消息_【XMPP】Smack源码之消息接收与解析
java 解析xmpp消息_【XMPP】Smack源码之消息接收与解析

PacketListener packetListener = newPacketListener() {

@Overridepublic voidprocessPacket(Packet packet) {

System.out.println("Activity----processPacket" +packet.toXML());

}

};

PacketFilter packetFilter= newPacketFilter() {

@Overridepublic booleanaccept(Packet packet) {

System.out.println("Activity----accept" +packet.toXML());return true;

}

};

View Code

解释:创建包的监听以及包的过滤,当有消息到时就会广播到所有注册的监听,当然前提是要通过packetFilter的过滤。

2.在这构造函数里面主要配置ip地址和端口

java 解析xmpp消息_【XMPP】Smack源码之消息接收与解析
java 解析xmpp消息_【XMPP】Smack源码之消息接收与解析

super(new ConnectionConfiguration("169.254.141.109", 9991));

View Code

3.注册监听,开始初始化连接。

java 解析xmpp消息_【XMPP】Smack源码之消息接收与解析
java 解析xmpp消息_【XMPP】Smack源码之消息接收与解析

connection.addPacketListener(packetListener, packetFilter);

connection.connect();

View Code

4.通过之前设置的ip和端口,建立socket对象

java 解析xmpp消息_【XMPP】Smack源码之消息接收与解析
java 解析xmpp消息_【XMPP】Smack源码之消息接收与解析

public voidconnect() {//Stablishes the connection, readers and writers

connectUsingConfiguration(config);

}private voidconnectUsingConfiguration(ConnectionConfiguration config) {

String host=config.getHost();int port =config.getPort();try{this.socket = newSocket(host, port);

}catch(UnknownHostException e) {

e.printStackTrace();

}catch(IOException e) {

e.printStackTrace();

}

initConnection();

}

View Code

5.建立reader和writer的对象关联到socket的InputStream

java 解析xmpp消息_【XMPP】Smack源码之消息接收与解析
java 解析xmpp消息_【XMPP】Smack源码之消息接收与解析

private voidinitReaderAndWriter() {try{

reader= new BufferedReader(newInputStreamReader(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();

}

View Code

6.实例化ConsoleDebugger,该类主要是打印出接收到的消息,给reader设置了一个消息的监听

java 解析xmpp消息_【XMPP】Smack源码之消息接收与解析
java 解析xmpp消息_【XMPP】Smack源码之消息接收与解析

protected voidinitDebugger() {

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 newIllegalArgumentException("Can't initialize the configured debugger!", e);

}

}

View Code

7.接着建立PacketReader对象,并启动。PacketReader主要负责消息的处理和通知

java 解析xmpp消息_【XMPP】Smack源码之消息接收与解析
java 解析xmpp消息_【XMPP】Smack源码之消息接收与解析

private voidinitConnection() {//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();

}

View Code

看看PacketReader类

java 解析xmpp消息_【XMPP】Smack源码之消息接收与解析
java 解析xmpp消息_【XMPP】Smack源码之消息接收与解析

public classPacketReader {privateExecutorService listenerExecutor;private booleandone;privateXMPPConnection connection;privateXmlPullParser parser;privateThread readerThread;protected PacketReader(finalXMPPConnection connection) {this.connection =connection;this.init();

}

protected voidinit() {

done= false;

readerThread= newThread() {public voidrun() {

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(newThreadFactory() {

@OverridepublicThread newThread(Runnable r) {

Thread thread= newThread(r,"smack listener processor");

thread.setDaemon(true);returnthread;

}

});

resetParser();

}

public voidstartup() {

readerThread.start();

}

public voidshutdown() {

done= true;//Shut down the listener executor.

listenerExecutor.shutdown();

}private voidresetParser() {try{

parser=XmlPullParserFactory.newInstance().newPullParser();

parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES,true);

parser.setInput(connection.reader);

}catch(XmlPullParserException xppe) {

xppe.printStackTrace();

}

}

private voidparsePackets(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 voidprocessPacket(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(newListenerNotification(packet));

}

private class ListenerNotification implementsRunnable {privatePacket packet;publicListenerNotification(Packet packet) {this.packet =packet;

}public voidrun() {for(ListenerWrapper listenerWrapper : connection.recvListeners

.values()) {

listenerWrapper.notifyListener(packet);

}

}

}

}

View Code

创建该类时就初始化线程和ExecutorService

接着调用resetParser()方法为parser设置输入源(这里是重点,parser的数据都是通过这里获取)

调用startup启动线程,循环监听parser

如果接收到消息根据消息协议的不同将调用PacketParserUtils类里的不同方法,这里调用parseMessage()该方法主要处理message的消息,在该方法里分析message消息并返回packet包。

返回的包将调用processPacket方法,先通知所有注册了PacketCollector的监听,接着消息(listenerExecutor.submit(newListenerNotification(packet)); )传递给所有注册了PacketListener的监听。

这样在activity开始之前注册的那个监听事件就会触发,从而完成了整个流程。

辅助包

比如PacketCollector这个类,它的用处主要用来处理一些需要在发送后需要等待一个答复这样的请求。

java 解析xmpp消息_【XMPP】Smack源码之消息接收与解析
java 解析xmpp消息_【XMPP】Smack源码之消息接收与解析

protected synchronized voidprocessPacket(Packet packet) {

System.out.println("PacketCollector---processPacket");if (packet == null) {return;

}if (packetFilter == null ||packetFilter.accept(packet)) {while (!resultQueue.offer(packet)) {

resultQueue.poll();

}

}

}

View Code

该方法就是将获取到的包,先过滤然后放到队列里,最后通过nextResult来获取包,这样就完成一个请求收一个答复。

java 解析xmpp消息_【XMPP】Smack源码之消息接收与解析
java 解析xmpp消息_【XMPP】Smack源码之消息接收与解析

public Packet nextResult(longtimeout) {long endTime = System.currentTimeMillis() +timeout;

System.out.println("nextResult");do{try{returnresultQueue.poll(timeout, TimeUnit.MILLISECONDS);

}catch (InterruptedException e) { }

}while (System.currentTimeMillis()

}

View Code

这样整个流程就完成了,最后总结一下,如图

java 解析xmpp消息_【XMPP】Smack源码之消息接收与解析

参考文章

http://www.cnblogs.com/not-code/archive/2011/08/01/2124340.html