基于XMPP协议的aSmack源码分析【2】PacketReader

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()方法终于告一个段落了。

 

你可能感兴趣的:(reader)