职业IT人-IT人生活圈

 找回密码
 成为会员
搜索
查看: 494|回复: 9

Android字符串拼接OutOfMemory

[复制链接]
醉倚西风 发表于 2011-9-5 14:17 | 显示全部楼层 |阅读模式
目前一个Android应用,要把对象生成XML,目前用的是StringBuffer来append完成的,但是数据量过大的时候就报OutOfMemory,原因就是AbstractStringBuilder的enlargeBuffer方法在容量不够的时候申请的是按额外增加两倍的容量来做的
Java代码  
private void enlargeBuffer(int min) {   
    int newSize = ((value.length >> 1) + value.length) + 2;   
    char[] newData = new char[min > newSize ? min : newSize];   
    System.arraycopy(value, 0, newData, 0, count);   
    value = newData;   
    shared = false;   
}  

    private void enlargeBuffer(int min) {
        int newSize = ((value.length >> 1) + value.length) + 2;
        char[] newData = new char[min > newSize ? min : newSize];
        System.arraycopy(value, 0, newData, 0, count);
        value = newData;
        shared = false;
    }
这样就超出了,目前是做的是预估算下数据的容量,然后直接先分配好,再来append,这样可以解决一点问题,但不能根本的解决问题,数据量再增多或者有估算不准还是会OutOfMemory

看了有些人的做法是分块来组装的,比如一个大的XML文件分很多块,它就每块每块的来拼接,最后再组装成一个大的

目前我这里是有个XMLNode对象,里面有XMLNode parent,List<XMLNode> children,int token,XMLNode自己有个toXMLString方法,拼接都在这里面

整个过程就是把POJO转成XMLNode,再用toXMLString

还有人提议先把XMLNode转换成临时文件,越过AbstractStringBuilder这里面的方法
我写了点也写不下去了,在XMLNode的toXML会涉及到递归,对于文件递归生成一个文件再添加到原来的文件这里搞不定啊

大家有没有什么高招,最好不要替换现有的解析XML程序,这个也不知道谁写的,谢谢大家


更新:为了不让这个帖子成为太监贴,还是把我解决的思路提出来
总体上我觉得目前这样做是不大妥当的,最好的情况数据应该来分批次发送,或者用WBXML,或者压缩后发送,但是目前服务器不这样支持,我也没有办法

我解决的方式还是利用文件来做的,也就是先把Java对象转化为XML文件,用了org.kxml2.io.KXmlSerializer库,把原来那个别人写的Formatter替换掉了,最后再用HttpClient发送请求到服务器端,虽然速度有点慢,但是不至于程序直接就Crash掉

以前发送小的XML文件就用的HttpURLConnection,它是把数据都写入内存的,所以有时候就报OOM了,代码如下
Java代码  
ByteArrayOutputStream baos = new ByteArrayOutputStream();   
    HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();   
    conn.setRequestProperty("Charset", "UTF-8");   
    conn.addRequestProperty("Content-Type",   
                                "application/vnd.syncml+xml;charset=UTF-8");   
    conn.addRequestProperty("cache-control", "no-store");   
    conn.addRequestProperty("accept",   
                                "application/vnd.syncml+wbxml, application/vnd.syncml+xml;");   
    conn.addRequestProperty("accept-charset", "UTF-8");   
        conn.setRequestMethod("POST");   
    conn.setDoInput(true);   
    conn.setDoOutput(true);   
    conn.setUseCaches(false);   
  
    OutputStream os = conn.getOutputStream();   
    os.write(baos.toByteArray());   
    os.close();   
    baos.close();   
  
    conn.connect();   
  
    int statusCode = conn.getResponseCode();   
    if (HttpURLConnection.HTTP_OK == statusCode) {   
        InputStream is = conn.getInputStream();   
        } else {   
            // DO XXOO HERE   
        }  

ByteArrayOutputStream baos = new ByteArrayOutputStream();
        HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
        conn.setRequestProperty("Charset", "UTF-8");
        conn.addRequestProperty("Content-Type",
                                "application/vnd.syncml+xml;charset=UTF-8");
        conn.addRequestProperty("cache-control", "no-store");
        conn.addRequestProperty("accept",
                                "application/vnd.syncml+wbxml, application/vnd.syncml+xml;");
        conn.addRequestProperty("accept-charset", "UTF-8");
        conn.setRequestMethod("POST");
        conn.setDoInput(true);
        conn.setDoOutput(true);
        conn.setUseCaches(false);

        OutputStream os = conn.getOutputStream();
        os.write(baos.toByteArray());
        os.close();
        baos.close();

        conn.connect();

        int statusCode = conn.getResponseCode();
        if (HttpURLConnection.HTTP_OK == statusCode) {
            InputStream is = conn.getInputStream();
        } else {
            // DO XXOO HERE
        }
现在换成了
Java代码  
HttpPost postRequest = new HttpPost(url);   
File tmpFile = new File("/data/data/tmp.xml");   
  
FileEntity entity = new FileEntity(tmpFile, "application/vnd.syncml+xml;charset=UTF-8");   
entity.setContentEncoding("UTF-8");   
postRequest.setEntity(entity);   
  
HttpResponse response = httpClient.execute(postRequest);   
InputStream content = response.getEntity().getContent();   
int statusCode = response.getStatusLine().getStatusCode();   
if (200 == statusCode) {   
      
} else {   
    // DO XXOO HERE   
}  

HttpPost postRequest = new HttpPost(url);
File tmpFile = new File("/data/data/tmp.xml");

FileEntity entity = new FileEntity(tmpFile, "application/vnd.syncml+xml;charset=UTF-8");
entity.setContentEncoding("UTF-8");
postRequest.setEntity(entity);

HttpResponse response = httpClient.execute(postRequest);
InputStream content = response.getEntity().getContent();
int statusCode = response.getStatusLine().getStatusCode();
if (200 == statusCode) {
   
} else {
    // DO XXOO HERE
}


能文能武 发表于 2011-9-5 14:17 | 显示全部楼层
StringBuffer之类的还是不给力啊,用kxml2把XML CONSTRUCTOR给重写了一遍

紫衿 发表于 2011-9-5 14:17 | 显示全部楼层
不知你所说的数据量过大,指的是多大?

曾经的小孩 发表于 2011-9-5 14:17 | 显示全部楼层
@serryzhao生成的XML有2M多

天上智喜 发表于 2011-9-5 14:17 | 显示全部楼层
我们在用到其他解析的时候 发现一般不要超过2M,好像但应用程序的可用内存不是很大
如果一定要这么做,推荐考虑能不能用service方法实现

走失的猫咪 发表于 2011-9-5 14:18 | 显示全部楼层
不知能否详细赐教,怎么一个思路,谢谢

shengdong 写道
我们在用到其他解析的时候 发现一般不要超过2M,好像但应用程序的可用内存不是很大
如果一定要这么做,推荐考虑能不能用service方法实现


shmilyyu 发表于 2011-9-5 14:18 | 显示全部楼层
如果你非要用StringBuffer,就把里面的代码copy一份,定制一个不会OOM的类

yoyo 发表于 2011-9-5 14:18 | 显示全部楼层
直接用“+”拼接吧。虽然格式上不太好看。
那个效率比StringBuilder还高点。

紫衿 发表于 2011-9-5 14:18 | 显示全部楼层
lucane 写道
不知能否详细赐教,怎么一个思路,谢谢

shengdong 写道
我们在用到其他解析的时候 发现一般不要超过2M,好像但应用程序的可用内存不是很大
如果一定要这么做,推荐考虑能不能用service方法实现




这个功能是和我一起做的经理完成的,我们有一个打印功能需要实现,但是由于自身程序已经有占用内存了,所以执行打印模块的时候,我们是在主程序中启动一个service,由service完成文档的生成工作的,文档生成过程中占用内存基本上在3M上下吧,具体的实现流程我们也是参照官方说明完成了一个service,就是这样的,但不确定和你们的业务实现相符。这个需要你自己试试看了

只学java 发表于 2011-9-5 14:18 | 显示全部楼层
考虑直接输入到InputStream中,避免在内存中拼接字符串。
您需要登录后才可以回帖 登录 | 成为会员

本版积分规则

QQ|手机版|小黑屋|网站帮助|职业IT人-IT人生活圈 ( 粤ICP备12053935号-1 )|网站地图
本站文章版权归原发布者及原出处所有。内容为作者个人观点,并不代表本站赞同其观点和对其真实性负责,本站只提供参考并不构成任何投资及应用建议。本站是信息平台,网站上部分文章为转载,并不用于任何商业目的,我们已经尽可能的对作者和来源进行了通告,但是能力有限或疏忽造成漏登,请及时联系我们,我们将根据著作权人的要求立即更正或者删除有关内容。

GMT+8, 2024-4-28 14:14 , Processed in 0.136089 second(s), 20 queries , Gzip On.

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表