aSmack源碼分析register過程分析


register過程分析
RegisterTask這個task在運行中,添加了一個監聽,上面說道的PacketReader中有一個消息機制,在不停的解析服務器返回的結果,然後將解析過後的包分發給各個監聽器(觀察者),而register中就註冊了一個監聽器,比較有意思的是,監聽器被註冊時還加了一個過濾器,這個過濾器的目的是監聽器只接收自己感興趣的內容,這個設計真的很贊。這樣就不必在數據源頭PacketReader中對數據進行過濾了,只要後期擴展自己Packet和自己的過濾器,就能達到排除自己不關心的信息的功能。
1 Registration registration = new Registration();
2 
3                 PacketFilter packetFilter = new AndFilter(new PacketIDFilter(
4                         registration.getPacketID()), new PacketTypeFilter(
5                         IQ.class));

其中Registration的類型其實一個IQ的子類,IQ是Packet的子類。
AndFilter是PacketFilter的子類,PacketFilter的種類型有很多,也可以自己擴展,AndFilter就是其中一個、PacketTypeFilter也是、PacketIDFilter也是,
其中PacketTypeFilter的構造方法傳入一個IQ.class,其實就是通過這個類文件來過濾packet,這個PacketTypeFilter就是要設置關心的Packet,這裏面它告訴監聽器,只接收類型爲IQ的Packet,這些Filter中都有一個關鍵方法,accept(Packet packet).這個accept方法每個Filter的實現方式都不一樣,我們可可以擴展自己的Filter並且重寫這個方法,最有意思的是AndFilter這個類,他的構造方法傳入的是一個動態數組,類型爲PacketFilter,你可以傳入你需要的過濾器,將他們當成組合條件使用來過濾Packet,這個就是典型的裝飾設計模式和職責鏈模式的組合使用。

註冊監聽器

複製代碼
PacketListener packetListener = new PacketListener() {
                    //這一部分就是監聽器接收到Packet後執行的後續操作
                    public void processPacket(Packet packet) {
                        Log.d("RegisterTask.PacketListener",
                                "processPacket().....");
                        Log.d("RegisterTask.PacketListener", "packet="
                                + packet.toXML());

                        if (packet instanceof IQ) {
                            IQ response = (IQ) packet;
                            if (response.getType() == IQ.Type.ERROR) {
                                if (!response.getError().toString().contains(
                                        "409")) {
                                    Log.e(LOGTAG,
                                            "Unknown error while registering XMPP account! "
                                                    + response.getError()
                                                            .getCondition());
                                }
                            } else if (response.getType() == IQ.Type.RESULT) {
                                xmppManager.setUsername(newUsername);
                                xmppManager.setPassword(newPassword);
                                Log.d(LOGTAG, "username=" + newUsername);
                                Log.d(LOGTAG, "password=" + newPassword);

                                Editor editor = sharedPrefs.edit();
                                editor.putString(Constants.XMPP_USERNAME,
                                        newUsername);
                                editor.putString(Constants.XMPP_PASSWORD,
                                        newPassword);
                                editor.commit();
                                Log
                                        .i(LOGTAG,
                                                "Account registered successfully");
                                //執行task
                                xmppManager.runTask();
                            }
                        }
                    }
                };

                connection.addPacketListener(packetListener, packetFilter);
複製代碼

addPacketListener方法傳入一個監聽器和過濾器,看一下內部

複製代碼
/**
     * Registers a packet listener with this connection. A packet filter determines
     * which packets will be delivered to the listener. If the same packet listener
     * is added again with a different filter, only the new filter will be used.
     * 
     * @param packetListener the packet listener to notify of new received packets.
     * @param packetFilter   the packet filter to use.
     */
    public void addPacketListener(PacketListener packetListener, PacketFilter packetFilter) {
        if (packetListener == null) {
            throw new NullPointerException("Packet listener is null.");
        }
        ListenerWrapper wrapper = new ListenerWrapper(packetListener, packetFilter);
        recvListeners.put(packetListener, wrapper);
    }
複製代碼

可以看到,監聽器和過濾器被 ListenerWrapper 再次封裝,後續的recvListeners這個集合將ListenerWrapper收入囊中,好整個註冊過程完畢,就等待接收信息了,那麼發送信息的地方在什麼地方呢?分析connect過程時,上面的PacketReader中已經開始循環發送了,代碼如下
listenerExecutor.submit(new ListenerNotification(packet));其中ListenerNotification是個Runnable

複製代碼
/**
     * A runnable to notify all listeners of a packet.
     */
    private class ListenerNotification implements Runnable {

        private Packet packet;

        public ListenerNotification(Packet packet) {
            this.packet = packet;
        }

        public void run() {
            for (ListenerWrapper listenerWrapper : connection.recvListeners.values()) {
                listenerWrapper.notifyListener(packet);
            }
        }
    }
複製代碼

而listenerWrapper的notifyListener(packet)內部,使用了傳入的過濾器對Packet進行了過濾

複製代碼
/**
         * Notify and process the packet listener if the filter matches the packet.
         * 
         * @param packet the packet which was sent or received.
         */
        public void notifyListener(Packet packet) {
            if (packetFilter == null || packetFilter.accept(packet)) {
                packetListener.processPacket(packet);
            }
複製代碼

而具體的過濾機制還是轉調了傳入的過濾器本身的過濾方式accept,非常的靈活。過濾完的Packet將被髮送出去

這個方法connection.sendPacket(registration);將一個Registration對象發了出去,

複製代碼
public void sendPacket(Packet packet) {
        if (!isConnected()) {
            throw new IllegalStateException("Not connected to server.");
        }
        if (packet == null) {
            throw new NullPointerException("Packet is null.");
        }
        packetWriter.sendPacket(packet);
    }
複製代碼

內部轉調的是 packetWriter.sendPacket(packet);以前提到過PacketWirter中有兩個循環機制,其中一個就是在不停的訪問隊列來獲取Packet,而這個sendPacket方法就是將消息寫入隊列中供消費者使用。

複製代碼
/**
     * Sends the specified packet to the server.
     *
     * @param packet the packet to send.
     */
    public void sendPacket(Packet packet) {
        if (!done) {
            // Invoke interceptors for the new packet that is about to be sent. Interceptors
            // may modify the content of the packet.
            //內部執行了一個發送數據源的動作,也是爲某些監聽器對象服務的interceptorWrapper.notifyListener(packet);
            connection.firePacketInterceptors(packet);

            try {
                //將一個Packet對象放入到阻塞隊列中,在上面的witerPacket方法中的wile循環中發送出去
                queue.put(packet);
            }
            catch (InterruptedException ie) {
                ie.printStackTrace();
                return;
            }
            synchronized (queue) {
                queue.notifyAll();
            }

            // Process packet writer listeners. Note that we're using the sending
            // thread so it's expected that listeners are fast.
            connection.firePacketSendingListeners(packet);
        }
    }    
複製代碼

其實,註冊的過程就是在註冊監聽,這樣在有消息發出時,纔可以根據業務需求對消息進行接收和處理。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章