zoukankan      html  css  js  c++  java
  • 基于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()方法终于告一个段落了。

  • 相关阅读:
    用VC编译lua源码,生成lua语言的解释器和编译器
    vs如何在C++中调用Lua
    打印页面 订单号生成 条形码
    关于 打印页面 图片被截断
    easyui numberbox输入框 编辑不可编辑的切换
    点击空白处--某个div 消失
    easyui扩展行默认展开 以及 去除滚动条
    eayui grid 每一页的行号都是从1开始
    js中的this--执行上下文
    easyui grid 本地做分页
  • 原文地址:https://www.cnblogs.com/rioder/p/2873549.html
Copyright © 2011-2022 走看看