aSmack源码分析PacketReader

来源:互联网 发布:editplus4怎么编程 编辑:程序博客网 时间:2024/05/13 07:26

PacketReader

PacketReader所有的核心逻辑都在一个线程中完成的,PacketReader的工作很专注,同样的在一个while loop中 不停的解析、刷新reader对象、同时作为事件源发送解析过后的各种Packet,解析这里用的是Android独特的Pull解析,Pull解析的特点事件驱动,在这里被完全的利用了起来,随着不同的标签,PacketReader都会做出不同的处理,处理完这些数据用不同Pocket对象封装,最后,分发出去,由监听者做最后的业务处理。

1 readerThread = new Thread() {2             public void run() {3                 parsePackets(this);4             }5         };

由于解析过程的代码量过于多,我写到什么地方就分解什么地方,大家有时间最好自己看源码。

一、初始化/重置解析器

复制代码
 1 private void resetParser() { 2         try { 3             //用的是Pull解析 4             parser = XmlPullParserFactory.newInstance().newPullParser(); 5             parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); 6             parser.setInput(connection.reader); 7         } 8         catch (XmlPullParserException xppe) { 9             xppe.printStackTrace();10         }11     }
复制代码

上面这个resetParser方法还会在解析的过程中碰到不同的业务需求会不断的被调用,有用和业务逻辑比较紧密,没什么技术含量,关键是要看解析的方式和同时作为事件源发送解析过后的各种Packet,这两部分的设计,是非常的迷人的。

二、解析

复制代码
 1 do { 2                 if (eventType == XmlPullParser.START_TAG) { 3                     if (parser.getName().equals("message")) { 4                         processPacket(PacketParserUtils.parseMessage(parser)); 5                     } 6                     else if (parser.getName().equals("iq")) { 7                         processPacket(PacketParserUtils.parseIQ(parser, connection)); 8                     } 9                     else if (parser.getName().equals("presence")) {10                         processPacket(PacketParserUtils.parsePresence(parser));11                     }
复制代码

PacketParserUtils是一个工具类,各个静态方法传入的还是Parser对象,内部同样的使用Pull的方式进行解析,但是由于Pull是驱动解析,不会无故的浪费资源只会加载感兴趣的内容,试想一下,如果这里用Dom解析……PacketParserUtils的这些静态解析方法返回的实例对象也不一样,从方法名可以看出有IQ、message、presence等,他们的父类为Packet,这些对象又被执行processPacket方法的时候传入

复制代码
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));    }
复制代码

processPacket方法内部有一个循环来转调collector.processPacket(packet);方法,前提是connection.getPacketCollectors()内部有货,到目前位置都没有涉及到PacketCollector这个接口的内容,他的作用其实是一个观察者模式中的执行者的作用,也就是传说中的监听器,凡是注册了它的对象,都可以通过processPacket这个抽象方法,监听packet的变化。可是到现在任何对象都没有注册它,所以这个Loop还没有作用,因为目前我们还处在连接的步骤(还没绕出来)。

复制代码
 1 listenerExecutor.submit(new ListenerNotification(packet));其中ListenerNotification是个Runnable 2     /** 3      * A runnable to notify all listeners of a packet. 4      */ 5     private class ListenerNotification implements Runnable { 6  7         private Packet packet; 8  9         public ListenerNotification(Packet packet) {10             this.packet = packet;11         }12 13         public void run() {14             for (ListenerWrapper listenerWrapper : connection.recvListeners.values()) {15                 listenerWrapper.notifyListener(packet);16             }17         }18     }
复制代码

我们上面看到listenerExecutor是一个线程池,在线程池中执行了一个凡是注册了ListenerWrapper的对象,都将接收到packet,同样的,到目前为止没有对象注册,(在RegisterTask过程中ListenerWrapper被注册)

else if (eventType == XmlPullParser.END_TAG) {                    if (parser.getName().equals("stream")) {                        // Disconnect the connection                        connection.disconnect();                    }                }

当文档读取结束是将断开连接

void cleanup() {        connection.recvListeners.clear();        connection.collectors.clear();    }

看到了吗,只是将监听器接口集合清空而已,并没有断开连接,或者取消消息循环

PacketReader对象的startup方法比较复杂,大体上执行了读取流,并将解析好的Packet对象发送给观察者,由观察者继续后续操作,目前观察者还没有出现,还有就是使用了线程池和令牌来操作执行线程,而且维护了一个connectionID成员,这个成员的作用还需要再看,这就不多说了。
关于Packet对象,packet对象有很多子类,上面举例了3个,其实还有很多,都是在parser时封装的
AuthMechanism\Challenge\Failure\IQ\Message\Presence\Response\Success
还有就是Pull解析的优点体现了出来,可以一个parser对象包含了很多信息,但可能没到一个时刻我们需要的信息只是一小部分,这样用Pull解析的驱动式就大大减少了冗余的过程,PacketReader对象使用了2个监听器集合对象,PacketCollector、listenerWrapper,还是那句话,还没看到观察者,所以还不知道什么情况下需要注册这两个监听。
到目前位置packetReader.startup()方法终于告一个段落了

0 0
原创粉丝点击