基於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()方法終於告一個段落了。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM