android开发-AndroidPN的阻塞IO

文章标签: android开发
2015-12-26 22:50:15     人阅读    

 androidPN服务端用的是mina,略去不表,客户端的socket通讯用的是asmack,期间使用xmpp协议通讯,这个xmpp通用是通用了,但用的是xml格式互发,之间不得不加了一堆的xml解析,大部分篇幅都是干这个,对此没多大兴趣,这里只是简单记录一下阅读源码中client与server的阻塞读写,寻找可以借鉴之处。

     客户端启动之后,负责管理连接的XMPPConnection初始化:

Java代码  
  1. if (isFirstInitialization) {  
  2.                packetWriter = new PacketWriter(this);  
  3.                packetReader = new PacketReader(this);  

   分别负责读写。然后启动二者:

Java代码  
  1. // Start the packet writer. This will open a XMPP stream to the server  
  2. packetWriter.startup();  
  3. // Start the packet reader. The startup() method will block until we  
  4. // get an opening stream packet back from server.  
  5. packetReader.startup();  
  6. // Make note of the fact that we're now connected.  
  7. connected = true;  

   

先来看packetWriter是如何向服务器发送数据的,它的初始化方法

Java代码  
  1. protected void init() {  
  2.     this.writer = connection.writer;  
  3.     done = false;  
  4.     writerThread = new Thread() {  
  5.         public void run() {  
  6.             writePackets(this);  
  7.         }  
  8.     };  
  9.     writerThread.setName("Smack Packet Writer (" + connection.connectionCounterValue + ")");  
  10.     writerThread.setDaemon(true);  
  11. }  

开一个守候线程writeThread,跑writePackets(this),该方法主要代码:

Java代码  
  1. // Write out packets from the queue.  
  2. while (!done && (writerThread == thisThread)) {  
  3.     Packet packet = nextPacket();  
  4.     if (packet != null) {  
  5.         writer.write(packet.toXML());  
  6.         if (queue.isEmpty()) {  
  7.             writer.flush();  
  8.         }  
  9.     }  
  10. }  

 其中queue,线程安全:

Java代码  
  1. private final BlockingQueue<Packet> queue;  

 queue中有数据就write到服务器,如果没数据,阻塞在nextPacket():

Java代码  
  1. private Packet nextPacket() {  
  2.     Packet packet = null;  
  3.     // Wait until there's a packet or we're done.  
  4.     while (!done && (packet = queue.poll()) == null) {  
  5.         try {  
  6.             synchronized (queue) {  
  7.                 queue.wait();  
  8.             }  
  9.         }  
  10.         catch (InterruptedException ie) {  
  11.             // Do nothing  
  12.         }  
  13.     }  
  14.     return packet;  
  15. }  

 看看,queue.wait(),写线程阻塞于此,省电! 既然有wait(),必然有notifyAll():

Java代码  
  1. public void sendPacket(Packet packet) {  
  2.     if (!done) {  
  3.         // Invoke interceptors for the new packet that is about to be sent. Interceptors  
  4.         // may modify the content of the packet.  
  5.         connection.firePacketInterceptors(packet);  
  6.   
  7.         try {  
  8.             queue.put(packet);  
  9.         }  
  10.         catch (InterruptedException ie) {  
  11.             ie.printStackTrace();  
  12.             return;  
  13.         }  
  14.         synchronized (queue) {  
  15.             queue.notifyAll();  
  16.         }  
  17.   
  18.         // Process packet writer listeners. Note that we're using the sending  
  19.         // thread so it's expected that listeners are fast.  
  20.         connection.firePacketSendingListeners(packet);  
  21.     }  
  22. }  

 当其他线程把要写的数据put进queue时,writerThread即被唤醒,继续运作,标准的生产消费模式。这个过程还是比较简单的,一目了然,相比之下PacketReader就不那么省心。

 

    PacketReader初始化:

Java代码  
  1. protected void init() {  
  2. .  
  3.     readerThread = new Thread() {  
  4.         public void run() {  
  5.             parsePackets(this);  
  6.         }  
  7.     };  
  8. ..  
  9.     resetParser();  
  10. }  
 接着看parsePackets:
Java代码  
  1.     private void parsePackets(Thread thread) {  
  2.         try {  
  3.             int eventType = parser.getEventType();  
  4.             do {  
  5.                 if (eventType == XmlPullParser.START_TAG) {  
  6. //...  
  7. //...很长很长,都在xml纠结  
  8. //...  
  9.                 eventType = parser.next();  
  10.             } while (!done && eventType != XmlPullParser.END_DOCUMENT && thread == readerThread);  
  11.         }  

好吧,循环体是有了,但没见着一个reader.read(),阻塞读在哪里?还有输入流在哪里?

注意到上面初始化方法中最后resetParser():

Java代码  
  1. private void resetParser() {  
  2.     try {  
  3.         parser = XmlPullParserFactory.newInstance().newPullParser();  
  4.         parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);  
  5.         parser.setInput(connection.reader);  
  6.     }  
  7.     catch (XmlPullParserException xppe) {  
  8.         xppe.printStackTrace();  
  9.     }  
  10. }  
 这里把输入流connection.reader交给了parser。看来只能从parser中查找,XmlPullParserFactory中
Java代码  
  1. public XmlPullParser newPullParser() throws XmlPullParserException {  
  2.     final XmlPullParser pp = new KXmlParser();  
  3.     for (Map.Entry<String, Boolean> entry : features.entrySet()) {  
  4.         pp.setFeature(entry.getKey(), entry.getValue());  
  5.     }  
  6.   
  7.     return pp;  
  8. }  
 parser来自于org.kxml2.io.KXmlParser,继续下kxml2源码包找,真够折腾的
Java代码  
  1.   public void setInput(Reader reader) throws XmlPullParserException {  
  2.         this.reader = reader;  
  3. //...  
  4.         if (reader == null)  
  5.             return;  
  6. //...  
  7.     }  
 可见socket输入流交给了parser,顺便看下parsePackets方法中用到的parser.next()等方法,都调用了peek()方法:
Java代码  
  1. /** Does never read more than needed */  
  2.   
  3. private final int peek(int pos) throws IOException {  
  4.     while (pos >= peekCount) {  
  5.         int nw;  
  6.         if (srcBuf.length <= 1)  
  7.             nw = reader.read();  
  8.         else if (srcPos < srcCount)  
  9.             nw = srcBuf[srcPos++];  
  10.         else {  
  11.             srcCount = reader.read(srcBuf, 0, srcBuf.length);  
  12.             if (srcCount <= 0)  
  13.                 nw = -1;  
  14.             else  
  15.                 nw = srcBuf[0];  
  16.             srcPos = 1;  
  17.         }  
  18.         if (nw == '\r') {  
  19.             wasCR = true;  
  20.             peek[peekCount++] = '\n';  
  21.         }  
  22.         else {  
  23.             if (nw == '\n') {  
  24.                 if (!wasCR)  
  25.                     peek[peekCount++] = '\n';  
  26.             }  
  27.             else  
  28.                 peek[peekCount++] = nw;  
  29.             wasCR = false;  
  30.         }  
  31.     }  
  32.     return peek[pos];  
  33. }  
 这里终于出现了reader.read(),就在这里阻塞读。这是一个多么苦逼的过程。为了xml解析不得不把一行代码化为数百行代码,而且这样很费电的你知道吗^_^。
 到此结束,化了一上午看这些,有些收获,有些头痛


原文地址:http://www.itmmd.com/201512/744.html
该文章由 萌萌的IT人 整理发布,转载须标明出处。

  上一篇
下一篇  Solr配置maxBooleanClauses属性不生效原因分析
精彩回复
我要追加问题,请求站长解决!
姓名: