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

原文地址:https://www.cnblogs.com/rioder/p/2873549.html