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); } }
其實,註冊的過程就是在註冊監聽,這樣在有消息發出時,纔可以根據業務需求對消息進行接收和處理。