2

我正在开发一个 android 客户端,该客户端通过 TCP 套接字从我的 java 服务器读取持续的 xml 数据流。服务器发送一个 '\n' 字符作为连续响应之间的分隔符。下面给出了一个模型实现..

<response1>
   <datas>
      <data>
           .....
           .....
      </data>
      <data>
           .....
           .....
      </data>
      ........
      ........
   </datas>
</response1>\n    <--- \n acts as delimiter ---/> 
<response2>

   <datas>
      <data>
           .....
           .....
      </data>
      <data>
           .....
           .....
      </data>
      ........
      ........
   </datas>
</response2>\n

好吧,我希望现在结构清晰。此响应是从服务器 zlib 压缩传输的。所以我必须首先膨胀我从服务器读取的任何内容,使用分隔符和解析分开响应。我正在使用 SAX 解析我的 XML

现在我的主要问题是来自服务器的 xml 响应可能非常大(可以在 3 到 4 MB 的范围内)。所以

  • 要基于分隔符 (\n) 分隔响应,我必须使用 stringBuilder来存储响应块,因为它从套接字读取,并且在某些手机上 StringBuilder 无法存储兆字节范围内的字符串。它给出了OutOfMemory异常,并且从这样的线程我了解到保留大字符串(即使是临时的)并不是一个好主意。

  • 接下来,我尝试将 inflatorReadStream(它反过来从套接字输入流中获取数据)作为 SAX 解析器的输入流(无需自己分离 xml 并依赖 SAX 基于标签查找文档结尾的能力)。这一次成功解析了一个响应,但随后在找到 '\n' 分隔符时,SAX 会抛出ExpatParserParseException,说 junk after document element

  • 在捕捉到ExpatParserParseException之后,我尝试再次读取,但是在抛出异常 SAX Parser 后关闭了流,所以当我再次尝试读取/解析时,它给出IOException说输入流已关闭。

下面给出了我所做的代码片段(为了清楚起见,删除了所有不相关的 try catch 块)。

private Socket clientSocket     =   null;
DataInputStream readStream      =   null;
DataOutputStream writeStream        =   null;
private StringBuilder incompleteResponse    =   null;
private AppContext  context     =   null;


public boolean connectToHost(String ipAddress, int port,AppContext myContext){
        context                     =   myContext;
        website                     =   site;
        InetAddress serverAddr          =   null;

    serverAddr                      =   InetAddress.getByName(website.mIpAddress);

    clientSocket                    =   new Socket(serverAddr, port);

    //If connected create a read and write Stream objects..
    readStream   =  new DataInputStream(new InflaterInputStream(clientSocket.getInputStream()));
    writeStream             =   new DataOutputStream(clientSocket.getOutputStream());

    Thread readThread = new Thread(){
            @Override
            public void run(){                              
            ReadFromSocket();                   
        }
    };
    readThread.start();     
    return true;
}


public void ReadFromSocket(){
   while(true){
       InputSource xmlInputSource = new InputSource(readStream);
       SAXParserFactory spf =   SAXParserFactory.newInstance();
       SAXParser sp =   null;
       XMLReader xr =   null;
       try{
           sp   = spf.newSAXParser();
       xr   = sp.getXMLReader();
       ParseHandler xmlHandler =    new ParseHandler(context.getSiteListArray().indexOf(website), context);
       xr.setContentHandler(xmlHandler);
       xr.parse(xmlInputSource);
   //  postSuccessfullParsingNotification();
       }catch(SAXException e){
           e.printStackTrace();
           postSuccessfullParsingNotification();
       }catch(ParserConfigurationException e){
           e.printStackTrace();
           postSocketDisconnectionBroadcast();
           break;
       }catch (IOException e){
           postSocketDisconnectionBroadcast();
           e.printStackTrace();
           e.toString();
           break;
       }catch (Exception e){
           postSocketDisconnectionBroadcast();
           e.printStackTrace();
           break;
       }
    }
}

现在我的问题是

  1. 有没有办法让 SAX Parser 在 xml 响应之后忽略垃圾字符,而不是抛出异常并关闭流..
  2. 如果没有,有什么方法可以避免 stringBuilder 出现内存不足错误。坦率地说,我对此并不例外。任何解决方法?
4

2 回答 2

2
  1. 您可能可以在您传递给过滤器的读取器或流周围使用包装器来检测换行符,然后关闭解析器并启动一个继续处理流的新解析器:您的流不是有效的 XML,您将不会能够按照您当前已实现的方式解析它。看看http://commons.apache.org/io/api-release/org/apache/commons/io/input/CloseShieldInputStream.html
  2. 不。
于 2011-08-16T05:54:50.387 回答
1

如果您的 SAX 解析器支持推送模型(您自己将原始数据块推送到其中并在解析原始数据时触发事件),那么您可以简单地在 SAX 会话开始时推送您自己的初始 XML 标记。这将成为顶级文档标签,然后您可以在收到响应时推送它们,就 SAX 而言,它们将是二级标签。这样,您可以在同一个 SAX 会话中推送多个响应,然后在 OnTagOpen 事件(或您正在使用的任何时候)中,当您在级别 1 检测到新响应的标签名称时,您将知道何时开始新响应。

于 2011-08-16T23:37:07.273 回答