在研究如何实现Pushing功能期间,收集了很多关于Pushing的资料,其中有一个androidnp开源项目用的人比较多,但是由于长时间没有什么人去维护,听说bug的几率挺多的,为了以后自己的产品稳定些,所以就打算自己研究一下asmack的源码,自己做一个插件,androidnp移动端的源码中包含了一个叫做asmack的jar。
Reader和Writer
在asmack中有两个非常重要的对象PacketReader和PacketWriter,那么从类名上看Packet + (Reader/Wirter),而TCP/IP传输的数据,叫做Packet(包),asmack使用的是XMPP协议,XMPP简单讲就是使用TCP/IP协议 + XML流协议的组合。所以这个了对象的作用从字面上看应该是,写包与读包,作用为从服务端读写数据。
PacketWriter中一定含有一个Writer对象,这个Writer是一个输出流,同样的PacketReader对象中有一个Reader,而这个Reader是一个输入流,Writer和Reader对象就是一个简单的读写器,他们是从socket对象中获取出来后,经过装饰变成现在这个样子。
reader = new BufferedReader(new InputStreamReader(socket.getInputStream(), "UTF-8")); writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), "UTF-8"));
没有什么神奇的地方,主要看PacketWriter/Reader,这两个对象分别把对应的Writer和Reader引用到自己的内部进行操作,下面就先看一个PacketWriter。
1 /** 2 * Creates a new packet writer with the specified connection. 3 * 4 * @param connection the connection. 5 */ 6 protected PacketWriter(XMPPConnection connection) { 7 this.queue = new ArrayBlockingQueue<Packet>(500, true); 8 this.connection = connection; 9 init(); 10 }
还有就是PacketWriter初始化的时候将XMPPConnection对象传了进来,因为在init方法中使用到了XMPPConnection对象的writer成员,我想说的是,为什么不直接传递writer成员?而是将整个对象XMPPConnection传了过来?其实这就是设计模式的好处,我们如果每次都传递的是自己的成员,那么如果后期有改动,实现一个新的XMPPConnection与PacketWriter关联,那么老的代码维护起来是很巨大的,如果这里XMPPConnection和他的同事类PacketWriter都有相对应的接口,(XMPPConnection的接口是Connection)那就更完美了,而这里用到的模式应该是中介者,不是绝对意义的中介者,由于形成中介者的条件比较高,所以实际开发中多是变形使用。PacketWriter对象在XMPPConnection中的connect方法中被初始化,它的最大作用是在其自身的内部创建了两个消息循环,其中一个用30s的heartbeats向服务器发送空白字符,保持长连接。而第二个循环则时刻从队列中主动取消息并发往服务器,而向外部提供的sendPacket方法则是向queue中添加消息,前面提到的循环机制都是在线程中工作,而消息的队列用的是ArrayBlockingQueue,这个无边界阻塞队列可以存放任何对象,这里存放的是Packet对象。
1 public void sendPacket(Packet packet) { 2 if (!done) { 3 try { 4 queue.put(packet); 5 } 6 catch (InterruptedException ie) { 7 ie.printStackTrace(); 8 return; 9 } 10 synchronized (queue) { 11 queue.notifyAll(); 12 } 13 } 14 }
1 while (!done && (writerThread == thisThread)) { 2 Packet packet = nextPacket(); 3 if (packet != null) { 4 synchronized (writer) { 5 writer.write(packet.toXML()); 6 writer.flush(); 7 // Keep track of the last time a stanza was sent to the server 8 lastActive = System.currentTimeMillis(); 9 } 10 } 11 }
消息循环则是一个通过各种成员变量控制的while loop,第一行的nextPacket方法是向queue中获取Packet消息,并且通过weiter将包发出去,这样生产/消费的模型就搭建好了,这里需要注意的是,我删减了很多影响阅读的代码,并没有全部贴上。关于heartbeats循环其实也是一个在线程中运行的while loop,也是通过一些成员控制。wirter向服务端写了写什么?看下面的这个方法
1 void openStream() throws IOException { 2 StringBuilder stream = new StringBuilder(); 3 stream.append("<stream:stream"); 4 stream.append(" to=\"").append(connection.getServiceName()).append("\""); 5 stream.append(" xmlns=\"jabber:client\""); 6 stream.append(" xmlns:stream=\"http://etherx.jabber.org/streams\""); 7 stream.append(" version=\"1.0\">"); 8 writer.write(stream.toString()); 9 writer.flush(); 10 }
XML,没错,这也是符合XMPP协议规范的一种表现吧,至于更多XMPP协议的好处,由于本人的经验有限,就不多做点评,希望后续会对其深入了解。
下面看一个PacketReader这个类都包含了什么职责。